Connect a Telegram bot and one of your agents will reply to direct messages and group chats. The agent uses every tool, skill, and memory it would in the web UI.
What you need
- A bot created with @BotFather on Telegram
- The bot's API token (issued by BotFather, looks like
123456:ABC-DEF...) - A publicly reachable URL for the engine. See
server.external_url. For local development, ngrok works.
Creating the bot
- Open a chat with @BotFather.
- Send
/newbotand follow the prompts to set the name and username. - Copy the API token from the confirmation message.
- (Optional) Send
/setprivacyand disable privacy mode if you want the bot to read every message in groups, not just ones that mention it.
Connecting the channel
- In Frona, open Settings → Channels and click Add channel.
- Pick Telegram Bot.
- Choose the space the conversation will live in and the agent that will respond.
- Paste the bot token into bot_token.
- Save.
Frona registers a webhook with Telegram pointing at your engine, and the channel transitions to Connected. Send the bot a message from your Telegram account and the conversation will appear in the chosen space.
Configuration fields
| Field | Required | Description |
|---|---|---|
bot_token | Yes | The token issued by BotFather. Stored encrypted at rest. |
How messages flow
- Direct messages to the bot land in a chat tagged as
direct. - Group messages that the bot can see (mentions, replies, or any message if privacy mode is disabled) land in a chat tagged as
group. - Replies are sent back to the same Telegram chat. The adapter renders Markdown into Telegram's MarkdownV2 format when possible and falls back to plain text otherwise.
- Tool calls are streamed as separate Telegram messages so you can watch progress, then the final reply is sent as one bubble.
Allowlisting senders
By default only paired-address senders pass receive_message. For a personal bot this means just you. To open the bot up to specific contacts or groups, add a policy. For example, allowing one Telegram username:
cedar
permit(
principal == Policy::Agent::"assistant",
action == Policy::Action::"receive_message",
resource in Policy::Channel::"telegram"
) when { resource.sender.address == "@friend_username" };See Policies for more patterns (channel-wide allows, blocking spammers, quiet mode).
Tips
- One bot per agent. Telegram tokens cannot be shared across applications, and giving each agent its own bot keeps personalities clean.
- Disable privacy mode for group bots. Otherwise the bot only sees
@mentionsand replies to its own messages. - Use Markdown in agent prompts. The adapter renders standard Markdown (bold, italic, links, code blocks) into Telegram's format.
- Public URL is required. Telegram won't deliver webhooks to localhost. Use ngrok for local testing.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Channel marked Failed right after creation | Bad token, the token already in use elsewhere, or the engine's external URL isn't reachable from Telegram |
| Bot receives messages but you don't see them | The agent doesn't have receive_message permission for that sender. Check the agent's policies. |
| Bot doesn't reply in groups | Privacy mode is on. Disable it via /setprivacy in BotFather. |