Telegram
Telegram bot support status, capabilities, and configuration.
Status: Production-ready. Supports bot DM and group chat via grammY. Uses long-polling by default; also supports webhook.
Quick Setup for Beginners
1. Create a bot with ''@BotFather'' (''direct link''). Make sure the handle is ''@BotFather'', then copy the bot token.
2. Configure token:
- Environment variable: ''TELEGRAM_BOT_TOKEN=...''
- Or config: ''channels.telegram.botToken: "..."''.
- When both are set, config takes precedence (env only serves as default account fallback).
3. Start Gateway.
4. DM enables pairing by default; first contact receives a pairing code, messages are processed only after approval.
Minimum config:
{
channels: {
telegram: {
enabled: true,
botToken: "123:abc",
dmPolicy: "pairing",
},
},
}What It Is
- Telegram Bot API channel managed by Gateway.
- Deterministic routing: replies only go back to Telegram, model doesn't choose channel.
- DM uses agent's main session by default; group chats are isolated as ''agent:<agentId>:telegram:group:<chatId>''.
Setup (Quick Path)
#
1) Create Bot Token (BotFather)
1. Open Telegram, chat with ''@BotFather'' (''direct link''), confirm handle is ''@BotFather''.
2. Run ''/newbot'', follow prompts (name + username ending with ''bot'').
3. Copy token and keep it safe.
Optional settings:
- ''/setjoingroups'' β allow/disallow bot to join groups
- ''/setprivacy'' β control whether bot can see all group messages
Optional settings:
- ''/setjoingroups'' β allow/disallow bot to join groups
- ''/setprivacy'' β control whether bot can see all group messages
#
2) Configure Token (env or config)
Example:
{
channels: {
telegram: {
enabled: true,
botToken: "123:abc",
dmPolicy: "pairing",
groups: { "*": { requireMention: true } },
},
},
}Environment variable: ''TELEGRAM_BOT_TOKEN=...'' (only affects default account). When both env and config exist, config takes precedence.
Multi-account: Use ''channels.telegram.accounts'' to configure each account's token (optional ''name''). For shared structure, see ''/gateway/configuration''.
3. Start Gateway: Telegram channel starts when token is resolvable (config priority, env fallback).
4. DM defaults to pairing: first contact gives pairing code, messages processed only after approval.
5. Group chat: add bot to group; decide BotFather privacy/admin policy (see below); then use ''channels.telegram.groups'' to control mention gating and allowlist.
Telegram Side: Token / Privacy / Permissions
#
Token (BotFather)
- ''/newbot'' creates bot and returns token (keep secret).
- If leaked, revoke/reset token in @BotFather and update your config.
#
Group Message Visibility (Privacy Mode)
Telegram bot enables Privacy Mode by default, which limits the range of group messages it can receive. If you need bot to see all messages in a group, there are two ways:
- Use ''/setprivacy'' to disable privacy mode, ''or''
- Set bot as group admin (admin bots can receive all messages).
Note: After switching privacy mode, you need to remove bot from group and re-add it for settings to take effect.
#
Group Permissions (Admin)
Admin permissions are set in group UI. Admin bots receive all group messages; only do this when you truly need "full visibility".
How It Works (Behavior)
- Inbound messages are normalized to generic channel envelope (including reply context and media placeholders).
- Group chat requires mention to reply by default (native @mention or ''agents.list[].groupChat.mentionPatterns'' / ''messages.groupChat.mentionPatterns'' match).
- With multiple agents, you can override per-agent in ''agents.list[].groupChat.mentionPatterns''.
- Replies always go back to the Telegram chat that triggered them.
- long-polling uses grammY runner and processes sequentially by chat; overall concurrency limited by ''agents.defaults.maxConcurrent''.
- Telegram Bot API has no read receipts, so no ''sendReadReceipts''.
Draft Streaming
OpenClaw can use ''sendMessageDraft'' to stream partial updates in Telegram DM.
Requirements:
- Enable Threaded Mode (forum topic mode) for bot in @BotFather.
- DM threads only (Telegram includes ''message_thread_id'' in inbound messages).
- ''channels.telegram.streamMode'' is not ''"off"'' (default ''"partial"''; ''"block"'' does chunked draft updates).
Draft streaming only supports DM; Telegram doesn't support this mechanism in groups/channels.
Formatting (Telegram HTML)
- Outbound Telegram text uses ''parse_mode: "HTML"'' (subset of tags supported by Telegram).
- Markdown-ish input renders as Telegram-safe HTML (bold/italic/strikethrough/code/links); block-level elements are flattened to text with line breaks/bullets.
- Raw HTML from model is escaped to avoid Telegram parse errors.
- If Telegram rejects HTML payload, OpenClaw retries same message with plain text.
Commands (Native + Custom)
OpenClaw registers native commands to Telegram bot menu at startup (like ''/status'', ''/reset'', ''/model'').
You can also add custom commands to menu via config:
Notes:
- Custom commands are just menu entries; OpenClaw won't automatically implement them unless you handle them elsewhere.
- Command names are normalized (remove leading ''/'', lowercase), can only contain ''a-z'', ''0-9'', ''_'' (length 1β32).
- Custom commands can't override native commands; conflicts are ignored and logged.
- If ''commands.native'' is disabled, only custom commands are registered (or menu cleared if no custom commands).
Troubleshooting
- ''setMyCommands failed'' in logs usually means HTTPS/DNS outbound to ''api.telegram.org'' is blocked.
- When seeing ''sendMessage'' or ''sendChatAction'' failures, prioritize checking IPv6 routing and DNS.
More: ''/channels/troubleshooting''.
Limits
- Outbound text is chunked by ''channels.telegram.textChunkLimit'' (default 4000).
- Optional chunk by blank lines first: ''channels.telegram.chunkMode="newline"'' (paragraph boundaries) then by length.
- Media download/upload limit: ''channels.telegram.mediaMaxMb'' (default 5MB).
- Telegram Bot API request timeout: ''channels.telegram.timeoutSeconds'' (default 500, grammY). Recommend setting smaller to avoid long hangs.
- Group history context: ''channels.telegram.historyLimit'' (or ''channels.telegram.accounts.*.historyLimit''), falls back to ''messages.groupChat.historyLimit''. Set to ''0'' to disable (default 50).
- DM history limit: ''channels.telegram.dmHistoryLimit'' (counted by user turns). Override per user: ''channels.telegram.dms["''.
Group Chat Trigger Mode
By default, bot only replies in groups when mentioned (''@botname'' or ''agents.list[].groupChat.mentionPatterns'' match). To adjust behavior:
#
Via Config (Recommended)
{
channels: {
telegram: {
groups: {
"-1001234567890": { requireMention: false }, // This group always responds
},
},
},
}''Important:'' Once ''channels.telegram.groups'' is set, it becomes ''group allowlist'': only listed groups (or ''"*"'') are accepted.
Forum topics inherit parent group config (allowFrom, requireMention, skills, prompts) by default, unless you write topic-level overrides in ''channels.telegram.groups.''.
Allow all groups and always reply:
{
channels: {
telegram: {
groups: {
"*": { requireMention: false },
},
},
},
}Keep all groups require mention (default behavior):
{
channels: {
telegram: {
groups: {
"*": { requireMention: true }, // Or omit groups entirely
},
},
},
}#
Via Commands (Current Session Only)
Send in group:
- ''/activation always'' β reply to all messages
- ''/activation mention'' β require mention (default)
Note: This command only changes session state. To persist behavior after restart, use config.
#
Get Group Chat ID
Forward any message from group to ''@userinfobot'' or ''@getidsbot'' to see chat ID (usually a negative number like ''-1001234567890'').
Privacy note: ''@userinfobot'' is a third-party bot. If you don't want to use third-party, add bot to group, send a message, then use ''openclaw logs --follow'' to read ''chat.id'', or use Bot API's ''getUpdates''.
Config Writes
By default, Telegram allows config updates triggered by channel events or ''/config set|unset'' to be written back to config file.
Typical scenarios:
- Group upgraded to supergroup, Telegram emits ''migrate_to_chat_id'' (chat ID changes); OpenClaw can automatically migrate ''channels.telegram.groups''.
- You run ''/config set'' or ''/config unset'' in Telegram chat (requires ''commands.config: true'').
Disable:
{
channels: { telegram: { configWrites: false } },
}Topics (Forum Supergroup)
Telegram forum topics carry ''message_thread_id'' with each message. OpenClaw will:
Inline Buttons
Telegram supports inline keyboard (callback buttons).
{
channels: {
telegram: {
capabilities: {
inlineButtons: "allowlist",
},
},
},
}Per-account config:
{
channels: {
telegram: {
accounts: {
main: {
capabilities: {
inlineButtons: "allowlist",
},
},
},
},
},
}Scopes:
- ''off'' β disabled
- ''dm'' β DM only (group targets blocked)
- ''group'' β group only (DM targets blocked)
- ''all'' β DM + group
- ''allowlist'' β DM + group, but only allow senders permitted by ''allowFrom''/''groupAllowFrom'' (consistent with control commands)
Default: ''allowlist''. Old syntax: ''capabilities: ["inlineButtons"]'' equals ''inlineButtons: "all"''.
#
Send Buttons
Use message tool to pass ''buttons'' parameter:
{
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" }],
],
}After user clicks button, callback data is sent back to agent as message:
''callback_data: value''
#
Config Hierarchy
Telegram capabilities can be configured at two levels (above example uses object form; old string array is still supported):
- ''channels.telegram.capabilities'': global default, applied to all Telegram accounts (unless overridden)
- ''channels.telegram.accounts.'': per-account override
Access Control (DM + Group)
#
DM Access
- Default: ''channels.telegram.dmPolicy = "pairing"''. Unknown senders receive pairing code; processed only after approval (1 hour expiry).
- Approve:
- ''openclaw pairing list telegram''
- ''openclaw pairing approve telegram ''''
- pairing is default token exchange for Telegram DM. See ''Pairing'' for details.
- ''channels.telegram.allowFrom'' recommends using numeric user id, also supports ''@username''. Note it refers to human sender's ID, not bot's username. Wizard resolves ''@username'' to numeric id when possible.
#
How to Get Your Telegram User ID
Safer (no third-party bot dependency):
1. Start gateway, send a DM to your bot.
2. Run ''openclaw logs --follow'', find ''from.id''.
Official Bot API (more direct):
1. Send DM to bot.
2. Use token to call ''getUpdates'' and read ''message.from.id'':
''''`bash", "p8": "curl "https://api.telegram.org/bot''''`
curl "https://api.telegram.org/bot'
''''`
Third-party (less privacy):
- DM ''@userinfobot'' or ''@getidsbot''.
#
Group Access
Group chat has two independent controls:
''1) Which groups to allow'' (''channels.telegram.groups'' as group allowlist):
- Don't write ''groups'': allow all groups
- Write ''groups'': only allow listed groups or ''"*"''
- Example: "groups": { "-1001234567890": {'}, "*": {'} }' means allow all groups (while writing overrides for specific groups)
''2) Which senders to allow'' (''channels.telegram.groupPolicy'' controls group sender filtering):
- ''"open"'': allow all senders in group
- ''"allowlist"'': only allow senders in ''channels.telegram.groupAllowFrom''
- ''"disabled"'': completely reject group messages
Default is ''groupPolicy: "allowlist"'' (i.e., block by default when ''groupAllowFrom'' not configured)
Most users want: ''groupPolicy: "allowlist"'' + ''groupAllowFrom'' + list allowed groups in ''channels.telegram.groups''.
Long-polling vs Webhook
- Default: long-polling (no public URL needed).
- Webhook: set ''channels.telegram.webhookUrl'' and ''channels.telegram.webhookSecret'' (optional ''channels.telegram.webhookPath'').
- Local listen binds ''0.0.0.0:8787'' by default, default path ''POST /telegram-webhook''.
- If your public URL is different, use reverse proxy and point ''channels.telegram.webhookUrl'' to public endpoint.
Reply Threading
Telegram supports optional "reply to trigger message" capability (based on tags):
- ''[[reply_to_current]]'' β reply to trigger message
- ''[[reply_to:'' β reply to specified message id
Control via ''channels.telegram.replyToMode'':
- ''first'' (default), ''all'', ''off''.
Audio Messages (Voice Notes vs Audio Files)
Telegram distinguishes voice notes (round bubble) from audio files (with metadata card). For compatibility with old behavior, OpenClaw sends audio files by default.
To force sending voice notes in agent replies, add anywhere in reply:
- ''[[audio_as_voice]]'' β send audio as voice note
This tag won't appear in final delivered text; other channels ignore it.
Use message tool to send voice note: set ''asVoice: true'' and provide voice-compatible audio ''media'' URL (can omit ''message''):
{
action: "send",
channel: "telegram",
to: "123456789",
media: "https://example.com/voice.ogg",
asVoice: true,
}