Skip to main content

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

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

Env fallback: TELEGRAM_BOT_TOKEN=... (default account only). Telegram does not use openclaw channels login telegram; configure token in config/env, then start gateway.
3

Start gateway and approve first DM

Pairing codes expire after 1 hour.
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.
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 side settings

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.
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 behavior

Access control and activation

channels.telegram.dmPolicy controls direct message access:
  • pairing (default)
  • allowlist (requires at least one sender ID in allowFrom)
  • open (requires allowFrom to include "*")
  • disabled
channels.telegram.allowFrom accepts numeric Telegram user IDs. telegram: / tg: prefixes are accepted and normalized. dmPolicy: "allowlist" with empty allowFrom blocks all DMs and is rejected by config validation. The onboarding wizard accepts @username input and resolves it to numeric IDs. If you upgraded and your config contains @username allowlist entries, run openclaw doctor --fix to resolve them (best-effort; requires a Telegram bot token). If you previously relied on pairing-store allowlist files, openclaw doctor --fix can recover entries into channels.telegram.allowFrom in allowlist flows (for example when dmPolicy: "allowlist" has no explicit IDs yet).

Finding your Telegram user ID

Safer (no third-party bot):
  1. DM your bot.
  2. Run openclaw logs --follow.
  3. Read from.id.
Official Bot API method:
Third-party method (less private): @userinfobot or @getidsbot.

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 (sendReadReceipts does not apply).

If Telegram replies stop or repeat network errors

For users, the visible symptom is simple: Telegram messages stop getting replies, or replies arrive only after a delay. Ask an operator to run the checks below; no Telegram app setting usually needs to change. For operators, repeated gateway logs like this usually mean long polling cannot keep a stable getUpdates request alive:
Check the startup health report and doctor output first:
channels.telegram.timeoutSeconds controls only the Telegram long-poll request window. Leave it unset unless you have a network-specific reason to override it. When unset, WednesdayAI applies the safe 30 second polling window. Values below 30 seconds are unsafe because they can create short, bursty reconnect loops; values above 50 seconds can exceed the gateway’s safe request window. The gateway clamps unsafe values at runtime, startup health reports the configured and applied value, and wednesdayai doctor --fix repairs the config:
  • below 30 seconds: remove the override so the 30 second default applies;
  • above 50 seconds: rewrite the override to 50 seconds.
If doctor reports a duplicate poller, make sure only one local account and one gateway process owns each Telegram bot token. Disable duplicate accounts, remove accidental shared token fallbacks, or restart the gateway to clear stale local pollers. Developer note: the timeout policy is applied only to the bot polling client. Standalone outbound send helpers use their existing send-client timeout path, so changing the polling policy should not change message delivery timeout behavior.

Multi-bot fleet

One gateway instance can run a fleet of Telegram bot accounts. Define shared defaults under channels.telegram, then add named bot accounts under channels.telegram.accounts.
Each enabled account resolves its own token. Named accounts inherit most top-level access-control defaults unless the account overrides them. The exception is channels.telegram.groups: in multi-account fleets, top-level groups is not inherited by named accounts because each bot may be present in different Telegram groups. Repeat group allowlists, mention settings, and topic settings under each account that should serve those groups. Single-account configs keep the legacy top-level groups fallback. Named accounts can also fall back to top-level botToken or tokenFile for backward-compatible single-bot configs. TELEGRAM_BOT_TOKEN is only a fallback for the default account. In multi-bot fleets, set account-level botToken or tokenFile for each named account so two enabled accounts do not accidentally resolve to the same bot token and enter a duplicate-owner conflict. Account IDs are operator labels, not Telegram identifiers. Use stable names such as alerts, support, or ops, and keep bot tokens in config secrets or token files.

Polling, webhook, and ownership

Each account runs in one of these modes:
  • polling: default long-polling mode using getUpdates.
  • webhook: enabled when the account has webhookUrl.
  • disabled: account is configured but not started.
