Pairing
Channel troubleshooting
Gateway configuration
Quick setup
Create the bot token in BotFather
- Chat flow: open Telegram, chat with @BotFather (confirm the handle is exactly
@BotFather), run/newbot, follow the prompts, and save the token. - Web flow: open BotFather’s web app — it runs in every Telegram client, including web.telegram.org — create the bot in the UI, and copy its token.
Configure token and DM policy
TELEGRAM_BOT_TOKEN (default account only; named accounts must use botToken or tokenFile).
Telegram does not use openclaw channels login telegram; set the token in config/env, then start the gateway.Add the bot to a group
- your Telegram user ID, for
allowFrom/groupAllowFrom - the Telegram group chat ID, as the key under
channels.telegram.groups
openclaw logs --follow, a forwarded-ID bot, or Bot API getUpdates. After the group is allowed, /whoami@<bot_username> confirms the user and group IDs.Negative supergroup IDs starting with -100 are group chat IDs. They go under channels.telegram.groups, not groupAllowFrom.tokenFile beats botToken beats env, and config always wins over TELEGRAM_BOT_TOKEN (which only resolves for the default account). After a successful startup, OpenClaw caches the bot identity for up to 24 hours so restarts skip an extra getMe call; changing or removing the token clears that cache.Telegram side settings
Privacy mode and group visibility
Privacy mode and group visibility
- disable privacy mode via
/setprivacy, or - make the bot a group admin.
Group permissions
Group permissions
Helpful BotFather toggles
Helpful BotFather toggles
/setjoingroups— allow/deny group adds/setprivacy— group visibility behavior
Access control and activation
Group bot identity
In groups and forum topics, an explicit mention of the configured bot handle (for example@my_bot) addresses the selected OpenClaw agent, even when the agent persona name differs from the Telegram username. Group silence policy still applies to unrelated traffic, but the bot handle itself is never “someone else.”
- DM policy
- Group policy and allowlists
- Mention behavior
channels.telegram.dmPolicy controls direct message access:pairing(default)allowlist(requires at least one sender ID inallowFrom)open(requiresallowFromto include"*")disabled
dmPolicy: "open" with allowFrom: ["*"] lets any Telegram account that finds or guesses the bot username command the bot. Use it only for intentionally public bots with tightly restricted tools; one-owner bots should use allowlist with numeric user IDs.channels.telegram.allowFrom accepts numeric Telegram user IDs. telegram: / tg: prefixes are accepted and normalized.
In multi-account configs, a restrictive top-level channels.telegram.allowFrom is a safety boundary: an account-level allowFrom: ["*"] does not make that account public unless the merged effective allowlist still contains an explicit wildcard.
dmPolicy: "allowlist" with empty allowFrom blocks all DMs and is rejected by config validation.
Setup asks for numeric user IDs only. If your config has @username allowlist entries from an older setup, run openclaw doctor --fix to resolve them to numeric IDs (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 for allowlist flows (for example when dmPolicy: "allowlist" has no explicit IDs yet).For one-owner bots, prefer dmPolicy: "allowlist" with explicit numeric allowFrom IDs over depending on previous pairing approvals.Common confusion: DM pairing approval does not mean “this sender is authorized everywhere.” Pairing grants DM access only. If no command owner exists yet, the first approved pairing also sets commands.ownerAllowFrom, giving owner-only commands and exec approvals an explicit operator account. Group sender authorization still comes from explicit config allowlists.
To be authorized for both DMs and group commands with one identity: put your numeric Telegram user ID in channels.telegram.allowFrom, and for owner-only commands make sure commands.ownerAllowFrom contains telegram:<your user id>.Finding your Telegram user ID
Safer (no third-party bot): DM your bot, runopenclaw logs --follow, read from.id.Official Bot API method:@userinfobot or @getidsbot.Runtime behavior
- Telegram runs inside 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, media placeholders, and persisted reply-chain context for replies the gateway has observed.
- Group sessions are isolated by group ID. Forum topics append
:topic:<threadId>. - DM messages can carry
message_thread_id; OpenClaw preserves it for replies. DM topic sessions split only when TelegramgetMereportshas_topics_enabled: truefor the bot; otherwise DMs stay on the flat session. - Long polling uses the grammY runner with per-chat/per-thread sequencing. Runner sink concurrency uses
agents.defaults.maxConcurrent. - Multi-account startup bounds concurrent
getMeprobes so large bot fleets do not fan out every account probe at once. - Each gateway process guards long polling so only one active poller can use a bot token at a time. Persistent
getUpdates409 conflicts point to another OpenClaw gateway, script, or external poller using the same token. - The polling watchdog restarts after 120 seconds without completed
getUpdatesliveness by default. Raisechannels.telegram.pollingStallThresholdMs(30000-600000, per-account overrides supported) only if your deployment sees false polling-stall restarts during long-running work. - Telegram Bot API has no read-receipt support (
sendReadReceiptsdoes not apply).
channels.telegram.dm.threadReplies and channels.telegram.direct.<chatId>.threadReplies were removed. Run openclaw doctor --fix after upgrading if your config still has those keys. DM topic routing now follows Telegram getMe.has_topics_enabled (controlled by BotFather threaded mode): topics-enabled bots use thread-scoped DM sessions when Telegram sends message_thread_id; other DMs stay on the flat session.Feature reference
Live stream preview (message edits)
Live stream preview (message edits)
editMessageText repeatedly, finalizing in place.channels.telegram.streamingisoff | partial | block | progress(default:partial)- short initial answer previews are debounced, then materialized after a bounded delay if the run is still active
progresskeeps one editable status draft for tool progress, shows the stable status label when answer activity arrives before tool progress, clears it at completion, and sends the final answer as a normal messagestreaming.preview.toolProgresscontrols whether tool/progress updates reuse the same edited preview message (default:truewhen preview streaming is active)streaming.preview.commandTextcontrols command/exec detail inside those lines:raw(default) orstatus(tool label only)streaming.progress.commentary(default:false) opts into assistant commentary/preamble text in the temporary progress draft- legacy
channels.telegram.streamMode, booleanstreamingvalues, and retired native draft preview keys are detected; runopenclaw doctor --fixto migrate them
v2026.4.22+).Keep answer-preview edits but hide tool-progress lines:progress mode shows tool progress without editing the final answer into that message. Put the command-text policy under streaming.progress:streaming.mode: "off" disables preview edits and suppresses generic tool/progress chatter instead of sending it as standalone status messages; approval prompts, media, and errors still route through normal final delivery. streaming.preview.toolProgress: false keeps only answer-preview edits.replyToMode is first, all, or batched and the inbound message has selected quote text, OpenClaw sends the final answer through Telegram’s native quote-reply path instead of editing the answer preview, so streaming.preview.toolProgress cannot show status lines that turn. Current-message replies without selected quote text still stream. Set replyToMode: "off" when tool-progress visibility matters more than native quote replies, or streaming.preview.toolProgress: false to accept that trade-off./reasoning stream streams reasoning into the live preview while generating, then deletes the reasoning preview after final delivery (use /reasoning on to keep it visible). The final answer is sent without reasoning text.Rich message formatting
Rich message formatting
$400-600K is not parsed as math. Long rich text splits automatically across Telegram’s limits. Tables over the 20-column limit fall back to a code block.Default: off, for client compatibility — some current Desktop, Web, Android, and third-party clients render accepted rich messages as unsupported. Keep this off unless every client used with the bot can render them. /status shows whether the current session has rich messages on or off.Link previews are on by default. channels.telegram.linkPreview: false disables automatic entity detection for rich text.Native commands and custom commands
Native commands and custom commands
setMyCommands. commands.native: "auto" enables native commands for Telegram.Add custom command menu entries:/, lowercase); valid pattern a-z, 0-9, _, length 1-32; custom commands cannot override native commands; conflicts/duplicates are skipped and logged.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 the Telegram menu. If native commands are disabled, built-ins are removed; custom/plugin commands may still register if configured.Common setup failures:setMyCommands failedwithBOT_COMMANDS_TOO_MUCHafter a trim retry means the menu still overflows; reduce plugin/skill/custom commands or disablechannels.telegram.commands.native.deleteWebhook,deleteMyCommands, orsetMyCommandsfailing with404: Not Foundwhile direct Bot API curl commands work usually meanschannels.telegram.apiRootwas set to the full/bot<TOKEN>endpoint.apiRootmust be the Bot API root only;openclaw doctor --fixremoves an accidental trailing/bot<TOKEN>.getMe returned 401means Telegram rejected the configured bot token. UpdatebotToken,tokenFile, orTELEGRAM_BOT_TOKEN(default account) with the current BotFather token; OpenClaw stops before polling so this is not reported as a webhook cleanup failure.setMyCommands failedwith network/fetch errors usually means outbound DNS/HTTPS toapi.telegram.orgis blocked.
Device pairing commands (device-pair plugin)
When installed:/pairgenerates a setup code- paste the code in the iOS app
/pair pendinglists pending requests (including role/scopes)- approve:
/pair approve <requestId>,/pair approve(only pending request), or/pair approve latest
requestId; re-run /pair pending before approving.More detail: Pairing.Telegram message actions for agents and automation
Telegram message actions for agents and automation
sendMessage(to,content, optionalmediaUrl,replyToMessageId,messageThreadId)react(chatId,messageId,emoji)deleteMessage(chatId,messageId)editMessage(chatId,messageId,contentorcaption, optionalpresentationinline buttons; button-only edits update reply markup)createForumTopic(chatId,name, optionaliconColor,iconCustomEmojiId)
send, react, delete, edit, sticker, sticker-search, topic-create.Gating: channels.telegram.actions.sendMessage, deleteMessage, reactions, sticker (default: disabled). edit, createForumTopic, and editForumTopic are enabled by default with no dedicated toggle.
Runtime sends use the active config/secrets snapshot from startup/reload, so action paths do not re-resolve SecretRef values per send.Reaction removal semantics: /tools/reactions.Forum topics and thread behavior
Forum topics and thread behavior
:topic:<threadId>; replies and typing target the topic thread; topic config path is channels.telegram.groups.<chatId>.topics.<threadId>.General topic (threadId=1) is a special case: message sends omit message_thread_id (Telegram rejects sendMessage(...thread_id=1) with “thread not found”), but typing actions still include message_thread_id (empirically required for the typing indicator to appear).Topic entries inherit group settings unless overridden (requireMention, allowFrom, skills, systemPrompt, enabled, groupPolicy). agentId is topic-only and does not inherit from group defaults. topics."*" sets defaults for every topic in that group; exact topic IDs still win over "*".Per-topic agent routing: each topic can route to a different agent via agentId in the topic config, giving it its own workspace, memory, and session:agent:zu:telegram:group:-1001234567890:topic:3.Persistent ACP topic binding: forum topics can pin ACP harness sessions through top-level typed bindings (bindings[] with type: "acp", match.channel: "telegram", peer.kind: "group", and a topic-qualified id like -1001234567890:topic:42). Currently scoped to forum topics in groups/supergroups. See ACP Agents.Thread-bound ACP spawn from chat: /acp spawn <agent> --thread here|auto binds the current topic to a new ACP session; follow-ups route there directly, and OpenClaw pins the spawn confirmation in-topic. Requires channels.telegram.threadBindings.spawnSessions (default: true).Template context exposes MessageThreadId and IsForum. DM chats with message_thread_id keep reply metadata but only use thread-aware session keys when Telegram getMe reports has_topics_enabled: true.
The retired dm.threadReplies and direct.*.threadReplies overrides are gone; BotFather threaded mode is the single source of truth. Run openclaw doctor --fix to remove stale config keys.Audio, video, and stickers
Audio, video, and stickers
Audio messages
Telegram distinguishes voice notes from audio files. Default: audio-file behavior; tag[[audio_as_voice]] in the agent reply to force a voice-note send. Inbound voice-note transcripts are framed as machine-generated, untrusted text in agent context, but mention detection still uses the raw transcript so mention-gated voice messages keep working.Video messages
Telegram distinguishes video files from video notes. Video notes do not support captions; provided message text sends separately.Stickers
Inbound: static WEBP is downloaded and processed (placeholder<media:sticker>); animated TGS and video WEBM are skipped.Sticker context fields: Sticker.emoji, Sticker.setName, Sticker.fileId, Sticker.fileUniqueId, Sticker.cachedDescription. Descriptions are cached in OpenClaw SQLite plugin state to reduce repeated vision calls.Enable sticker actions:Reaction notifications
Reaction notifications
message_reaction updates, separate from message payloads. When enabled, OpenClaw enqueues system events like Telegram reaction added: 👍 by Alice (@alice) on msg 42.channels.telegram.reactionNotifications:off | own | all(default:own)channels.telegram.reactionLevel:off | ack | minimal | extensive(default:minimal)
own means user reactions to bot-sent messages only (best-effort via a 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 the group chat session; forum groups route to the general-topic session (:topic:1), not the exact originating topic.allowed_updates for polling/webhook include message_reaction automatically.Ack reactions
Ack reactions
ackReaction sends an acknowledgement emoji while OpenClaw processes an inbound message. messages.ackReactionScope decides when it is sent.Emoji resolution order:channels.telegram.accounts.<accountId>.ackReactionchannels.telegram.ackReactionmessages.ackReaction- agent identity emoji fallback (
agents.list[].identity.emoji, else ”👀”)
"" to disable the reaction for a channel or account.Scope (messages.ackReactionScope, default "group-mentions"; no Telegram-account or Telegram-channel override today):all (DMs + groups, including ambient room events), direct (DMs only), group-all (every group message except ambient room events, no DMs), group-mentions (groups when the bot is mentioned; no DMs — default), off / none (disabled).group-mentions) does not fire ack reactions in DMs or ambient room events. Use direct or all for DMs; only all acknowledges ambient room events. This value is read at Telegram provider startup, so a gateway restart is needed for the change to take effect.Config writes from Telegram events and commands
Config writes from Telegram events and commands
configWrites !== false). Telegram-triggered writes include group migration events (migrate_to_chat_id, updates channels.telegram.groups) and /config set / /config unset (requires command enablement).Disable:Long polling vs webhook
Long polling vs webhook
channels.telegram.webhookUrl and channels.telegram.webhookSecret; optional webhookPath (default /telegram-webhook), webhookHost (default 127.0.0.1), webhookPort (default 8787), webhookCertPath (self-signed cert PEM for direct-IP or no-domain setups).In long-polling mode, OpenClaw persists its restart watermark only after an update dispatches successfully; a failed handler leaves that update retryable in the same process instead of marking it completed.The local listener binds to 127.0.0.1:8787 by default. For public ingress, put a reverse proxy in front of the local port, or set webhookHost: "0.0.0.0" intentionally.Webhook mode validates request guards, the Telegram secret token, and the JSON body before returning 200. OpenClaw then processes the update asynchronously through the same per-chat/per-topic bot lanes used by long polling, so slow agent turns do not hold Telegram’s delivery ACK.Limits, retry, and CLI targets
Limits, retry, and CLI targets
channels.telegram.textChunkLimitdefault 4000;chunkMode="newline"prefers paragraph boundaries (blank lines) before length splitting.channels.telegram.mediaMaxMb(default 100) caps inbound and outbound media size.channels.telegram.mediaGroupFlushMs(default 500, range 10-60000) controls how long albums/media groups are buffered before OpenClaw dispatches them as one inbound message. Increase it if album parts arrive late; decrease it to reduce album reply latency.channels.telegram.timeoutSecondsoverrides the API client timeout (grammY default applies if unset). Bot clients clamp configured values below the 60-second outbound text/typing request guard so grammY does not abort visible reply delivery before OpenClaw’s transport guard and fallback can run. Long polling still uses a 45-secondgetUpdatesrequest guard so idle polls are not abandoned indefinitely.channels.telegram.pollingStallThresholdMsdefaults to 120000; tune between 30000 and 600000 only for false-positive polling-stall restarts.- group context history uses
channels.telegram.historyLimitormessages.groupChat.historyLimit(default 50);0disables. - reply/quote/forward supplemental context normalizes into one selected conversation context window when the gateway has observed the parent messages; the observed-message cache lives in OpenClaw SQLite plugin state, and
openclaw doctor --fiximports legacy sidecars. Telegram only includes one shallowreply_to_messageper update, so chains older than the cache are limited to that payload. - Telegram allowlists primarily gate who can trigger the agent, not a full supplemental-context redaction boundary.
- DM history:
channels.telegram.dmHistoryLimit,channels.telegram.dms["<user_id>"].historyLimit. channels.telegram.retryapplies to Telegram send helpers (CLI/tools/actions) for recoverable outbound API errors. Inbound final-reply delivery uses a bounded safe-send retry for pre-connect failures, but does not retry ambiguous post-send network envelopes that could duplicate visible messages.
openclaw message poll and support forum topics:--poll-duration-seconds (5-600), --poll-anonymous, --poll-public, --thread-id (or a :topic: target). --poll-option repeats 2-12 times (Telegram’s option cap).Telegram send also supports --presentation with buttons blocks for inline keyboards (when channels.telegram.capabilities.inlineButtons allows it), --pin or --delivery '{"pin":true}' to request pinned delivery when the bot can pin in that chat, and --force-document to send outbound images, GIFs, and videos as documents instead of compressed/animated/video uploads.Action gating: channels.telegram.actions.sendMessage=false disables all outbound messages including polls; channels.telegram.actions.poll=false disables poll creation while leaving regular sends enabled.Exec approvals in Telegram
Exec approvals in Telegram
channels.telegram.execApprovals.enabled("auto"enables when at least one approver is resolvable)channels.telegram.execApprovals.approvers(falls back to numeric owner IDs fromcommands.ownerAllowFrom)channels.telegram.execApprovals.target:dm(default) |channel|bothagentFilter,sessionFilter
channels.telegram.allowFrom, groupAllowFrom, and defaultTo control who can talk to the bot and where it sends normal replies — they do not make someone an exec approver. The first approved DM pairing bootstraps commands.ownerAllowFrom when no command owner exists yet, so one-owner setups work without duplicating IDs under execApprovals.approvers.Channel delivery shows the command text in the chat; only enable channel or both in trusted groups/topics. When the prompt lands in a forum topic, OpenClaw preserves the topic for the approval prompt and follow-up. Exec approvals expire after 30 minutes by default.Inline approval buttons also require channels.telegram.capabilities.inlineButtons to allow the target surface (dm, group, or all). Approval IDs prefixed with plugin: resolve through plugin approvals; others resolve through exec approvals first.See Exec approvals.Error reply controls
When the agent hits a delivery or provider error, the error policy controls whether error messages reach the Telegram chat:| Key | Values | Default | Description |
|---|---|---|---|
channels.telegram.errorPolicy | always, once, silent | always | always sends every error message to the chat. once sends each unique error message once per cooldown window (suppresses repeated identical errors). silent never sends error messages to the chat. |
channels.telegram.errorCooldownMs | number (ms) | 14400000 (4h) | Cooldown window for the once policy. After an error is sent, the same message is suppressed until this interval elapses. Prevents error spam during outages. |
Troubleshooting
Bot does not respond to non mention group messages
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 the bot to the group. openclaw channels statuswarns when config expects unmentioned group messages.openclaw channels status --probechecks explicit numeric group IDs; wildcard"*"cannot be membership-probed.- Quick session test:
/activation always.
Bot not seeing group messages at all
Bot not seeing group messages at all
- When
channels.telegram.groupsexists, the group must be listed (or include"*"). - Verify bot membership in the group.
- Review
openclaw logs --followfor skip reasons.
Commands work partially or not at all
Commands work partially or not at all
- Authorize your sender identity (pairing and/or numeric
allowFrom); command authorization still applies even when group policy isopen. setMyCommands failedwithBOT_COMMANDS_TOO_MUCHmeans the native menu has too many entries; reduce plugin/skill/custom commands or disable native menus.deleteMyCommands/setMyCommandsstartup calls andsendChatActiontyping calls are bounded and retry once through Telegram’s transport fallback on request timeout. Persistent network/fetch errors usually mean DNS/HTTPS toapi.telegram.orgis unreachable.
Polling or network instability
Polling or network instability
- Node 22+ with a custom fetch/proxy can trigger immediate abort behavior if
AbortSignaltypes mismatch. - Some hosts resolve
api.telegram.orgto IPv6 first; broken IPv6 egress causes intermittent API failures. - Logs with
TypeError: fetch failedorNetwork request for 'getUpdates' failed!are retried as recoverable network errors. - During polling startup, OpenClaw reuses the successful startup
getMeprobe for grammY so the runner does not need a secondgetMebefore the firstgetUpdates. - If
deleteWebhookfails with a transient network error during polling startup, OpenClaw continues into long polling instead of making another pre-poll control-plane call. A still-active webhook then surfaces as agetUpdatesconflict; OpenClaw rebuilds the transport and retries webhook cleanup. - If Telegram sockets recycle on a short fixed cadence, check for a low
channels.telegram.timeoutSeconds— bot clients clamp configured values below the outbound andgetUpdatesrequest guards, but older releases could abort every poll or reply when this was set below those guards. Polling stall detectedin logs means OpenClaw restarts polling and rebuilds the transport after 120 seconds without completed long-poll liveness by default.openclaw channels status --probeandopenclaw doctorwarn when a running polling account has not completedgetUpdatesafter startup grace, a running webhook account has not completedsetWebhookafter startup grace, or the last successful polling transport activity is stale.- Raise
channels.telegram.pollingStallThresholdMsonly when long-runninggetUpdatescalls are healthy but your host still reports false polling-stall restarts. Persistent stalls usually point to proxy, DNS, IPv6, or TLS egress issues toapi.telegram.org. - Telegram honors process proxy env for Bot API transport:
HTTP_PROXY,HTTPS_PROXY,ALL_PROXY, and lowercase variants.NO_PROXY/no_proxycan still bypassapi.telegram.org. - If
OPENCLAW_PROXY_URLis set for a service environment and no standard proxy env is present, Telegram uses that URL for Bot API transport too. - On VPS hosts with unstable direct egress/TLS, route Telegram API calls through a proxy:
- Node 22+ defaults to
autoSelectFamily=true(except WSL2). Telegram DNS result order honorsOPENCLAW_TELEGRAM_DNS_RESULT_ORDER, thenchannels.telegram.network.dnsResultOrder, then the process default (for exampleNODE_OPTIONS=--dns-result-order=ipv4first), falling back toipv4firston Node 22+ if none applies. - On WSL2, or when IPv4-only behavior works better, force family selection:
- RFC 2544 benchmark-range answers (
198.18.0.0/15) are already allowed for Telegram media downloads by default. If a trusted fake-IP or transparent proxy rewritesapi.telegram.orgto some other private/internal/special-use address during media downloads, opt in to the Telegram-only bypass:
- The same opt-in is available per account at
channels.telegram.accounts.<accountId>.network.dangerouslyAllowPrivateNetwork. - If your proxy resolves Telegram media hosts into
198.18.x.x, leave the dangerous flag off first — that range is already allowed by default.
- Temporary environment overrides:
OPENCLAW_TELEGRAM_DISABLE_AUTO_SELECT_FAMILY=1,OPENCLAW_TELEGRAM_ENABLE_AUTO_SELECT_FAMILY=1,OPENCLAW_TELEGRAM_DNS_RESULT_ORDER=ipv4first. - Validate DNS answers:
Configuration reference
Primary reference: Configuration reference - Telegram.High-signal Telegram fields
High-signal Telegram fields
- startup/auth:
enabled,botToken,tokenFile(must be a regular file; symlinks are rejected),accounts.* - access control:
dmPolicy,allowFrom,groupPolicy,groupAllowFrom,groups,groups.*.topics.*, top-levelbindings[](type: "acp") - topic defaults:
groups.<chatId>.topics."*"applies to unmatched forum topics; exact topic IDs override it - exec approvals:
execApprovals,accounts.*.execApprovals - command/menu:
commands.native,commands.nativeSkills,customCommands - threading/replies:
replyToMode,threadBindings - streaming:
streaming(modesoff | partial | block | progress),streaming.preview.toolProgress - formatting/delivery:
textChunkLimit,chunkMode,richMessages,markdown.tables(off | bullets | code | block),linkPreview,responsePrefix - media/network:
mediaMaxMb,mediaGroupFlushMs,timeoutSeconds,pollingStallThresholdMs,retry,network.autoSelectFamily,network.dangerouslyAllowPrivateNetwork,proxy - custom API root:
apiRoot(Bot API root only; do not include/bot<TOKEN>),trustedLocalFileRoots(self-hosted Bot API absolutefile_pathroots) - webhook:
webhookUrl,webhookSecret,webhookPath,webhookHost,webhookPort,webhookCertPath - actions/capabilities:
capabilities.inlineButtons,actions.sendMessage|editMessage|deleteMessage|reactions|sticker|createForumTopic|editForumTopic - reactions:
reactionNotifications,reactionLevel - errors:
errorPolicy,errorCooldownMs,silentErrorReplies - writes/history:
configWrites,historyLimit,dmHistoryLimit,dms.*.historyLimit
channels.telegram.defaultAccount (or include channels.telegram.accounts.default) to make default routing explicit. Otherwise OpenClaw falls back to the first normalized account ID and openclaw doctor warns. Named accounts inherit channels.telegram.allowFrom / groupAllowFrom, but not accounts.default.* values.