> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dairo.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Telegram

> Connect a Telegram bot to Dairo and send and receive Telegram messages through the same inbox, mailbox, and send call as email.

Connect a Telegram bot and it becomes a Dairo inbox. Messages people send the
bot arrive in your mailbox as structured messages, and your agent answers with
the same send call it already uses for email. Setup is one API call and a
one-time verification code.

Telegram rides the same model as every Dairo [channel](/concepts/channels): the
bot is an inbox with `channel: "telegram"`, inbound messages land in the
mailbox, and `message.*` webhooks fire as usual. The only channel-specific step
is the connect.

## Connect a bot

<Steps>
  <Step title="Create the bot">
    In Telegram, open [@BotFather](https://t.me/BotFather), send `/newbot`, and
    follow the prompts. BotFather returns a bot token that looks like
    `123456789:AAE...`. Keep it secret — anyone who has it can control the bot.
  </Step>

  <Step title="Connect it to Dairo">
    Create the inbox by passing the token to `POST /v1/inboxes`. There is no domain
    or address to set up — a Telegram inbox has no email identity.

    ```bash title="cURL" theme={null}
    curl -X POST https://api.dairo.app/v1/inboxes \
      -H "Authorization: Bearer $DAIRO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "channel": "telegram",
        "botToken": "123456789:AAE...",
        "agent": "Support Bot"
      }'
    ```

    Dairo validates the token with Telegram — an invalid token is a `400` — then
    creates the inbox and returns a six-character verification code:

    ```json theme={null}
    {
      "object": "inbox",
      "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "channel": "telegram",
      "status": "pending_verification",
      "telegram": {
        "botId": 123456789,
        "botUsername": "acme_support_bot",
        "verificationCode": "AB2CD3",
        "connectUrl": "https://t.me/acme_support_bot?start=AB2CD3",
        "instructions": "Open your bot in Telegram and send the message: AB2CD3"
      }
    }
    ```
  </Step>

  <Step title="Verify you own the bot">
    Open `connectUrl` — it opens the bot in Telegram and tapping **Start** sends
    the code for you — or open the bot yourself and send the code as a message. The
    bot confirms the connection in the chat, and that conversation becomes the
    inbox's home chat.

    Until the code arrives, the bot answers every message with a prompt for the
    code, and nothing is stored in your mailbox.
  </Step>
</Steps>

Connecting the same bot a second time returns the existing inbox instead of
creating a duplicate.

## Receive messages

Every message sent to the bot lands in your mailbox with
`channel: "telegram"`, readable with the same list and get calls as any other
[message](/receiving/messages-and-threads).

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl "https://api.dairo.app/v1/messages?channel=telegram&direction=inbound" \
    -H "Authorization: Bearer $DAIRO_API_KEY"
  ```

  ```ts title="TypeScript" theme={null}
  const page = await dairo.messages.list({
    channel: "telegram",
    direction: "inbound",
  });
  ```
</CodeGroup>

Each entry in `messages` carries the sender and the reply coordinates:

```json title="An inbound Telegram message, trimmed" theme={null}
{
  "object": "message",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "channel": "telegram",
  "direction": "inbound",
  "inboxId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "from": { "address": "@ada", "name": "Ada" },
  "textBody": "Where is my order?",
  "channelMetadata": {
    "chatId": 5031234567,
    "telegramMessageId": 42,
    "updateId": 987654321,
    "fromUsername": "ada"
  },
  "receivedAt": "2026-07-10T09:20:00Z"
}
```

| `channelMetadata` field | What it is                                                                            |
| ----------------------- | ------------------------------------------------------------------------------------- |
| `chatId`                | The conversation the message came from. Echo it back as `telegram:{chatId}` to reply. |
| `telegramMessageId`     | Telegram's id for this message.                                                       |
| `updateId`              | Telegram's delivery id, for your own dedup if you want it.                            |
| `fromUsername`          | The sender's public username, when they have one.                                     |

Three things happen on arrival without any code on your side:

* Each inbound message fires the `message.received`
  [webhook](/webhooks/webhooks), so your agent can react instead of polling.
* Photos, documents, video, audio, and voice notes are downloaded and attached
  to the message as first-class attachments; a caption becomes the text body.
* The bot answers a fixed set of built-in commands itself — `/start`, `/help`,
  `/status`, `/usage`, `/whoami`. Those never reach your mailbox; every other
  command and message does.

## Send and reply

Send with the same `POST /v1/messages` call as email: point `inboxId` at the
Telegram inbox and put a Telegram recipient in `to`.

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl -X POST https://api.dairo.app/v1/messages \
    -H "Authorization: Bearer $DAIRO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "inboxId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "to": ["telegram:5031234567"],
      "text": "Your order shipped and arrives Thursday."
    }'
  ```

  ```ts title="TypeScript" theme={null}
  await dairo.messages.send({
    inboxId: "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    to: ["telegram:5031234567"],
    text: "Your order shipped and arrives Thursday.",
  });
  ```
</CodeGroup>

The response comes back with `status: "sent"` and `channel: "telegram"`, and
the send appears in `GET /v1/messages` like any other outbound message.

The first Telegram entry in `to` picks the destination:

| `to` entry                              | Delivers to                                                                  |
| --------------------------------------- | ---------------------------------------------------------------------------- |
| `telegram:{chatId}`                     | That chat. Echo `channelMetadata.chatId` from the message you are answering. |
| `@username` or `telegram:@username`     | A public username — another bot, or a channel the bot can post to.           |
| Any other recipient (a regular address) | The home chat — the conversation that sent the verification code.            |

A send to an unverified inbox that names no `@username` target is a `422`: the
bot has no home chat until the code is verified. Scheduled sends (`sendAt`) are
not supported on the Telegram channel and return a `400`.

For formatted output — headings, lists, tables, code blocks, media captions —
send an `html` body instead of `text` and Telegram renders it as a rich
message. The [rich messages guide](/agent-first/telegram-rich-messages) covers
the supported markup.

## Buttons, edits, and reactions

Telegram is more than text in, text out. On the same surface your agent can:

* **Attach inline buttons** — pass `buttons`, an array of rows, each row an
  array of `{text, url}` link buttons or `{text, callback}` action buttons.
* **Handle a tap** — a callback tap arrives as a normal inbound message and
  fires a `message.button.tapped` webhook, so your agent branches on the
  choice.
* **Edit a sent message** — `POST /v1/messages/{messageId}/edit` replaces the
  text, HTML, or keyboard in place.
* **React** — `POST /v1/messages/{messageId}/react` sets the bot's emoji
  reaction on a message you sent or received; `/unreact` clears it.
* **Send native types** — a `telegram` object on the send carries exactly one
  of `location`, `venue`, `contact`, `poll`, `dice`, `sticker`, `videoNote`, or
  `voice` (a synthesized voice note from text).

```json title="A send with an approval keyboard" theme={null}
{
  "inboxId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "to": ["telegram:5031234567"],
  "text": "Deploy 42 is ready. Approve?",
  "buttons": [[
    { "text": "Approve", "callback": "approve:42" },
    { "text": "Reject",  "callback": "reject:42" }
  ]]
}
```

Button shapes, tap handling, editing, reactions, media, voice, and bot-to-bot
messaging are covered end to end in the
[Telegram interactivity guide](/agent-first/telegram-interactivity).

## Behavior and limits

* **One bot, one inbox.** Reconnecting the same bot returns the same inbox.
* **Scopes.** Reading rides `messages:read`; sending, editing, and reacting
  ride `messages:send`; connecting a bot rides `inboxes:write`.
* **Synthetic address.** A Telegram inbox has no email identity. Its `address`
  field carries an internal handle of the form `tg-{botId}@telegram.dairo.local`
  used for routing and filtering — it is not deliverable email.
* **Quota.** Each Telegram send counts as one message against your monthly
  message quota; a send over quota returns a `429`.

## Related

* [Slack](/channels/slack) — the same receive-and-reply model, installed into
  your customers' workspaces instead of connected with a token.
* [Build a support inbox agent](/examples/support-inbox-agent) — a worked agent
  over the unified inbox.