Polling and webhook accounts can coexist in the same gateway as long as they use distinct bot tokens. A single bot token must have one active local owner. If two accounts resolve to the same token, the duplicate account is marked conflicted and the first owner remains authoritative. Polling startup performs non-destructive webhook cleanup before getUpdates: it calls Telegram with drop_pending_updates: false. WednesdayAI does not implicitly discard pending Telegram updates. Treat any manual use of drop_pending_updates outside WednesdayAI as a deliberate data loss operation. Long-polling uses a safe Telegram API timeout window. channels.telegram.timeoutSeconds (default 30 seconds) controls the getUpdates long-poll window. The bot’s underlying HTTP client abort timeout is derived automatically as the poll window plus a 10-second buffer β€” for example, a 30-second poll window gives the HTTP client 40 seconds before it aborts β€” so an empty poll that completes right at the window boundary is never misidentified as a network error. Explicit values below 30 seconds are treated as unsafe and clamped to 30 seconds; values above 50 seconds are clamped to 50 seconds. Account-level channels.telegram.accounts.<id>.timeoutSeconds wins over the channel-level value for that account. Standalone outbound send helpers keep their existing timeout behavior because they use a separate send client path. Startup health and wednesdayai doctor report the config path, configured value, and applied safe value when a timeout override is unsafe. wednesdayai doctor --fix writes the durable version of that runtime policy back to config: below-minimum overrides are removed when the applied safe value is the default, and above-maximum overrides are rewritten to 50 seconds. Conflict states usually mean one of:
  • another local account in the same gateway resolved the same bot token;
  • another gateway process is polling the same bot;
  • Telegram still has a webhook owner while this account is trying to poll;
  • the webhook endpoint is reachable but rejecting or timing out.
Fix conflicts by ensuring one account, one mode, and one gateway process owns each bot token. When duplicate polling ownership is detected, the gateway records a conflicted account with the account id and sanitized token fingerprint, never the raw bot token. Startup health can report conflicts already recorded when the startup report is emitted; wednesdayai doctor reads the running gateway channel-status snapshot for live duplicate-poller warnings.

Channels UI health

The Channels UI shows per-account Telegram fleet state from application.telegram. High-signal fields:
  • mode, state, reason, and previousState show the account lifecycle.
  • configured, running, gatewayInstanceId, owner, and ownerStartedAt show ownership.
  • retryAt marks recoverable backoff or flood-wait recovery windows.
  • poll.lastPollAt, poll.lastConflictAt, and poll.lastOffset describe long-polling health.
  • webhook.configured, webhook.urlHash, webhook.path, webhook.secretPresent, webhook.pendingUpdateCount, and webhook request counters describe webhook health without showing the raw URL secret or request body.
  • delivery.queueDepth, delivery.lastDeliveryStatus, delivery.retryAfterMs, delivery.nextRetryAt, and delivery.lastDestinationHash describe outbound send health.
  • lastApiRequests, lastUpdates, and lastDeliveries retain recent events with hashed Telegram identifiers only.
Use the Channels UI for transport health and configuration troubleshooting. Use Sessions active runs for execution state. The active-runs surface is correlated to Telegram ingress with hashed account, chat, user, and thread identity; it is not the place to diagnose Bot API ownership.

OTel and Langfuse export

When diagnostics OTel export is enabled, Telegram lifecycle, polling, webhook, update, and delivery events are exported as spans, counters, and histograms. Telegram-correlated run, model, tool, and model-usage spans inherit the same hashed actor/session identity so Langfuse can group activity without receiving raw Telegram identifiers. Representative exported attributes:
  • openclaw.channel = "telegram"
  • telegram.account.hash
  • telegram.bot.hash
  • telegram.chat.hash
  • telegram.user.hash
  • telegram.thread.hash
  • telegram.update.id
  • telegram.mode
  • telegram.account.state
  • telegram.api.method
  • telegram.retry_after_ms
  • langfuse.session.id
  • langfuse.user.id
