OpenClaw Telegram Bot: Setup, Config and Features
Telegram (Bot API)
Section titled “Telegram (Bot API)”Status: production-ready for bot DMs + groups via grammY. Long polling is the default mode; webhook mode is optional.
-
Pairing: Default DM policy for Telegram is pairing.
-
Channel troubleshooting: Cross-channel diagnostics and repair playbooks.
-
Gateway configuration: Full channel config patterns and examples.
Quick setup
Section titled “Quick setup”1. Create the bot token in BotFather
Section titled “1. 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.
2. Configure token and DM policy
Section titled “2. Configure token and DM policy”{ channels: { telegram: { enabled: true, botToken: "123:abc", dmPolicy: "pairing", groups: { "*": { requireMention: true } }, }, },}Env fallback: TELEGRAM_BOT_TOKEN=... (default account only).
3. Start gateway and approve first DM
Section titled “3. Start gateway and approve first DM”openclaw gatewayopenclaw pairing list telegramopenclaw pairing approve telegram <CODE>Pairing codes expire after 1 hour.
4. Add the bot to a group
Section titled “4. Add the bot to a group”Add the bot to your group, then set channels.telegram.groups and groupPolicy to match your access model.
Telegram side settings
Section titled “Telegram side settings”Privacy mode and group visibility
Section titled “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.
Group permissions
Section titled “Group permissions”Admin status is controlled in Telegram group settings.
Admin bots receive all group messages, which is useful for always-on group behavior.
Helpful BotFather toggles
Section titled “Helpful BotFather toggles”/setjoingroupsto allow/deny group adds/setprivacyfor group visibility behavior
Access control and activation
Section titled “Access control and activation”DM policy
Section titled “DM policy”channels.telegram.dmPolicy controls direct message access:
-
pairing(default) -
allowlist -
open(requiresallowFromto include"*") -
disabledchannels.telegram.allowFromaccepts numeric IDs and usernames.telegram:/tg:prefixes are accepted and normalized.
Finding your Telegram user ID
Section titled “Finding your Telegram user ID”Safer (no third-party bot):
- DM your bot.
- Run
openclaw logs --follow. - Read
from.id.
Official Bot API method:
curl "https://api.telegram.org/bot<bot_token>/getUpdates"Third-party method (less private): @userinfobot or @getidsbot.
Group policy and allowlists
Section titled “Group policy and allowlists”There are two independent controls:
- Which groups are allowed (
channels.telegram.groups)
-
no
groupsconfig: all groups allowed -
groupsconfigured: acts as allowlist (explicit IDs or"*")- Which senders are allowed in groups (
channels.telegram.groupPolicy)
- Which senders are allowed in groups (
-
open -
allowlist(default) -
disabledgroupAllowFromis used for group sender filtering. If not set, Telegram falls back toallowFrom.Example: allow any member in one specific group:
{ channels: { telegram: { groups: { "-1001234567890": { groupPolicy: "open", requireMention: false, }, }, }, },}Mention behavior
Section titled “Mention behavior”Group replies require mention by default.
Mention can come from:
-
native
@botusernamemention, or -
mention patterns in:
-
agents.list[].groupChat.mentionPatterns -
messages.groupChat.mentionPatternsSession-level command toggles:
-
/activation always -
/activation mentionThese update session state only. Use config for persistence.
Persistent config example:
{ channels: { telegram: { groups: { "*": { requireMention: false }, }, }, },}Getting the group chat ID:
- forward a group message to
@userinfobot/@getidsbot - or read
chat.idfromopenclaw logs --follow - or inspect Bot API
getUpdates
Runtime behavior
Section titled “Runtime behavior”- Telegram is owned by the gateway process.
- Routing is deterministic: Telegram inbound replies back to Telegram (the model does not pick channels).
- Inbound messages normalize into the shared channel envelope with reply metadata and media placeholders.
- Group sessions are isolated by group ID. Forum topics append
:topic:<threadId>to keep topics isolated. - DM messages can carry
message_thread_id; OpenClaw routes them with thread-aware session keys and preserves thread ID for replies. - Long polling uses grammY runner with per-chat/per-thread sequencing. Overall runner sink concurrency uses
agents.defaults.maxConcurrent. - Telegram Bot API has no read-receipt support (
sendReadReceiptsdoes not apply).
Feature reference
Section titled “Feature reference”Draft streaming in Telegram DMs
Section titled “Draft streaming in Telegram DMs”OpenClaw can stream partial replies with Telegram draft bubbles (sendMessageDraft).
Requirements:
-
channels.telegram.streamModeis not"off"(default:"partial") -
private chat
-
inbound update includes
message_thread_id -
bot topics are enabled (
getMe().has_topics_enabled)Modes:
-
off: no draft streaming -
partial: frequent draft updates from partial text -
block: chunked draft updates usingchannels.telegram.draftChunkdraftChunkdefaults for block mode: -
minChars: 200 -
maxChars: 800 -
breakPreference: "paragraph"maxCharsis clamped bychannels.telegram.textChunkLimit.Draft streaming is DM-only; groups/channels do not use draft bubbles.
If you want early real Telegram messages instead of draft updates, use block streaming (
channels.telegram.blockStreaming: true).Telegram-only reasoning stream:
-
/reasoning streamsends reasoning to the draft bubble while generating -
final answer is sent without reasoning text
Formatting and HTML fallback
Section titled “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, OpenClaw retries as plain text.
Link previews are enabled by default and can be disabled with
channels.telegram.linkPreview: false.
Native commands and custom commands
Section titled “Native commands and custom commands”Telegram command menu registration is handled at startup with setMyCommands.
Native command defaults:
-
commands.native: "auto"enables native commands for TelegramAdd custom command menu entries:
{ channels: { telegram: { customCommands: [ { command: "backup", description: "Git backup" }, { command: "generate", description: "Create an image" }, ], }, },}Rules:
-
names are normalized (strip leading
/, lowercase) -
valid pattern:
a-z,0-9,_, length1..32 -
custom commands cannot override native commands
-
conflicts/duplicates are skipped and logged
Notes:
-
custom commands are menu entries only; they do not auto-implement behavior
-
plugin/skill commands can still work when typed even if not shown in Telegram menu
If native commands are disabled, built-ins are removed. Custom/plugin commands may still register if configured.
Common setup failure:
-
setMyCommands failedusually means outbound DNS/HTTPS toapi.telegram.orgis blocked.
Device pairing commands (device-pair plugin)
Section titled “Device pairing commands (device-pair plugin)”When the device-pair plugin is installed:
/pairgenerates setup code- paste code in iOS app
/pair approveapproves latest pending request
More details: Pairing.
Inline buttons
Section titled “Inline buttons”Configure inline keyboard scope:
{ channels: { telegram: { capabilities: { inlineButtons: "allowlist", }, }, },}Per-account override:
{ channels: { telegram: { accounts: { main: { capabilities: { inlineButtons: "allowlist", }, }, }, }, },}Scopes:
-
off -
dm -
group -
all -
allowlist(default)Legacy
capabilities: ["inlineButtons"]maps toinlineButtons: "all".Message action example:
{ 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>
Telegram message actions for agents and automation
Section titled “Telegram message actions for agents and automation”Telegram tool actions include:
-
sendMessage(to,content, optionalmediaUrl,replyToMessageId,messageThreadId) -
react(chatId,messageId,emoji) -
deleteMessage(chatId,messageId) -
editMessage(chatId,messageId,content)Channel message actions expose ergonomic aliases (
send,react,delete,edit,sticker,sticker-search).Gating controls:
-
channels.telegram.actions.sendMessage -
channels.telegram.actions.editMessage -
channels.telegram.actions.deleteMessage -
channels.telegram.actions.reactions -
channels.telegram.actions.sticker(default: disabled)Reaction removal semantics: /tools/reactions
Reply threading tags
Section titled “Reply threading tags”Telegram supports explicit reply threading tags in generated output:
-
[[reply_to_current]]replies to the triggering message -
[[reply_to:<id>]]replies to a specific Telegram message IDchannels.telegram.replyToModecontrols handling: -
first(default) -
all -
off
Forum topics and thread behavior
Section titled “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 rejectssendMessage(...thread_id=1)) -
typing actions still include
message_thread_idTopic inheritance: topic entries inherit group settings unless overridden (
requireMention,allowFrom,skills,systemPrompt,enabled,groupPolicy).Template context includes:
-
MessageThreadId -
IsForumDM thread behavior:
-
private chats with
message_thread_idkeep DM routing but use thread-aware session keys/reply targets.
Audio, video, and stickers
Section titled “Audio, video, and stickers”Audio messages
Section titled “Audio messages”Telegram distinguishes voice notes vs audio files.
-
default: audio file behavior
-
tag
[[audio_as_voice]]in agent reply to force voice-note sendMessage action example:
{ action: "send", channel: "telegram", to: "123456789", media: "https://example.com/voice.ogg", asVoice: true,}Video messages
Section titled “Video messages”Telegram distinguishes video files vs video notes.
Message action example:
{ action: "send", channel: "telegram", to: "123456789", media: "https://example.com/video.mp4", asVideoNote: true,}Video notes do not support captions; provided message text is sent separately.
Stickers
Section titled “Stickers”Inbound sticker handling:
-
static WEBP: downloaded and processed (placeholder
<media:sticker>) -
animated TGS: skipped
-
video WEBM: skipped
Sticker context fields:
-
Sticker.emoji -
Sticker.setName -
Sticker.fileId -
Sticker.fileUniqueId -
Sticker.cachedDescriptionSticker cache file:
-
~/.openclaw/telegram/sticker-cache.jsonStickers are described once (when possible) and cached to reduce repeated vision calls.
Enable sticker actions:
{ channels: { telegram: { actions: { sticker: true, }, }, },}Send sticker action:
{ action: "sticker", channel: "telegram", to: "123456789", fileId: "CAACAgIAAxkBAAI...",}Search cached stickers:
{ action: "sticker-search", channel: "telegram", query: "cat waving", limit: 5,}Reaction notifications
Section titled “Reaction notifications”Telegram reactions arrive as message_reaction updates (separate from message payloads).
When enabled, OpenClaw enqueues system events like:
-
Telegram reaction added: 👍 by Alice (@alice) on msg 42Config:
-
channels.telegram.reactionNotifications:off | own | all(default:own) -
channels.telegram.reactionLevel:off | ack | minimal | extensive(default:minimal)Notes:
-
ownmeans user reactions to bot-sent messages only (best-effort via sent-message cache). -
Telegram does not provide thread IDs in reaction updates.
-
non-forum groups route to group chat session
-
forum groups route to the group general-topic session (
:topic:1), not the exact originating topicallowed_updatesfor polling/webhook includemessage_reactionautomatically.
Config writes from Telegram events and commands
Section titled “Config writes from Telegram events and commands”Channel config writes are enabled by default (configWrites !== false).
Telegram-triggered writes include:
-
group migration events (
migrate_to_chat_id) to updatechannels.telegram.groups -
/config setand/config unset(requires command enablement)Disable:
{ channels: { telegram: { configWrites: false, }, },}Long polling vs webhook
Section titled “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)Default local listener for webhook mode binds to
0.0.0.0:8787.If your public endpoint differs, place a reverse proxy in front and point
webhookUrlat the public URL.
Limits, retry, and CLI targets
Section titled “Limits, retry, and CLI targets”-
channels.telegram.textChunkLimitdefault is 4000. -
channels.telegram.chunkMode="newline"prefers paragraph boundaries (blank lines) before length splitting. -
channels.telegram.mediaMaxMb(default 5) caps inbound Telegram media download/processing size. -
channels.telegram.timeoutSecondsoverrides Telegram API client timeout (if unset, grammY default applies). -
group context history uses
channels.telegram.historyLimitormessages.groupChat.historyLimit(default 50);0disables. -
DM history controls:
-
channels.telegram.dmHistoryLimit -
channels.telegram.dms["<user_id>"].historyLimit -
outbound Telegram API retries are configurable via
channels.telegram.retry.CLI send target can be numeric chat ID or username:
openclaw message send --channel telegram --target 123456789 --message "hi"openclaw message send --channel telegram --target @name --message "hi"Troubleshooting
Section titled “Troubleshooting”Bot does not respond to non mention group messages
Section titled “Bot does not respond to non mention group messages”- If
requireMention=false, Telegram privacy mode must allow full visibility. - BotFather:
/setprivacy-> Disable - then remove + re-add bot to group
openclaw channels statuswarns when config expects unmentioned group messages.openclaw channels status --probecan check explicit numeric group IDs; wildcard"*"cannot be membership-probed.- quick session test:
/activation always.
Bot not seeing group messages at all
Section titled “Bot not seeing group messages at all”- when
channels.telegram.groupsexists, group must be listed (or include"*") - verify bot membership in group
- review logs:
openclaw logs --followfor skip reasons
Commands work partially or not at all
Section titled “Commands work partially or not at all”- authorize your sender identity (pairing and/or
allowFrom) - command authorization still applies even when group policy is
open setMyCommands failedusually indicates DNS/HTTPS reachability issues toapi.telegram.org
Polling or network instability
Section titled “Polling or network instability”- Node 22+ + custom fetch/proxy can trigger immediate abort behavior if AbortSignal types mismatch.
- Some hosts resolve
api.telegram.orgto IPv6 first; broken IPv6 egress can cause intermittent Telegram API failures. - Validate DNS answers:
dig +short api.telegram.org Adig +short api.telegram.org AAAAMore help: Channel troubleshooting.
Telegram config reference pointers
Section titled “Telegram config reference pointers”Primary reference:
Telegram-specific high-signal fields:
- startup/auth:
enabled,botToken,tokenFile,accounts.* - access control:
dmPolicy,allowFrom,groupPolicy,groupAllowFrom,groups,groups.*.topics.* - command/menu:
commands.native,customCommands - threading/replies:
replyToMode - streaming:
streamMode,draftChunk,blockStreaming - formatting/delivery:
textChunkLimit,chunkMode,linkPreview,responsePrefix - media/network:
mediaMaxMb,timeoutSeconds,retry,network.autoSelectFamily,proxy - webhook:
webhookUrl,webhookSecret,webhookPath - actions/capabilities:
capabilities.inlineButtons,actions.sendMessage|editMessage|deleteMessage|reactions|sticker - reactions:
reactionNotifications,reactionLevel - writes/history:
configWrites,historyLimit,dmHistoryLimit,dms.*.historyLimit
Related
Section titled “Related”OpenClaw Expert
Still stuck?
If this page didn't answer your case, ask OpenClaw Expert for step-by-step guidance.