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

# Telegram (Bot API)

# Telegram (Bot API)

Status: production-ready for bot DMs + groups via grammY. Long polling is the default mode; webhook mode is optional.

## Quick setup

<Steps>
  <Step title="Create the bot token in BotFather">
    Open Telegram and chat with **@BotFather** (confirm the handle is exactly `@BotFather`).

    Run `/newbot`, follow prompts, and save the token.
  </Step>

  <Step title="Configure token and DM policy">
    ```json5 theme={null}
    {
      channels: {
        telegram: {
          enabled: true,
          botToken: "123:abc",
          dmPolicy: "pairing",
          groups: { "*": { requireMention: true } },
        },
      },
    }
    ```

    Env fallback: `TELEGRAM_BOT_TOKEN=...` (default account only).
  </Step>

  <Step title="Start gateway and approve first DM">
    ```bash theme={null}
    datzi gateway
    datzi pairing list telegram
    datzi pairing approve telegram <CODE>
    ```

    Pairing codes expire after 1 hour.
  </Step>

  <Step title="Add the bot to a group">
    Add the bot to your group, then set `channels.telegram.groups` and `groupPolicy` to match your access model.
  </Step>
</Steps>

## Telegram side settings

### Privacy mode and group visibility

Telegram bots default to **Privacy Mode**, which limits what group messages they receive.

If the bot must see all group messages, either:

* disable privacy mode via `/setprivacy`, or
* make the bot a group admin.

When toggling privacy mode, remove + re-add the bot in each group so Telegram applies the change.

### Helpful BotFather toggles

* `/setjoingroups` to allow/deny group adds
* `/setprivacy` for group visibility behavior

## Access control and activation

`channels.telegram.dmPolicy` controls direct message access:

* `pairing` (default)
* `allowlist`
* `open` (requires `allowFrom` to include `"*"`)
* `disabled`

`channels.telegram.allowFrom` accepts numeric Telegram user IDs. `telegram:` / `tg:` prefixes are accepted and normalized.

### Finding your Telegram user ID

Safer (no third-party bot):

1. DM your bot.
2. Run `datzi logs --follow`.
3. Read `from.id`.

Official Bot API method:

```bash theme={null}
curl "https://api.telegram.org/bot<bot_token>/getUpdates"
```

### Group policy and allowlists

There are two independent controls:

1. **Which groups are allowed** (`channels.telegram.groups`)
   * no `groups` config: all groups allowed
   * `groups` configured: acts as allowlist (explicit IDs or `"*"`)

2. **Which senders are allowed in groups** (`channels.telegram.groupPolicy`)
   * `open`
   * `allowlist` (default)
   * `disabled`

`groupAllowFrom` is used for group sender filtering. If not set, Telegram falls back to `allowFrom`.

Example: allow any member in one specific group:

```json5 theme={null}
{
  channels: {
    telegram: {
      groups: {
        '-1001234567890': {
          groupPolicy: 'open',
          requireMention: false
        }
      }
    }
  }
}
```

### Mention behavior

Group replies require mention by default.

Mention can come from:

* native `@botusername` mention, or
* mention patterns in:
  * `agents.list[].groupChat.mentionPatterns`
  * `messages.groupChat.mentionPatterns`

Session-level command toggles:

* `/activation always`
* `/activation mention`

Persistent config example:

```json5 theme={null}
{
  channels: {
    telegram: {
      groups: {
        '*': { requireMention: false }
      }
    }
  }
}
```

## Runtime behavior

* Telegram is owned by the gateway process.
* Routing is deterministic: Telegram inbound replies back to Telegram.
* Group sessions are isolated by group ID. Forum topics append `:topic:<threadId>` to keep topics isolated.
* Long polling uses grammY runner with per-chat/per-thread sequencing.

## Feature reference

### Live stream preview (message edits)

Datzi can stream partial replies by sending a temporary Telegram message and editing it as text arrives.

`channels.telegram.streaming` is `off | partial | block | progress` (default: `off`)

### Formatting and HTML fallback

Outbound text uses Telegram `parse_mode: "HTML"`.

* Markdown-ish text is rendered to Telegram-safe HTML.
* Raw model HTML is escaped to reduce Telegram parse failures.
* If Telegram rejects parsed HTML, Datzi retries as plain text.

Link previews are enabled by default and can be disabled with `channels.telegram.linkPreview: false`.

### Native commands and custom commands

Telegram command menu registration is handled at startup with `setMyCommands`.

`commands.native: "auto"` enables native commands for Telegram.

Add custom command menu entries:

