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: 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

Create the bot

In Telegram, open @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.

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.

cURL
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:

{
  "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"
  }
}

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.

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.

curl "https://api.dairo.app/v1/messages?channel=telegram&direction=inbound" \
  -H "Authorization: Bearer $DAIRO_API_KEY"

Each entry in data carries the sender and the reply coordinates:

An inbound Telegram message, trimmed
{
  "object": "message",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "channel": "telegram",
  "direction": "inbound",
  "inboxId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "from": { "address": "telegram:5031234567", "name": "Ada" },
  "textBody": "Where is my order?",
  "channelMetadata": {
    "chatId": 5031234567,
    "telegramMessageId": 42,
    "updateId": 987654321,
    "fromUsername": "ada"
  },
  "receivedAt": "2026-07-10T09:20:00Z"
}
channelMetadata fieldWhat it is
chatIdThe conversation the message came from. Echo it back as telegram:{chatId} to reply.
telegramMessageIdTelegram's id for this message.
updateIdTelegram's delivery id, for your own dedup if you want it.
fromUsernameThe sender's public username, when they have one.

The sender's from.address is always the chat, telegram:{chatId} — the value you reply to — never their @username, because a bot cannot message a user by username.

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

  • Each inbound message fires the message.received webhook, 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.

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."
  }'

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

A Telegram send goes to exactly one chat. Put at most one Telegram target in to: a second one, or any cc or bcc, is a 400 rather than a silently dropped recipient.

to entryDelivers to
telegram:{chatId}That chat. Echo channelMetadata.chatId from the message you are answering.
@username or telegram:@usernameA 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 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 the message.received webhook, so your agent branches on the choice. The event stream also records a message.button.tapped event with the button's label and callback value.
  • 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).
A send with an approval keyboard
{
  "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.

Behavior and limits

  • One bot, one inbox. Reconnecting the same bot returns the same inbox, and a bot that is already verified comes back active, with no new verification code.
  • Plan limit. A Telegram inbox counts against your plan's inbox limit like any other inbox; connecting a bot over the limit is a 429 whose message names the inbox plan limit (the code is currently rate_limited, see plan limits on resource counts).
  • 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.
  • Slack — the same receive-and-reply model, installed into your customers' workspaces instead of connected with a token.
  • Build a support inbox agent — a worked agent over the unified inbox.

On this page