Skip to main content

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

The response carries a linking token, a deep link (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

While the channel is 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.

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 inbound message.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.
  1. 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).
  2. Connect the token to Orbit:
  1. Orbit registers the webhook subscription immediately: the bot is pointed at https://api.orbit.devotel.io/api/v1/webhooks/inbound/telegram, and the header that Telegram must present (secret_token) is rotated and stored in Orbit’s encrypted credential vault. Inbound delivery is live from that call.
One bot, one tenant: Orbit identifies a bot by its 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. If that trips on you, see the ownership disputes recovery paths on the channel page. Access: /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:
force_reply and remove_keyboard use the same stringified-array convention when you want a forced text reply or a keyboard removed.

4. Callback ingestion — buttons land as normal inbound messages

An inline-keyboard press arrives in Telegram as a callback_query update. Orbit ingests each press as an inbound message: the button’s callback_data becomes the message body, and the event fans out on the standard message.received webhook — the same event as any other inbound Telegram message. There is no separate telegram.callback event type to subscribe to, so your one webhook handler covers both plain text and button presses.

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 the body and/or scheduled_at of a message that is still scheduled (not yet dispatched). Once the message has been sent, the edit returns 409 MESSAGE_NOT_EDITABLE — a delivered message is not re-editable.
  • DELETE: soft-deletes the Orbit message record and syncs the removal across your dashboards.
If you claim the message is edited or removed in Telegram itself, that is wrong — Orbit does not attempt it. For a delivered message the right recovery is a follow-up correction sent to the chat.

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 whose error.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.