Skip to main content

Telegram channel — developer reference

This page covers what plugin authors need when writing plugins that interact with the telegram 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)

Filtering to a specific account

Session keys

Telegram session keys follow these patterns: Use ctx.sessionKey in hook handlers — do not construct keys manually.

Message actions

Telegram supports richer message actions than most channels. Available actions: Gating via config:
  • channels.telegram.actions.reactions — default true
  • channels.telegram.actions.sendMessage — default true
  • channels.telegram.actions.deleteMessage — default true
  • channels.telegram.actions.sticker — default false (must be enabled explicitly)

Inline buttons

Inline keyboards can be included in outbound messages when capabilities.inlineButtons scope allows:
Callback button clicks are delivered back to the agent as text: 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: outbound sendMessage omits message_thread_id because Telegram rejects it for threadId=1
  • Topic config inherits from the parent group unless overridden (requireMention, allowFrom, skills, etc.)
Check ctx.sessionKey to determine if a message originated from a forum topic:

Reaction notifications as hook events

When reactionNotifications is enabled, Telegram reaction events are enqueued as system messages like:
These arrive as message_received events. Filter them by inspecting event.body if your hook should not act on reaction notifications.

Streaming behavior

Telegram uses native sendMessageDraft (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
For complex replies (media payloads), streaming falls back to normal final delivery and cleans up the preview message. Plugin hooks on message_sending and message_sent fire once at final delivery, not for each streaming update.

Sender ID format

Telegram sender IDs in ctx.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

Capture api.pluginConfig at registration time — do not access it inside execute():

Async safety

Do not use synchronous blocking I/O in register() or hook handlers: