Telegram Onboarding: Shared Test Bot to Your Own Bot
This guide walks you from an empty Telegram channel to a working, production-ready setup. It complements the Telegram channel page, which covers message shapes and capability detail — this guide is the ordered path you follow the first time, and re-run whenever you attach, rotate, or retire a bot. Telegram has no approval cycle and no brand registration. The only real gate is that a production bot is a BotFather bot that you own, and Orbit can only connect it via the token BotFather hands you. Until you have a token, the built-in test account lets you send real messages through Orbit’s shared bot so you can validate your integration today.1. The test-account loop — send real messages today
The test account routes through Devotel’s shared, already-connected bot. You never touch BotFather to use it. Three steps:Step 1: Start verification
https://t.me/<shared_bot>?start=<token>), and a QR-code value. Open the deep link (or scan the QR code) in Telegram on your device — the linked chat with the shared bot is what makes the channel test_active.
Step 2: Send a test message through the shared bot
test_active, the send goes out under the shared bot. Once you bring your own bot and the channel enters production mode, the same endpoint routes through your bot instead, so your integration code does not change between test and production.
Check where you are at any point with GET /api/v1/telegram/channel-state — it returns the channel mode plus the verified-chat list when test-linked:
200
Step 3: Deliver demo flows while still test-scope
Test scope is enough to prove a group-keyword flow end to end: message a group that your shared-bot link is in, confirm the inboundmessage.received hits your webhook, then confirm your keyword router fires. The demo pack (GET /api/v1/telegram/demo-content) ships sample group-keyword and cost-rollup flows so you can validate rendering and routing before you commit a token.
To reset the loop — for example, to point a different tester device at it later — call POST /api/v1/telegram/test/disconnect. Verified-chat links clear; the daily test quota carries over.
The test account is for evaluation, not production: sends go out under the shared bot’s branding, not yours, and the quota is deliberately small. Move to your own bot below for anything a customer touches.
2. Bring your own bot (paste-token onboarding)
The production path is a single call once BotFather hands you a token.- Talk to @BotFather in Telegram and create a bot. Save the token it returns — treat it like a bearer credential (see Token handling on the channel page).
- Connect the token to Orbit:
data confirms the connected bot and meta identifies the request:
200
bot_id (the numeric prefix before the colon) and enforces a single-tenant claim. Once an Orbit organization connects a bot, the same bot_id cannot be connected to a second organization until the first disconnects — a second attempt returns 409 TELEGRAM_BOT_ALREADY_CONNECTED:
409 Error
- Orbit registers the webhook subscription immediately: the bot is pointed at
https://api.orbit.devotel.io/api/v1/webhooks/inbound/telegram, and theX-Telegram-Bot-Api-Secret-Tokenvalue Telegram must present is rotated and stored in Orbit’s encrypted credential vault. Inbound delivery is live from that call.
/test/*, /connect-bot, and /disconnect-bot are write-class operations — they require the owner, admin, or developer role, not just a valid session.
3. Send with metadata — types, keyboards, and formatting
Sending goes through the unified Messaging surface (POST /api/v1/messages/telegram). The text body is the top-level body field; everything else — the send type, parse_mode, and keyboards — lives under metadata, whose values are all strings.
Supported type values: text (default), photo, video, document, location. A photo, video, or document send needs metadata.mediaUrl; a location send needs metadata.latitude and metadata.longitude.
Supply inline_keyboard and reply_keyboard as JSON-stringified button[][] arrays — one string, an outer array of rows, each row an array of button objects. parse_mode accepts HTML or MarkdownV2.
A full send with a two-row inline keyboard. Each language keeps the keyboard as a JSON string inside metadata; do not pass the button rows as a nested object.
202
force_reply and remove_keyboard use the same stringified-array convention when you want a forced text reply or a keyboard removed.
4. Inbound webhook — receive Telegram messages
A plain inbound message and an inline-keyboard press both arrive at your webhook asmessage.received on the telegram channel. For a button press, the button’s callback_data becomes the message body; there is no separate telegram.callback event to subscribe to.
X-Telegram-Bot-Api-Secret-Token to Orbit’s registered inbound URL. Orbit rotates the per-bot secret_token during connect and validates it before forwarding the normalized event to your receiver. Your receiver sees the forwarded message.received payload; it does not receive Telegram’s secret header. Never use a BotFather token as a webhook secret.
Example forwarded event:
5. Edit and delete are Orbit-side only
PATCH /messages/:id and DELETE /messages/:id operate on the Orbit message record — they never call Telegram’s editMessageText or deleteMessage:
PATCH: edits thebodyand/orscheduled_atof a message that is still scheduled (not yet dispatched). Once the message has been sent, the edit returns409 MESSAGE_NOT_EDITABLE— a delivered message is not re-editable.DELETE: soft-deletes the Orbit message record and syncs the removal across your dashboards.
6. Lifecycle — retire the bot
POST /api/v1/telegram/disconnect-bot soft-deletes the connected-bot row and revokes the webhook registration with Telegram (deleteWebhook). The conversation history survives — Orbit keys history by bot_id, not by the token — so if you rotate the token via BotFather and reconnect with the new value, your history is preserved.
7. Error reference
A failed send returns a JSON error envelope whoseerror.code is one of the values below.
Webhook security depends on the same
secret_token per bot that Orbit rotates on connect — inbound updates that do not present it are rejected, so a spoofed sender cannot inject traffic into your callbacks.
8. Group events via the telegram-group-events module
Community bots added to groups or supergroups produce a different event shape — member joins and leaves, message-delete floods, and group keyword flows land on the dedicated telegram-group-events webhook module rather than the plain inbound path. Keep your primary handler on message.received for the customer chat surface, and subscribe the group module only if your bot operates in groups — the high-churn event classes (member-count churn, message-delete storms) are classified and dropped there so standard routing stays clean.