```json5 theme={null}
{
  channels: {
    telegram: {
      customCommands: [
        { command: 'backup', description: 'Git backup' },
        { command: 'generate', description: 'Create an image' }
      ]
    }
  }
}
```

### Inline buttons

Configure inline keyboard scope:

```json5 theme={null}
{
  channels: {
    telegram: {
      capabilities: {
        inlineButtons: 'allowlist'
      }
    }
  }
}
```

Scopes:

* `off`
* `dm`
* `group`
* `all`
* `allowlist` (default)

Message action example:

```json5 theme={null}
{
  action: 'send',
  channel: 'telegram',
  to: '123456789',
  message: 'Choose an option:',
  buttons: [
    [
      { text: 'Yes', callback_data: 'yes' },
      { text: 'No', callback_data: 'no' }
    ],
    [{ text: 'Cancel', callback_data: 'cancel' }]
  ]
}
```

Callback clicks are passed to the agent as text:
`callback_data: <value>`

### Forum topics and thread behavior

Forum supergroups:

* topic session keys append `:topic:<threadId>`
* replies and typing target the topic thread
* topic config path: `channels.telegram.groups.<chatId>.topics.<threadId>`

General topic (`threadId=1`) special-case:

* message sends omit `message_thread_id` (Telegram rejects `sendMessage(...thread_id=1)`)
* typing actions still include `message_thread_id`

### Audio, video, and stickers

Telegram distinguishes voice notes vs audio files.

* default: audio file behavior
* tag `[[audio_as_voice]]` in agent reply to force voice-note send

Enable sticker actions:

```json5 theme={null}
{
  channels: {
    telegram: {
      actions: {
        sticker: true
      }
    }
  }
}
```

Send sticker action:

```json5 theme={null}
{
  action: 'sticker',
  channel: 'telegram',
  to: '123456789',
  fileId: 'CAACAgIAAxkBAAI...'
}
```

Search cached stickers:

```json5 theme={null}
{
  action: 'sticker-search',
  channel: 'telegram',
  query: 'cat waving',
  limit: 5
}
```

### Reaction notifications

When enabled, Datzi enqueues system events like:

* `Telegram reaction added: 👍 by Alice (@alice) on msg 42`

Config:

* `channels.telegram.reactionNotifications`: `off | own | all` (default: `own`)
* `channels.telegram.reactionLevel`: `off | ack | minimal | extensive` (default: `minimal`)

### Long polling vs webhook

Default: long polling.

Webhook mode:

* set `channels.telegram.webhookUrl`
* set `channels.telegram.webhookSecret` (required when webhook URL is set)
* optional `channels.telegram.webhookPath` (default `/telegram-webhook`)
* optional `channels.telegram.webhookHost` (default `127.0.0.1`)

Default local listener for webhook mode binds to `127.0.0.1:8787`.

## Troubleshooting

* **Bot does not respond to non mention group messages**: check privacy mode via BotFather (`/setprivacy` -> Disable) and remove + re-add bot.
* **Bot not seeing group messages at all**: when `channels.telegram.groups` exists, group must be listed or include `"*"`.
* **Commands work partially or not at all**: authorize your sender identity; `setMyCommands failed` indicates DNS/HTTPS issues to `api.telegram.org`.
* **Polling or network instability**: check IPv6 egress if Telegram API calls fail intermittently.

## Telegram config reference pointers

* `channels.telegram.enabled`: enable/disable channel startup.
* `channels.telegram.botToken`: bot token (BotFather).
* `channels.telegram.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
* `channels.telegram.allowFrom`: DM allowlist (numeric Telegram user IDs).
* `channels.telegram.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
* `channels.telegram.groupAllowFrom`: group sender allowlist (numeric Telegram user IDs).
* `channels.telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
* `channels.telegram.capabilities.inlineButtons`: `off | dm | group | all | allowlist` (default: allowlist).
* `channels.telegram.replyToMode`: `off | first | all` (default: `off`).
* `channels.telegram.textChunkLimit`: outbound chunk size (chars).
* `channels.telegram.streaming`: `off | partial | block | progress` (live stream preview; default: `off`).
* `channels.telegram.mediaMaxMb`: inbound/outbound media cap (MB).
* `channels.telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).
* `channels.telegram.webhookUrl`: enable webhook mode.

## Related

* [Pairing](/datzi/channels/pairing)
* [Channel routing](/datzi/channels/channel-routing)
* [Multi-agent routing](/datzi/concepts/multi-agent)
* [Troubleshooting](/datzi/channels/troubleshooting)