Privacy boundary:
  • Bot tokens, raw chat IDs, raw user IDs, raw thread IDs, raw message IDs, webhook secrets, raw webhook URLs, and message content are not exported.
  • Telegram tool input/output capture is replaced with a fixed redaction marker on Telegram-correlated spans.
  • Telegram-shaped OTLP log records redact the body and drop aggregate argument fields when they contain Telegram IDs or message content.

Feature reference

OpenClaw can stream partial replies in real time:
  • direct chats: Telegram native draft streaming via sendMessageDraft
  • groups/topics: preview message + editMessageText
Requirement:
  • channels.telegram.streaming is off | partial | block | progress (default: partial)
  • progress maps to partial on Telegram (compat with cross-channel naming)
  • legacy channels.telegram.streamMode and boolean streaming values are auto-mapped
  • the global streaming value is inherited by all accounts β€” per-account config does not need its own streaming key to pick it up. Set streaming once at the channel level to apply it to all bots.
Telegram enabled sendMessageDraft for all bots in Bot API 9.5 (March 1, 2026).For text-only replies:
  • DM: OpenClaw updates the draft in place (no extra preview message)
  • group/topic: OpenClaw keeps the same preview message and performs a final edit in place (no second message)
For complex replies (for example media payloads), OpenClaw 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, OpenClaw skips the preview stream to avoid double-streaming.If native draft transport is unavailable/rejected, OpenClaw automatically falls back to sendMessage + editMessageText.Telegram-only reasoning stream:
  • /reasoning stream sends reasoning to the live preview while generating
  • final answer is sent without reasoning text
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.
Telegram command menu registration is handled at startup with setMyCommands.Native command defaults:
  • commands.native: "auto" enables native commands for Telegram
Add custom command menu entries:
Rules:
  • names are normalized (strip leading /, lowercase)
  • valid pattern: a-z, 0-9, _, length 1..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 failed usually means outbound DNS/HTTPS to api.telegram.org is blocked.

Device pairing commands (device-pair plugin)

When the device-pair plugin is installed:
  1. /pair generates setup code
  2. paste code in iOS app
  3. /pair approve approves latest pending request
More details: Pairing.
Configure inline keyboard scope:
Per-account override:
Scopes:
  • off
  • dm
  • group
  • all
  • allowlist (default)
Legacy capabilities: ["inlineButtons"] maps to inlineButtons: "all".Message action example:
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)
  • createForumTopic (chatId, name, optional iconColor, iconCustomEmojiId)
Channel message actions expose ergonomic aliases (send, react, delete, edit, sticker, sticker-search, topic-create).Gating controls:
  • channels.telegram.actions.sendMessage
  • channels.telegram.actions.deleteMessage
  • channels.telegram.actions.editMessage
  • channels.telegram.actions.reactions
  • channels.telegram.actions.sticker (default: disabled)
  • channels.telegram.actions.createForumTopic (default: enabled)
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 ID
channels.telegram.replyToMode controls handling:
  • off (default)
  • first
  • all
Note: off disables implicit reply threading. Explicit [[reply_to_*]] tags are still honored.
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
Topic inheritance: topic entries inherit group settings unless overridden (requireMention, allowFrom, skills, systemPrompt, enabled, groupPolicy).Template context includes:
  • MessageThreadId
  • IsForum
DM thread behavior:
  • private chats with message_thread_id keep DM routing but use thread-aware session keys/reply targets.

Audio messages

Telegram distinguishes voice notes vs audio files.
  • default: audio file behavior
  • tag [[audio_as_voice]] in agent reply to force voice-note send
Message action example:

Video messages

Telegram distinguishes video files vs video notes.Message action example:
Video notes do not support captions; provided message text is sent separately.

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.cachedDescription
Sticker cache file:
  • ~/.openclaw/telegram/sticker-cache.json
