4.0 KiB
Telegram Channel
Connect a Telegram bot to an agent so the agent can read incoming messages and
reply. Telegram is the only channel implemented today; the machinery lives in
backend/src/app_mixins/channel.py (logic) and backend/src/server/telegram.py
(webhook route).
An agent that owns a channel/telegram resource is the agent that answers.
You can bind the same bot token to several agents, and one agent can be bound to
several bots.
1. Create a bot
Talk to @BotFather, create a bot, and copy its token
(looks like 123456:ABC-DEF...). Treat the token as a secret.
2. Choose a delivery mode
Telegram can push updates to a webhook, or the hub can pull them with long
polling. The choice is driven entirely by the HUB_HOST environment variable:
- Webhook — set
HUB_HOSTto a publicly reachable host. Telegram requires HTTPS on ports 443, 80, 88 or 8443, so the hub must be reachable there. - Long polling — leave
HUB_HOSTunset. The hub prints a warning and pollsgetUpdatesinstead. No public endpoint or TLS certificate is needed.
The POST /channel/telegram/<agent_id> route is always served regardless of
mode; in polling mode it simply never receives traffic.
Webhooks and polling are mutually exclusive. A webhook is never registered when
HUB_HOST is unset, and the poller clears any existing webhook (deleteWebhook)
before it starts.
3. Bind the bot to an agent
Add a resource to the responding agent (via the resources UI or the
save-agent-resource action):
kind = "channel"
key = "telegram"
value = {"token": "<bot token>", "secret": "<random webhook secret>", "name": "<display>"}
token— the BotFather token. Required.secret— a random string ofA-Z a-z 0-9 _ -, 1–256 characters. Optional, and only meaningful in webhook mode. When present, Telegram sends it back on every request in theX-Telegram-Bot-Api-Secret-Tokenheader and the route rejects mismatches with403.name— a display label. Optional.
This is the only state the channel persists. Conversations are not stored —
no Chat, Thread or Message rows are created. The rolling transcript is
held in memory on the hub and discarded when the process exits.
4. Restart the hub
On startup the hub queries all channel/telegram resources and reconciles the
provider:
- Webhook mode: it calls
getWebhookInfofor each binding and, when the registered URL does not matchhttps://<HUB_HOST>/channel/telegram/<agent_id>, callssetWebhookwith that URL, thesecret_token, andallowed_updates=["message"]. Verify withgetWebhookInfo. - Polling mode: it starts one background task per binding. Confirm the bot is
reachable with
getMe, then send a test message.
5. Talk to the bot
Message the bot. On each text message the hub:
- Appends the message to the conversation's in-memory transcript.
- Runs the agent with a flattened input — an instruction, a status message (channel, chat id/title/type, current time, sender), and the rendered conversation.
- The agent replies by calling the
send_messagetool, which issues asendMessagecall to Telegram.
Non-text updates (edited messages, media, callbacks) are ignored. Messages authored by other bots are ignored.
6. Troubleshooting
| Symptom | Cause |
|---|---|
| Webhook not set | HUB_HOST unset, or the host is not reachable over HTTPS. Check the startup log. |
| Polling active when you expected a webhook | HUB_HOST is unset. |
403 from the webhook route |
X-Telegram-Bot-Api-Secret-Token does not match the resource's secret. |
404 from the webhook route |
No channel/telegram resource on that agent. |
| No reply | The agent has no model/tool configuration, or the run errored — check the hub log for [channel] lines. |
Conflict: can't use getUpdates method while webhook is active |
A stale webhook from a previous deployment. Remove it (deleteWebhook) or set HUB_HOST so webhook mode takes over. |