Skip to content

Ask the docs

Answers come only from these docs and are written by an AI model (Gemini), so check the linked pages. Your question is not kept. Do not include personal details or keys.

Telegram

Telegram · Telegram bot API for AI agents · Telegram AI agent · Telegram LLM bot · connect ChatGPT to Telegram · Telegram bot with OpenAI · Telegram chatbot API

Try the sandbox bot, then connect your own Telegram sender.

Try your agent on the Telegram sandbox, then connect your own bot without changing its message handler.

TypeScript
// npm install @flow-engineer/messaging openai
import OpenAI from "openai";
import { FlowMessaging, contentText } from "@flow-engineer/messaging";
const flow = new FlowMessaging();
const openai = new OpenAI();
for await (const event of flow.events.stream({ types: ["message.received"] })) {
await event.conversation.reply(
openai.chat.completions.create({
model: "gpt-4.1-mini",
stream: true,
messages: [{ role: "user", content: contentText(event.data.message.content) }],
}),
);
}
  1. Get a test key, with no account: curl -X POST https://api.flow.engineer/v0/sandbox/keys (or npx @flow-engineer/messaging init). Set its key as FLOW_MESSAGING_KEY. It allows 1 contact and 50 messages for 7 days; sign in to get more (Keys and sign-in).
  2. GET /v0/senders?channel=telegram lists the sandbox bot. Its address.link is https://t.me/<bot>?start=<code>, with your join code in it. (npx @flow-engineer/messaging init prints it too.)
  3. Open the link and tap Start. That’s it, you’ve joined: your app gets conversation.started, then /start as your first message.received.
  4. Run the code above and send the bot a message.

If the link can’t be used (for example you found the bot by searching for it), send the bot the sender’s join_code instead, as it is, for example Hi Sandbox app (ref wild-otter-04508705).

Telegram is the fastest channel to go live on: bring a bot token and it is connected at once.

  1. In Telegram, open @BotFather, send /newbot, and copy the token.
  2. Put the token in an environment variable on your server or in your terminal, and connect it with a live key (FLOW_MESSAGING_KEY=fk_live_...):
curl
export TELEGRAM_BOT_TOKEN=... # the token from BotFather; never commit it
curl https://api.flow.engineer/v0/senders \
-H "Authorization: Bearer $FLOW_MESSAGING_KEY" \
-H "Content-Type: application/json" \
-d "{\"channel\": \"telegram\", \"telegram_bot_token\": \"$TELEGRAM_BOT_TOKEN\"}"

Flow checks the token, keeps it encrypted, points the bot’s webhook at Flow and answers with the sender active. The token is never returned. With FLOW_MESSAGING_KEY set to the live key, your agent now answers on your bot.

To disconnect the bot, call DELETE /v0/senders/{sender_id} with your live key: Flow removes the bot’s webhook, deletes its token and retires the sender (banned).

If Telegram rejects the token (you revoked it in @BotFather), the sender turns flagged: new sends answer 403 permission and queued messages are held. Send the new token the same way (POST /v0/senders) and the same sender becomes active again and sends them. A held message waits at most 72 hours after Flow accepted it; after that it fails with outside_window (channel_code queued_too_long) in message.failed instead of going out late.

A buttons message shows as an inline keyboard (1 to 10 buttons). Taps arrive as button_reply with the button’s id. URL buttons open a link and send nothing back. Telegram has no list or carousel: those answer 422 unsupported_content, and the hint says what to send instead (for a carousel, a basic message, then one buttons message with an image header per card).

TypeScript
import { buttons } from "@flow-engineer/messaging";
await event.conversation.send(buttons("Pick a size", ["Small", "Medium", "Large"]));

Telegram lets bots edit their messages at any time and unsend them within 48 hours of sending:

TypeScript
const [msg] = await event.conversation.reply("Checking stock...");
await flow.messages.edit(msg.id, "In stock: 12 left.");
await flow.messages.unsend(msg.id);

An unsend more than 48 hours after sending is refused at once with 422 unsupported_content and channel_code unsend_window_closed; send a new message instead. A 200 means accepted: wait for message.sent (or message.failed) for the unsend. See Edit and unsend limits.

KindTelegram
Text (basic)yes, sent as written; style it with Telegram’s own HTML or MarkdownV2 in telegram.parse_mode
Photos, video, audio, voice notesyes
Documentsyes, sent (up to 50 MB) and received; malware-scanned before Flow keeps them (an upload that fails answers 422 file_blocked)
Buttonsinline keyboard, up to 10; no lists or carousels
Reactions, typingyes; reactions only with Telegram’s reaction emoji
Read receiptsno: bots cannot send them (422 unsupported_content, param kind)
Edit, unsendyes: edits with no time limit, unsends within 48 hours (otherwise 422 unsupported_content, unsend_window_closed)
Starting a conversationonly with people who started your bot (to.telegram_user_id)
  • People must start your bot first. Telegram does not let a bot message someone who has never pressed Start.
  • Commands are messages. /start and other commands arrive as basic messages; your agent decides what to answer.
  • Contacts have no phone number. A Telegram contact is identified by address.telegram_user_id and maybe address.username.