Stickers are described once (when possible) and cached to reduce repeated vision calls.Enable sticker actions:
Send sticker action:
Search cached stickers:
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 42
Config:
  • 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).
  • Reaction events still respect Telegram access controls (dmPolicy, allowFrom, groupPolicy, groupAllowFrom); unauthorized senders are dropped.
  • 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 topic
allowed_updates for polling/webhook include message_reaction automatically.
ackReaction sends an acknowledgement emoji while OpenClaw is processing an inbound message.Resolution order:
  • channels.telegram.accounts.<accountId>.ackReaction
  • channels.telegram.ackReaction
  • messages.ackReaction
  • agent identity emoji fallback (agents.list[].identity.emoji, else β€πŸ‘€β€)
Notes:
  • Telegram expects unicode emoji (for example β€πŸ‘€β€).
  • Use "" to disable the reaction for a channel or account.
Channel config writes are enabled by default (configWrites !== false).Telegram-triggered writes include:
  • group migration events (migrate_to_chat_id) to update channels.telegram.groups
  • /config set and /config unset (requires command enablement)
Disable:
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)
  • optional channels.telegram.webhookPort (default 8787)
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/block boundaries and packs as many complete paragraphs as fit into each message up to textChunkLimit, falling back to length-based splitting only when a single block exceeds the limit. Fenced code blocks and tables are kept intact.
  • channels.telegram.chunkNewlinePacking (default true): set to false to restore one message per paragraph (the pre-packing behaviour).
  • channels.telegram.mediaMaxMb (default 5) caps inbound Telegram media download/processing size.
  • channels.telegram.timeoutSeconds controls the Telegram long-poll (getUpdates) window. Safe values are 30-50 seconds; unset uses 30 seconds. The bot’s HTTP client abort timeout is derived as this value plus 10 seconds (for example, 30s poll window β†’ 40s HTTP abort), ensuring an empty poll that completes at the window boundary is never aborted. Account-level channels.telegram.accounts.<id>.timeoutSeconds overrides the channel-level value.
  • group context history uses channels.telegram.historyLimit or messages.groupChat.historyLimit (default 50); 0 disables.
  • DM history controls:
    • channels.telegram.dmHistoryLimit
    • channels.telegram.dms["<user_id>"].historyLimit
  • channels.telegram.retry config applies to Telegram send helpers (CLI/tools/actions) for recoverable outbound API errors.
CLI send target can be numeric chat ID or username:

Troubleshooting

  • If requireMention=false, Telegram privacy mode must allow full visibility.
    • BotFather: /setprivacy -> Disable
    • then remove + re-add bot to group
  • openclaw channels status warns when config expects unmentioned group messages.
  • openclaw channels status --probe can check explicit numeric group IDs; wildcard "*" cannot be membership-probed.
  • quick session test: /activation always.
  • when channels.telegram.groups exists, group must be listed (or include "*")
  • verify bot membership in group
  • review logs: openclaw logs --follow for skip reasons
  • authorize your sender identity (pairing and/or numeric allowFrom)
  • command authorization still applies even when group policy is open
  • setMyCommands failed usually indicates DNS/HTTPS reachability issues to api.telegram.org
  • Node 22+ + custom fetch/proxy can trigger immediate abort behavior if AbortSignal types mismatch.
  • Some hosts resolve api.telegram.org to IPv6 first; broken IPv6 egress can cause intermittent Telegram API failures.
  • If logs include TypeError: fetch failed or Network request for 'getUpdates' failed!, WednesdayAI treats these as recoverable long-polling network errors for read-only API calls (getUpdates, getFile, and other get* methods). The IPv4-fallback retry fires once on these methods. Mutation methods (sendMessage, editMessageText, deleteMessage, etc.) are not retried on TypeError: fetch failed β€” a lost-in-transit HTTP response for a mutation could cause duplicate delivery, so the error propagates without retry. On a recoverable read error, WednesdayAI stops the active runner and bot, refreshes polling transport state, backs off, and creates a fresh bot before the next getUpdates cycle.
  • Repeated getUpdates failures usually indicate unsafe low channels.telegram.timeoutSeconds, unstable DNS/TLS egress to api.telegram.org, a stale proxy connection, or duplicate pollers for one bot token. Run wednesdayai doctor to see timeout and duplicate-poller warnings, and wednesdayai doctor --fix to repair unsafe timeout overrides.
  • On VPS hosts with unstable direct egress/TLS, route Telegram API calls through channels.telegram.proxy:
  • Node 22+ defaults to autoSelectFamily=true (except WSL2) and dnsResultOrder=ipv4first.
  • If your host is WSL2 or explicitly works better with IPv4-only behavior, force family selection:
  • Environment overrides (temporary):
    • OPENCLAW_TELEGRAM_DISABLE_AUTO_SELECT_FAMILY=1
    • OPENCLAW_TELEGRAM_ENABLE_AUTO_SELECT_FAMILY=1
    • OPENCLAW_TELEGRAM_DNS_RESULT_ORDER=ipv4first
  • Validate DNS answers:
WednesdayAI drains in-flight message buffers before the Telegram bot stops:
  • Media group buffer: multi-photo messages grouped within the same media_group_id are collected with a short timer before processing. On shutdown, buffered groups are flushed immediately through the normal media processing pipeline.
  • Text fragment buffer: messages split across the 4096-character Telegram limit are collected and re-assembled. On shutdown, buffered fragments are flushed and dispatched before the bot stops.
Both buffers drain synchronously into their processing chains when the abort signal fires, so messages in-flight at shutdown are not lost under normal operating conditions.If messages are still being lost at restart, check:
  • whether the process receives a clean SIGTERM (not SIGKILL) β€” only SIGTERM allows the abort-signal drain to run
  • journalctl --user -u openclaw-gateway -n 50 for drain log entries confirming flush occurred
More help: Channel troubleshooting.

Telegram config reference pointers

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). allowlist requires at least one sender ID. open requires "*". openclaw doctor --fix can resolve legacy @username entries to IDs and can recover allowlist entries from pairing-store files in allowlist migration flows.
  • channels.telegram.defaultTo: default Telegram target used by CLI --deliver when no explicit --reply-to is provided.
  • channels.telegram.groupPolicy: open | allowlist | disabled (default: allowlist).
  • channels.telegram.groupAllowFrom: group sender allowlist (numeric Telegram user IDs). openclaw doctor --fix can resolve legacy @username entries to IDs. Non-numeric entries are ignored at auth time. Group auth does not use DM pairing-store fallback (2026.2.25+).
  • Multi-account precedence:
    • channels.telegram.accounts.default.allowFrom and channels.telegram.accounts.default.groupAllowFrom apply only to the default account.
    • Named accounts inherit channels.telegram.allowFrom and channels.telegram.groupAllowFrom when account-level values are unset.
    • Named accounts do not inherit channels.telegram.accounts.default.allowFrom / groupAllowFrom.
  • 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>.disableAudioPreflight: skip automatic voice-note transcription for mention detection in this group.
    • 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.groups.<id>.topics.<threadId>.disableAudioPreflight: per-topic voice-note transcription skip.
  • channels.telegram.direct: per-DM configuration keyed by chat ID.
    • channels.telegram.direct.<chatId>.dmPolicy: per-DM override (open | disabled | allowlist).
    • channels.telegram.direct.<chatId>.tools: per-DM tool policy overrides.
    • channels.telegram.direct.<chatId>.skills: per-DM skill filter.
    • channels.telegram.direct.<chatId>.allowFrom: per-DM sender allowlist override.
    • channels.telegram.direct.<chatId>.systemPrompt: extra system prompt for the DM.
    • channels.telegram.direct.<chatId>.enabled: disable the DM when false.
    • channels.telegram.direct.<chatId>.requireTopic: require messages to be from a topic when topics are enabled.
    • channels.telegram.direct.<chatId>.topics.<threadId>.*: per-DM topic overrides (same fields as group topics).
  • channels.telegram.capabilities.inlineButtons: off | dm | group | all | allowlist (default: allowlist).
  • channels.telegram.accounts.<account>.capabilities.inlineButtons: per-account override.
  • channels.telegram.commands.nativeSkills: enable/disable Telegram native skills commands.
  • channels.telegram.replyToMode: off | first | all (default: off).
  • channels.telegram.textChunkLimit: outbound chunk size (chars).
  • channels.telegram.chunkMode: length (default) or newline to pack paragraph blocks up to textChunkLimit per message; oversized blocks fall back to length splitting.
  • channels.telegram.chunkNewlinePacking: packing mode for newline chunking (default: true). Set to false to restore one message per paragraph.
  • channels.telegram.linkPreview: toggle link previews for outbound messages (default: true).
  • channels.telegram.streaming: off | partial | block | progress (live stream preview; default: partial; progress maps to partial; block is legacy preview mode compatibility). In DMs, partial uses native sendMessageDraft when available. Per-account configs inherit this value β€” set it once at the channel level to apply to all bots. To override for a single account, set channels.telegram.accounts.<id>.streaming.
  • channels.telegram.mediaMaxMb: inbound Telegram media download/processing cap (MB).
  • channels.telegram.retry: retry policy for Telegram send helpers (CLI/tools/actions) on recoverable outbound API errors (attempts, minDelayMs, maxDelayMs, jitter).
  • channels.telegram.timeoutSeconds: Telegram long-poll (getUpdates) window in seconds. Safe range is 30-50; unset applies 30. The bot’s HTTP client abort timeout is derived as this value plus 10 seconds. Per-account values under channels.telegram.accounts.<id>.timeoutSeconds take precedence.
  • 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.webhookPort: local webhook bind port (default 8787).
  • 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.editMessage: gate Telegram tool message edits (default: true).
  • channels.telegram.actions.sticker: gate Telegram sticker actions β€” send and search (default: false).
  • channels.telegram.actions.createForumTopic: gate Telegram forum topic creation (default: true).
  • 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).
  • channels.telegram.configWrites: allow channel-initiated config writes from Telegram events/commands (default: true).
  • channels.telegram.ackReaction: per-channel ack reaction emoji override (unicode emoji, e.g. β€πŸ‘€β€). Use "" to disable.
  • channels.telegram.responsePrefix: per-channel outbound response prefix override. Use "auto" to derive [{identity.name}] from the routed agent.
  • channels.telegram.heartbeat: per-channel heartbeat visibility settings.
  • channels.telegram.markdown: markdown formatting overrides (tables).
  • channels.telegram.defaultAccount: default account id when multiple accounts are configured.
  • channels.telegram.groups.<id>.tools: per-group tool policy overrides.
  • channels.telegram.groups.<id>.toolsBySender: per-group tool policy by sender.
  • Configuration reference - Telegram
Telegram-specific high-signal fields:
  • startup/auth: enabled, botToken, tokenFile, accounts.*
  • access control: dmPolicy, allowFrom, groupPolicy, groupAllowFrom, groups, groups.*.topics.*, direct
  • delivery target: defaultTo
  • command/menu: commands.native, commands.nativeSkills, customCommands
  • threading/replies: replyToMode
  • streaming: streaming (preview), blockStreaming
  • formatting/delivery: textChunkLimit, chunkMode, linkPreview, responsePrefix
  • media/network: mediaMaxMb, timeoutSeconds, retry, network.autoSelectFamily, proxy
  • webhook: webhookUrl, webhookSecret, webhookPath, webhookHost
  • actions/capabilities: capabilities.inlineButtons, actions.sendMessage|editMessage|deleteMessage|reactions|sticker
  • reactions: reactionNotifications, reactionLevel
  • writes/history: configWrites, historyLimit, dmHistoryLimit, dms.*.historyLimit