Telegram channel — developer reference
This page covers what plugin authors need when writing plugins that interact with thetelegram channel: channel identifier, hook filtering, available message actions (including Telegram-specific actions), forum topic behavior, and gotchas to handle in plugin code.
For building a new channel adapter from scratch, see Channel adapters. For the admin configuration reference, see Admin: Telegram.
Channel identifier
The Telegram channel registers under the id"telegram". Use this to filter hooks:
ctx.channelId is always "telegram" for messages, hooks, and tool calls originating from the Telegram channel.
Available hooks (Telegram-relevant)
| Hook | Fires when | Notes |
|---|---|---|
message_received | Inbound Telegram message accepted | After allowlist/policy checks pass |
before_tool_call | Agent is about to call a tool | Filter on ctx.channelId === "telegram" |
after_tool_call | Tool call returned | |
message_sending | WednesdayAI is about to send a Telegram reply | Can modify content or cancel |
message_sent | Reply delivered to Telegram | |
session_start | New Telegram session created | |
before_agent_start | Agent loop begins for a Telegram message |
Filtering to a specific account
Session keys
Telegram session keys follow these patterns:| Chat type | Session key pattern |
|---|---|
| DM | agent:<agentId>:telegram:dm:<userId> |
| Group | agent:<agentId>:telegram:group:<chatId> |
| Forum topic | agent:<agentId>:telegram:group:<chatId>:topic:<threadId> |
| DM with thread | agent:<agentId>:telegram:dm:<userId>:thread:<threadId> |
ctx.sessionKey in hook handlers — do not construct keys manually.
Message actions
Telegram supports richer message actions than most channels. Available actions:| Action | Method | Description |
|---|---|---|
send | sendMessage | Send text, media, or inline buttons |
react | react | Send a reaction emoji to a message |
delete | deleteMessage | Delete a message |
edit | editMessage | Edit a previously sent message |
sticker | (sticker) | Send a sticker by fileId |
sticker-search | (sticker-search) | Search cached stickers by query |
topic-create | createForumTopic | Create a forum topic in a supergroup |
channels.telegram.actions.reactions— defaulttruechannels.telegram.actions.sendMessage— defaulttruechannels.telegram.actions.deleteMessage— defaulttruechannels.telegram.actions.sticker— defaultfalse(must be enabled explicitly)
Inline buttons
Inline keyboards can be included in outbound messages whencapabilities.inlineButtons scope allows:
callback_data: <value>.
Forum topics
Forum supergroup topics are first-class in Telegram. Key behaviors for plugin authors:- Topic session key suffix:
:topic:<threadId> - Replies and typing indicators target the topic thread
- The general topic (
threadId=1) is special: outboundsendMessageomitsmessage_thread_idbecause Telegram rejects it forthreadId=1 - Topic config inherits from the parent group unless overridden (
requireMention,allowFrom,skills, etc.)
ctx.sessionKey to determine if a message originated from a forum topic:
Reaction notifications as hook events
WhenreactionNotifications is enabled, Telegram reaction events are enqueued as system messages like:
message_received events. Filter them by inspecting event.body if your hook should not act on reaction notifications.
Streaming behavior
Telegram uses nativesendMessageDraft (Bot API 9.5+, March 2026) in DMs and preview message + edits in groups when streaming: "partial" (default). This means:
- In DMs: the message is updated in-place as tokens arrive (no second message)
- In groups: a preview message is sent, then edited in-place; no second message is sent
message_sending and message_sent fire once at final delivery, not for each streaming update.
Sender ID format
Telegram sender IDs inctx.senderId are numeric strings: "123456789". The telegram: / tg: prefix is stripped internally before the ID reaches hook context. Do not add prefixes when comparing ctx.senderId to known IDs.
Plugin config access pattern
Captureapi.pluginConfig at registration time — do not access it inside execute():
Async safety
Do not use synchronous blocking I/O inregister() or hook handlers:
Related
- Channel adapters — building a new channel from scratch
- Hooks — full hook reference
- Agent tools — registering plugin tools
- Admin: Telegram — configuration reference