MINARA

Telegram

Recommended starting point — three-minute setup, streaming-capable gateway, most battle-tested provider.

🟢 Runtime-ready — the gateway ships with streaming-edit capability (no production caller wires it yet — see below) and is the provider most Minara operators start with. No external binary, no business-account approval, just a bot token and a chat id.

What you get

  • Streaming edits (gateway-ready)TelegramGateway posts a placeholder and edits it as text arrives, throttled at 750 ms. Drive it via createStreamSink in apps/agent/src/messaging/stream-helpers.ts; the send_message tool itself is one-shot.
  • Rich text (default on) — markdown replies render as Telegram formatting (bold, headings, tables, task lists, code blocks). Set TELEGRAM_RICH_TEXT=false to send plain text instead.
  • Attachments — images (sendPhoto), files (sendDocument), voice notes (sendVoice, OGG/Opus required), audio (sendAudio). Source must be a file the agent generated under the sandbox (see overview page for details).
  • Group and 1:1 chats — same flow for both; group chat ids are negative numbers.
  • 4 096-char limit per message. send_message rejects longer text at the tool boundary; the stream sink truncates mid-stream with a … (truncated) marker.

Setup

1. Create a bot

  1. Open Telegram, find @BotFather
  2. Send /newbot → follow the prompts (name, then username)
  3. Save the token BotFather gives you (looks like 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11)

2. Get your chat id

  1. Start a chat with your new bot and send any message to it — Telegram drops bot → chat messages if the chat hasn't initiated contact first
  2. Visit https://api.telegram.org/bot<TOKEN>/getUpdates in a browser and find "chat":{"id":...} in the response

For a group: add the bot to the group, send a message in the group, then check getUpdates. Group ids are negative (e.g. -1001234567890).

3. Configure Minara

Easy way — ask the agent in chat:

"set up Telegram notifications"

Minara prompts for the token + chat id, writes them to ~/.minara/credentials.json (messaging slot), and runs a test ping.

Manual way:

minara auth messaging add telegram

Or set the env vars directly in your project .env file:

TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
TELEGRAM_CHAT_ID=-1001234567890

4. Test

minara auth messaging test telegram

You should see the ping message within a second or two.

Streaming behaviour

Telegram's editMessageText endpoint accepts up to ~30 edits per message without hitting rate limits. The TelegramGateway exposes a startStream() session that posts a placeholder and edits it as new text arrives; createStreamSink throttles those edits at 750 ms — fast enough to feel live, slow enough to stay well under the per-chat limit. Cumulative text exceeding 4 096 characters is truncated mid-stream with a … (truncated) marker.

Throttle and length overrides are caller-side options on createStreamSink(gw, msg, { intervalMs, maxLength }), not arguments on send_message. The tool path itself is one-shot — the LLM sends a fully-formed message; streaming lives in workflow / autopilot code (not yet wired in production).

Rich text

Replies render as Telegram rich text by default. The agent writes markdown; the gateway converts it to Telegram's HTML subset before sending:

  • Headings become bold, lists keep their bullets, task lists show ☐ / ☑, tables render as aligned monospace, and code blocks keep their language label.
  • If Telegram rejects the HTML (rare), the gateway retries the same content as MarkdownV2, then as plain text. A message is never dropped because of a formatting error.
  • During streaming, each edit only shows fully-closed formatting, so a half-typed **bold never flashes before it resolves.

Turn it off to send the agent's text verbatim:

TELEGRAM_RICH_TEXT=false

Accepted "off" values are 0, false, no, off; unset means on. The setting is read once at startup, so restart the gateway after changing it. You can also toggle it in the web UI under Settings → Messaging behaviour → Rich text on Telegram.

Attachments

Attachments reference files the agent has already generated inside the sandbox (via image_generate, audio_generate, write_file, code execution, etc.). The LLM passes the sandbox-relative path:

send_message({
  provider: "telegram",
  text: "BTC/USD daily — key levels marked",
  attachments: [
    { kind: "image", sandbox_path: "images/btc-2026-04-19.png" },
  ],
})

Kind routing:

kindTelegram endpointNotes
imagesendPhotocaption supported
filesendDocumentarbitrary filetypes
voicesendVoicerequires OGG/Opus; non-OGG is rejected
audiosendAudiomp3 / m4a / flac — music-style player UI

Multiple attachments go as sequential messages; the first carries msg.text as its caption if short enough (≤ 1024 chars), otherwise the text is sent as its own leading message and attachments follow threaded under it. See apps/agent/src/messaging/telegram.ts for details.

Overriding the channel

Route urgent alerts to a different chat while keeping routine notifications on the default:

send_message({
  provider: "telegram",
  channel: "-1009876543210",
  text: "Critical: position liquidation imminent",
})

Troubleshooting

"Test message didn't arrive"

  • Did you message the bot first? Telegram drops messages to chats that haven't initiated contact
  • Check the sign on TELEGRAM_CHAT_ID — group chats are negative
  • Confirm the bot is still in the group: BotFather → your bot → Bot SettingsGroup Privacy

"Streaming feels slow / jumpy"

  • Expected: 750 ms baseline throttle inside TelegramGateway. Caller code can override via createStreamSink(gw, msg, { intervalMs: 500 }) — this is a caller-side option, not a send_message tool argument
  • Very long responses get truncated at 4 096 characters — consider splitting the workflow into multiple messages

"Chat not found (400)"

  • The bot was removed from the group, or the chat id is wrong
  • Re-run the getUpdates step after sending a fresh message in the target chat

Reference

On this page