Pairing
Pairing β Default DM policy for Telegram is pairing.
Status: production-ready for bot DMs + groups via grammY. Long polling is the default mode; webhook mode is optional.
Pairing
Pairing β Default DM policy for Telegram is pairing.
Channel troubleshooting
Channel troubleshooting β Cross-channel diagnostics and repair playbooks.
Gateway configuration
Gateway configuration β Full channel config patterns and examples.
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.
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).
Telegram does not use remoteclaw channels login telegram; configure token in config/env, then start gateway.
Start gateway and approve first DM
remoteclaw gatewayremoteclaw pairing list telegramremoteclaw pairing approve telegram <CODE>Pairing codes expire after 1 hour.
Add the bot to a group
Add the bot to your group, then set channels.telegram.groups and groupPolicy to match your access model.
Token resolution order is account-aware. In practice, config values win over env fallback, and TELEGRAM_BOT_TOKEN only applies to the default account.
Telegram bots default to Privacy Mode, which limits what group messages they receive.
If the bot must see all group messages, either:
/setprivacy, orWhen toggling privacy mode, remove + re-add the bot in each group so Telegram applies the change.
Admin status is controlled in Telegram group settings.
Admin bots receive all group messages, which is useful for always-on group behavior.
/setjoingroups to allow/deny group adds/setprivacy for group visibility behaviorchannels.telegram.dmPolicy controls direct message access:
pairing (default)allowlistopen (requires allowFrom to include "*")disabledchannels.telegram.allowFrom accepts numeric Telegram user IDs. telegram: / tg: prefixes are accepted and normalized.
The onboarding wizard accepts @username input and resolves it to numeric IDs.
If you upgraded and your config contains @username allowlist entries, run remoteclaw doctor --fix to resolve them (best-effort; requires a Telegram bot token).
Safer (no third-party bot):
remoteclaw logs --follow.from.id.Official Bot API method:
curl "https://api.telegram.org/bot<bot_token>/getUpdates"Third-party method (less private): @userinfobot or @getidsbot.
There are two independent controls:
Which groups are allowed (channels.telegram.groups)
groups config: all groups allowedgroups configured: acts as allowlist (explicit IDs or "*")Which senders are allowed in groups (channels.telegram.groupPolicy)
openallowlist (default)disabledgroupAllowFrom is used for group sender filtering. If not set, Telegram falls back to allowFrom.
groupAllowFrom entries must be numeric Telegram user IDs.
Runtime note: if channels.telegram is completely missing, runtime falls back to groupPolicy="allowlist" for group policy evaluation (even if channels.defaults.groupPolicy is set).
Example: allow any member in one specific group:
{ channels: { telegram: { groups: { "-1001234567890": { groupPolicy: "open", requireMention: false, }, }, }, },}Group replies require mention by default.
Mention can come from:
@botusername mention, oragents.list[].groupChat.mentionPatternsmessages.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:
@userinfobot / @getidsbotchat.id from remoteclaw logs --followgetUpdates:topic:<threadId> to keep topics isolated.message_thread_id; RemoteClaw routes them with thread-aware session keys and preserves thread ID for replies.agents.defaults.maxConcurrent.sendReadReceipts does not apply).RemoteClaw can stream partial replies by sending a temporary Telegram message and editing it as text arrives.
Requirement:
channels.telegram.streaming is off | partial | block | progress (default: off)progress maps to partial on Telegram (compat with cross-channel naming)channels.telegram.streamMode and boolean streaming values are auto-mappedThis works in direct chats and groups/topics.
For text-only replies, RemoteClaw keeps the same preview message and performs a final edit in place (no second message).
For complex replies (for example media payloads), RemoteClaw falls back to normal final delivery and then cleans up the preview message.
Preview streaming is separate from block streaming. When block streaming is explicitly enabled for Telegram, RemoteClaw skips the preview stream to avoid double-streaming.
Telegram-only reasoning stream:
/reasoning stream sends reasoning to the live preview while generatingOutbound text uses Telegram parse_mode: "HTML".
Link previews are enabled by default and can be disabled with channels.telegram.linkPreview: false.
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:
/, lowercase)a-z, 0-9, _, length 1..32Notes:
If native commands are disabled, built-ins are removed. Custom/plugin commands may still register if configured.
Common setup failure:
setMyCommands failed usually means outbound DNS/HTTPS to api.telegram.org is blocked.device-pair plugin)When the device-pair plugin is installed:
/pair generates setup code/pair approve approves latest pending requestMore details: Pairing.
Configure inline keyboard scope:
{ channels: { telegram: { capabilities: { inlineButtons: "allowlist", }, }, },}Per-account override:
{ channels: { telegram: { accounts: { main: { capabilities: { inlineButtons: "allowlist", }, }, }, }, },}Scopes:
offdmgroupallallowlist (default)Legacy capabilities: ["inlineButtons"] maps to inlineButtons: "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 tool actions include:
sendMessage (to, content, optional mediaUrl, 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.sendMessagechannels.telegram.actions.editMessagechannels.telegram.actions.deleteMessagechannels.telegram.actions.reactionschannels.telegram.actions.sticker (default: disabled)Reaction removal semantics: /tools/reactions
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.replyToMode controls handling:
off (default)firstallNote: off disables implicit reply threading. Explicit [[reply_to_*]] tags are still honored.
Forum supergroups:
:topic:<threadId>channels.telegram.groups.<chatId>.topics.<threadId>General topic (threadId=1) special-case:
message_thread_id (Telegram rejects sendMessage(...thread_id=1))message_thread_idTopic inheritance: topic entries inherit group settings unless overridden (requireMention, allowFrom, skills, systemPrompt, enabled, groupPolicy).
Template context includes:
MessageThreadIdIsForumDM thread behavior:
message_thread_id keep DM routing but use thread-aware session keys/reply targets.Telegram distinguishes voice notes vs audio files.
[[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,}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.
Inbound sticker handling:
<media:sticker>)Sticker context fields:
Sticker.emojiSticker.setNameSticker.fileIdSticker.fileUniqueIdSticker.cachedDescriptionSticker cache file:
~/.remoteclaw/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,}Telegram reactions arrive as message_reaction updates (separate from message payloads).
When enabled, RemoteClaw 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:
own means user reactions to bot-sent messages only (best-effort via sent-message cache).:topic:1), not the exact originating topicallowed_updates for polling/webhook include message_reaction automatically.
ackReaction sends an acknowledgement emoji while RemoteClaw is processing an inbound message.
Resolution order:
channels.telegram.accounts.<accountId>.ackReactionchannels.telegram.ackReactionmessages.ackReactionagents.list[].identity.emoji, else βπβ)Notes:
"" to disable the reaction for a channel or account.Channel config writes are enabled by default (configWrites !== false).
Telegram-triggered writes include:
migrate_to_chat_id) to update channels.telegram.groups/config set and /config unset (requires command enablement)Disable:
{ channels: { telegram: { configWrites: false, }, },}Default: long polling.
Webhook mode:
channels.telegram.webhookUrlchannels.telegram.webhookSecret (required when webhook URL is set)channels.telegram.webhookPath (default /telegram-webhook)channels.telegram.webhookHost (default 127.0.0.1)Default local listener for webhook mode binds to 127.0.0.1:8787.
If your public endpoint differs, place a reverse proxy in front and point webhookUrl at the public URL.
Set webhookHost (for example 0.0.0.0) when you intentionally need external ingress.
channels.telegram.textChunkLimit default 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.timeoutSeconds overrides Telegram API client timeout (if unset, grammY default applies).channels.telegram.historyLimit or messages.groupChat.historyLimit (default 50); 0 disables.channels.telegram.dmHistoryLimitchannels.telegram.dms["<user_id>"].historyLimitchannels.telegram.retry.CLI send target can be numeric chat ID or username:
remoteclaw message send --channel telegram --target 123456789 --message "hi"remoteclaw message send --channel telegram --target @name --message "hi"requireMention=false, Telegram privacy mode must allow full visibility.
/setprivacy -> Disableremoteclaw channels status warns when config expects unmentioned group messages.remoteclaw channels status --probe can check explicit numeric group IDs; wildcard "*" cannot be membership-probed./activation always.channels.telegram.groups exists, group must be listed (or include "*")remoteclaw logs --follow for skip reasonsallowFrom)opensetMyCommands failed usually indicates DNS/HTTPS reachability issues to api.telegram.orgapi.telegram.org to IPv6 first; broken IPv6 egress can cause intermittent Telegram API failures.TypeError: fetch failed or Network request for 'getUpdates' failed!, RemoteClaw now retries these as recoverable network errors.channels.telegram.proxy:channels: telegram: proxy: socks5://user:pass@proxy-host:1080autoSelectFamily=true (except WSL2) and dnsResultOrder=ipv4first.channels: telegram: network: autoSelectFamily: falseREMOTECLAW_TELEGRAM_DISABLE_AUTO_SELECT_FAMILY=1REMOTECLAW_TELEGRAM_ENABLE_AUTO_SELECT_FAMILY=1REMOTECLAW_TELEGRAM_DNS_RESULT_ORDER=ipv4firstdig +short api.telegram.org Adig +short api.telegram.org AAAAMore help: Channel troubleshooting.
Primary reference:
channels.telegram.enabled: enable/disable channel startup.
channels.telegram.botToken: bot token (BotFather).
channels.telegram.tokenFile: read token from file path.
channels.telegram.dmPolicy: pairing | allowlist | open | disabled (default: pairing).
channels.telegram.allowFrom: DM allowlist (numeric Telegram user IDs). open requires "*". remoteclaw doctor --fix can resolve legacy @username entries to IDs.
channels.telegram.groupPolicy: open | allowlist | disabled (default: allowlist).
channels.telegram.groupAllowFrom: group sender allowlist (numeric Telegram user IDs). remoteclaw doctor --fix can resolve legacy @username entries to IDs.
channels.telegram.groups: per-group defaults + allowlist (use "*" for global defaults).
channels.telegram.groups.<id>.groupPolicy: per-group override for groupPolicy (open | allowlist | disabled).channels.telegram.groups.<id>.requireMention: mention gating default.channels.telegram.groups.<id>.skills: skill filter (omit = all skills, empty = none).channels.telegram.groups.<id>.allowFrom: per-group sender allowlist override.channels.telegram.groups.<id>.systemPrompt: extra system prompt for the group.channels.telegram.groups.<id>.enabled: disable the group when false.channels.telegram.groups.<id>.topics.<threadId>.*: per-topic overrides (same fields as group).channels.telegram.groups.<id>.topics.<threadId>.groupPolicy: per-topic override for groupPolicy (open | allowlist | disabled).channels.telegram.groups.<id>.topics.<threadId>.requireMention: per-topic mention gating override.channels.telegram.capabilities.inlineButtons: off | dm | group | all | allowlist (default: allowlist).
channels.telegram.accounts.<account>.capabilities.inlineButtons: per-account override.
channels.telegram.replyToMode: off | first | all (default: off).
channels.telegram.textChunkLimit: outbound chunk size (chars).
channels.telegram.chunkMode: length (default) or newline to split on blank lines (paragraph boundaries) before length chunking.
channels.telegram.linkPreview: toggle link previews for outbound messages (default: true).
channels.telegram.streaming: off | partial | block | progress (live stream preview; default: off; progress maps to partial).
channels.telegram.mediaMaxMb: inbound/outbound media cap (MB).
channels.telegram.retry: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter).
channels.telegram.network.autoSelectFamily: override Node autoSelectFamily (true=enable, false=disable). Defaults to enabled on Node 22+, with WSL2 defaulting to disabled.
channels.telegram.network.dnsResultOrder: override DNS result order (ipv4first or verbatim). Defaults to ipv4first on Node 22+.
channels.telegram.proxy: proxy URL for Bot API calls (SOCKS/HTTP).
channels.telegram.webhookUrl: enable webhook mode (requires channels.telegram.webhookSecret).
channels.telegram.webhookSecret: webhook secret (required when webhookUrl is set).
channels.telegram.webhookPath: local webhook path (default /telegram-webhook).
channels.telegram.webhookHost: local webhook bind host (default 127.0.0.1).
channels.telegram.actions.reactions: gate Telegram tool reactions.
channels.telegram.actions.sendMessage: gate Telegram tool message sends.
channels.telegram.actions.deleteMessage: gate Telegram tool message deletes.
channels.telegram.actions.sticker: gate Telegram sticker actions β send and search (default: false).
channels.telegram.reactionNotifications: off | own | all β control which reactions trigger system events (default: own when not set).
channels.telegram.reactionLevel: off | ack | minimal | extensive β control agentβs reaction capability (default: minimal when not set).
Telegram-specific high-signal fields:
enabled, botToken, tokenFile, accounts.*dmPolicy, allowFrom, groupPolicy, groupAllowFrom, groups, groups.*.topics.*commands.native, customCommandsreplyToModestreaming (preview), blockStreamingtextChunkLimit, chunkMode, linkPreview, responsePrefixmediaMaxMb, timeoutSeconds, retry, network.autoSelectFamily, proxywebhookUrl, webhookSecret, webhookPath, webhookHostcapabilities.inlineButtons, actions.sendMessage|editMessage|deleteMessage|reactions|stickerreactionNotifications, reactionLevelconfigWrites, historyLimit, dmHistoryLimit, dms.*.historyLimit