Skip to main content

Telegram bot dispatch and demo-account model

Telegram on Orbit is dispatched through exactly one of two assets: your own BotFather bot connected by pasting a token, or the shared demo bot Orbit operates for evaluation. This page defines how a dispatch picks between the two, how inbound updates and delivery receipts find their way back to your organization, and why a channel Telegram does not charge for still writes cost rows. The per-feature how-to lives on the Telegram channel page; the cross-channel ladder vocabulary lives on the channel test-account model. This page owns the Telegram-specific concepts.

Dispatch: own bot or shared demo bot

Every Telegram send resolves one of two paths, decided by the same gate you meet as a 403:
  • Your own bot — connected once by pasting the BotFather token into POST /api/v1/telegram/connect-bot. All Bot API features are available, dispatched under your bot’s identity.
  • The shared demo bot — available while you evaluate. You link one chat against the shared bot with POST /api/v1/telegram/test/start-verify (a five-minute deep-link token, rendered in the dashboard as a link and QR), and POST /api/v1/telegram/test/send dispatches curated demo content to that chat, bounded by a daily test-send allowance. When your organization also owns a bot, the same test/send endpoint dispatches through your own bot instead — the call shape never changes.
The gate classes are the cross-channel ones: the send-permitted gate opens on either an own bot or an active demo verification, and the asset-owner gate (bot identity mutations, payments, business connections, sticker packs) only opens on an own bot. Both classes are defined once on the channel test-account model; Telegram implements them with the Bot API feature list.

The demo pack, and what linking a chat unlocks

A verified demo account is not an empty channel. On verification, Orbit seeds a curated pack of demo content so the dashboard preview and the test send both have real material to work with:
  • Text templates spanning the common transactional shapes — OTP, welcome, order status, appointment reminder, shipping update, payment receipt, feedback request, booking confirmation, account alert, back-in-stock — with {{1}}-style variables filled by sample values.
  • Inline keyboards for conversational UX — a support menu, NPS collector, agent handoff, and an appointment time picker — rendered as real button matrices.
  • Reply keyboards for the three Bot API request primitives (contact, location, group member) that inline buttons cannot express.
  • Polls, venues, Mini App, and streaming draft — the remainder of the current Bot API surface presented as demo placeholders, so the preview shows what the paid surface will look like when you connect your own bot.
The whole pack is returned by GET /api/v1/telegram/demo-content, so your front end can render it without a Bot API round-trip. Demo content IDs follow a fixed orbit_demo_* prefix, which is how the demo gate filters curated material from operator-authored content on the test path.

Resolving inbound updates back to your organization

Telegram webhook payloads do not carry the bot id — every bot’s updates arrive on the same endpoint — so routing depends on the X-Telegram-Bot-Api-Secret-Token header Telegram echoes back from the secret registered at setWebhook time. The credentials repository resolves that header in ordered lanes:
  1. Per-organization bot credential row. Each connected bot stores a per-row webhook secret; a match resolves the owning organization and refuses organizations that have been soft-deleted, so inbound into a retired tenant is rejected rather than routed.
  2. Shared demo-bot row. The shared test bot is a single platform entry; a match authorizes the request, and tenant attribution then happens by verified chat id or by the linking token redeemed on /start.
  3. Derived platform secret. A value derived from the platform secret — matching exactly what the platform registration passed to setWebhook — closes the loop between registration and verification without a second Secret.
Delivery receipts use the same first lane: the receipt endpoint resolves the bot by its webhook secret and returns a retryable failure if the lookup itself errors, so a database blip does not masquerade as a forged token. A missing or non-matching secret answers 401, and Orbit distinguishes a genuine mismatch from an infrastructure failure — a probe is logged and dropped; an outage is loud and retryable.

The bot-credential lifecycle

One credential row per bot, kept for audit:
  • Connect validates the token with a getMe call, stores the token encrypted, registers the webhook with its per-row secret, and marks the row active. A bot id can belong to exactly one organization at a time.
  • Disconnect soft-retires the row (revoking the webhook) rather than deleting it — history of who owned a bot is preserved, and lookup lanes short-circuit retired rows cheaply.
  • Reconnect reactivates a retired row in place, so re-pasting a bot token — by the same organization or a new one claiming a released bot — succeeds instead of colliding with the archived row.
Numeric Telegram bot ids exceed 32-bit range; they are handled as 64-bit values end-to-end so routing and ownership checks do not lose precision at the HTTP boundary.

Cost model: ledger rows on a free channel

Telegram’s Bot API is free at the provider level. Orbit still writes a $0 cost row on every Telegram send, marked free for production traffic and test for demo-bot traffic:
  • Analytics stay complete. The per-channel cost rollup aggregates message prices by channel; without an explicit row, Telegram volume would vanish from the chart and skew per-message-cost denominators low.
  • Demo traffic is metered. Demo-tier rows let the platform account for the shared asset’s operating cost, the same reason the WhatsApp demo tier is metered.
  • A wiring point for paid features. When a paid Telegram surface such as Telegram Stars lands, the breakdown gains a unit-priced tier while the ledger shape, the aggregation, and the dashboard donut keep working unchanged.
The rollup endpoint GET /api/v1/telegram/analytics/costs splits demo vs free volume over a window and returns the totals with a free-tier disclaimer, so a future paid tier becomes a column in the same shape rather than a new integration.

Cross-references