Docs

AI agent playbooks

Send one message safely

Send approved content to one person, prevent duplicates and track the result through the safety gate.

View Markdown

Goal

You send one message to exactly one person using an account you have been granted. An accepted call only means a job has been created. It does not mean Telegram has confirmed successful sending.

Permissions needed

  • messages.read to read the conversation and context.
  • messages.send to claim the conversation and send.
  • accounts.read if you need to check the account before sending.
  • crm.read if you need to read the customer profile and do-not-contact information.

You do not need connection management or limit-changing permissions to send a normal message.

Ask the person before

Confirm the recipient, sending account, content and purpose. If you already have explicit authorization for this type of reply, work within that scope. Ask again for content outside it.

Do not infer consent from the recipient's presence in the CRM. Do not change the sending account yourself when the chosen account is blocked.

Check first

Call whoami. Read the conversation with conversations_get and the latest messages with messages_list.

Check do-not-contact status when data is available. The safety gate still makes the decision when dispatching the job. Claim the conversation with conversations_claim; do not use force to take the lock.

Read the messages.send schema with tools_describe, especially the message identity fields, latest-message condition and outgoing content.

Steps

  1. Define a stable identifier for the send intent.
  2. Finalize the payload for that intent.
  3. If using REST, send Idempotency-Key according to the operation contract.
  4. If the send contract has clientMessageId, keep the same value when resubmitting the same intent.
  5. Set ifLastMessageId when the reply is only appropriate if the customer has not sent another message.
  1. Call MCP messages_send, or REST operation messages.send.
  2. Save the returned operation ID.
  3. Call operations_get to track it.
  4. Release the conversation with conversations_release when you finish handling it.

For REST, authentication uses:

Authorization: Bearer $TELEGRAM_AUTO_API_KEY
Idempotency-Key: reply-demo-event-a

Do not assume tools_run has an idempotency parameter outside its schema. Read the exact contract and use the message identity mechanism the operation supports.

Check after

Only report successful sending when the status confirms it. If the connection drops after the call, look up the old operation or resubmit the exact same identifier and payload. Do not create a new key just because you have not received a response.

The same identifier with different content may be rejected as a conflict. To change the content, reread the context and decide whether this is genuinely a new send intent.

On errors

  • 409: read the specific error code. It may be an identity conflict, conversation lock or stale context. Do not retry every 409 unchanged.
  • 429: wait for the time specified in the response. Do not switch connections to get around the limit.
  • held: track the hold reason and existing operation; do not create a duplicate.
  • confirmation_required: present the request to the person and wait for a decision.
  • outcome_unknown or unverified: reconcile; do not resend blindly.

When to stop

Stop when do-not-contact restrictions apply, someone else holds the lock, permissions are revoked, content exceeds your authorization or the outcome is unclear.

See investigating a failed job and Telegram limits.