Telegram Bridge
RoboCo can DM you on Telegram the moment something needs you — a task escalating for your approval, or a task completing — and, once you turn on the inbound half, you can act right from the chat: check status, browse everything awaiting your decision, and approve or reject with a tap. A third layer, the Mini App, goes further: open the bot's menu button in Telegram and you land on a phone-sized cockpit — a daily brief, native approval cards, a read-only board, your inbox, and A2A chat, all without leaving the app. The panel's own notifications and journals stay the source of truth; Telegram is a faster way to notice, a faster way to act, and now a real second surface to run the company from your phone.
All three layers are off by default, and even armed none of them do anything until you've stored a bot token and chat id. The inbound half needs its own extra flag on top of the outbound one, and the Mini App needs cloud auth plus its own flag on top of that — see "Enable it" below.
Outbound: DMs to you
- Three triggers. A Telegram DM fires when a task escalates to
awaiting_ceo_approval, when a task completes, and whenever any of the four held-draft queues — a release proposal, an X post, a video, or a roadmap review cycle — originates a new item. - Subject and a link, never the content. The message is the notification's subject line plus (when
ROBOCO_PUBLIC_BASE_URL/your panel URL is configured) a deep-link straight to that task in the panel. The task's body, comments, diffs, or notes are never sent to Telegram — if you want the detail, you follow the link into the panel. - Best-effort, deferred. The send happens after the triggering database transaction commits and is wrapped so a Telegram outage, a bad token, or a rate limit never blocks the escalation, completion, or origination it's attached to. Worst case, you just don't get the DM — the org keeps moving.
- Actionable when the inbound half is on. With commands and buttons armed, the escalation DM and every held-draft-origination DM carry their own Approve / Reject / Open row, so you can act on it without leaving Telegram. The completion DM stays link-only — there's nothing to approve on a task that's already done.
Inbound: commands and one-tap actions
With the inbound half armed, RoboCo also polls Telegram for what you send it — commands you type, and taps on the buttons that ride along on escalation DMs, held-draft-origination DMs, and the queue. Nothing fires without you typing a command or tapping a button; the bot never posts to you on its own beyond the three outbound triggers above.
/status— a quick fleet snapshot: how many agents are active, and task counts by status./queue— everything currently awaiting your decision across the panel's approval queues — CEO-escalated tasks, an open release proposal, held X/video drafts, and proposed roadmap items — each one sent as its own message with its own Approve / Reject / Open row (capped at 10 items per call; use the panel for the rest)./task <id>— look up one task by its 8-character id or a title fragment. An ambiguous fragment lists the candidates instead of guessing./agents— who's working on what, right now./blocked— every blocked task plus everything currently awaiting you, capped to a phone-sized list./usage— today's token spend./help— the full command list, also shown as the fallback reply to anything unrecognized. The same list drives Telegram's own/command menu for the bot — one registry, so the two can never drift apart.
Every message and every button tap is checked against the stored chat id, and — when Telegram supplies a sender — the sender's id too. Anything from another chat or another sender is silently dropped, so this only ever works from the one private chat you set up.
Chatting with Secretary or drafting a task, from the chat itself
Two more commands bridge the Telegram chat straight into the same live agents the panel's own chat surfaces drive — not a separate, lesser copy of them:
/secretary [message]— opens (or continues) a conversation with the chief-of-staff. Send it with no message to just open the session; from then on, plain text you send is routed into that live conversation, the same as typing into the Secretary panel./newtask [message]— opens a live Intake conversation the same way. Describe what you want built, back-and-forth, exactly as in the panel's Task Assistant. When the agent has enough to propose a task, it posts a draft card — title, team, description, acceptance criteria — with Send to Board and Discard buttons right under it. Tapping Send to Board confirms the draft into the normal board-review flow and parks the chat, so when the Product Owner and Head of Marketing finish reviewing it, their feedback streams right back into the same Telegram thread instead of stranding you back in the panel to see it./end— closes whichever of the two sessions is open in this chat.
Because Intake and Secretary are single, process-wide conversations, starting one from Telegram takes over from a session left open in the panel, and vice versa — there's one live conversation per kind, not two independently drifting copies. A session left idle for a while is swept automatically; a parked session (waiting on board feedback) is exempt, so it can't be reaped out from under you mid-review. MegaTask batches (several tasks proposed in one draft) still confirm from the panel only — the chat bridge is single-task by design.
Approving and rejecting from chat
Tapping Approve or Reject on a task, release, X/video draft, or roadmap item calls the exact same guarded service path the panel's own approve/reject buttons call — the same locks, the same idempotency, no shortcut. The action is audited alongside every other approval, tagged via=telegram so you can tell a chat-driven decision from a panel one.
- Approving a task needs a note (the same 20-character-minimum CEO note the panel requires), so RoboCo replies asking you to type it; your next message answers the prompt. Release, X/video, and roadmap approvals need no note and post-and-publish (or approve) immediately.
- Rejecting anything always prompts for a reason, with the same per-kind minimum length the panel's own reject forms enforce (a release rejection needs more detail than an X-post rejection, for instance) — reply to the prompt with your reason to complete it.
- A reply prompt expires if you don't answer it in time; RoboCo tells you to tap the button again rather than silently accepting a stale reply.
- Stale buttons are refused, not silently accepted. Tap Approve or Reject on an item someone (or you, from the panel) already decided, and RoboCo tells you plainly — "already rejected" or "already published" — instead of double-posting or re-publishing it.
Setting up a bot with BotFather
Telegram bots are created through Telegram's own @BotFather, not through RoboCo:
- Open a chat with @BotFather in Telegram.
- Send
/newbot, then follow the prompts for a display name and a username (must end inbot). - BotFather replies with a bot token — copy it, you'll paste it into the panel.
- Message your new bot directly (or add it to a group and message the group) so it has a chat to send into.
- Get the chat id: the simplest way is to message a helper bot like
@userinfobot(for a DM) or@getidsbot(for a group), which replies with the numeric id. Alternatively, hitapi.telegram.org/bot<your-token>/getUpdates(over https) after messaging your bot and read thechat.idfield from the JSON response. - Optional, for the Mini App: send
/setmenubuttonto BotFather, pick your bot, then supply the button text and the URLhttps://<your-public-host>/tg. Telegram only accepts anhttps://URL here — see "Mini App: a cockpit on your phone" below for the rest of what that needs.
Mini App: a cockpit on your phone
Beyond the DM and command surfaces above, RoboCo ships a full Mini App: open the bot's menu button in Telegram and it loads /tg, a phone-sized cockpit with five tabs.
- Today — the tab you land on. One glance answers "does anything need me?": a spend hero (today's cost, its trend against yesterday, and a live 7-day sparkline), a needs-you banner when something's waiting plus a held-draft count per queue, the fleet as live avatars with each agent's current task title, the week's shipped-task count as a day-by-day bar chart, and the current version plus whether a release proposal is waiting (the ship state). A row of quick actions sits right under the spend hero — see "Operations ring" below.
- Approvals — the exact same release-proposal, X-post, video-post, and roadmap-review queues the panel's dashboard shows, as a native card stack you thumb through: full context per kind (a live 280-character counter while editing an X post, the actual video preview with a cut switch, the per-acceptance-criterion view on a release proposal, a roadmap pitch's full write-up), Telegram's own bottom MainButton/BackButton chrome for the primary action, and haptic feedback on every tap. If one of the four underlying queues fails to load, that failure is shown plainly — it never quietly renders as "you're all caught up."
- Inbox — every notification, newest first, with an Acknowledge button on the ones that require one.
- Board — every task, read-only, grouped into collapsible sections by status. Tapping a row opens a task detail sheet: status, the same "bounced" chip the desktop panel shows once a task has been sent back before, the description, every acceptance criterion, the open items from the revision findings ledger (severity-coded, with the file/line and the suggested fix), and a link straight to the PR. It's read-only by design — decisions still happen in Approvals or the desktop panel — but you no longer have to leave the chat just to see what a task actually says.
- Chat — your A2A conversations: read a thread, reply, or start a new DM with any DM-capable agent.
Chat and Today both refresh live off the /ws/system operator stream, the same socket-with-fallback pattern the desktop panel uses everywhere (see WebSockets): the moment something changes, the tab updates without a manual pull-to-refresh, and it falls back to polling if the socket drops.
Operations ring
Today's quick-action row is for doing things, not navigating — the tab bar below already handles navigation:
- Ship — jumps straight to the release proposal in Approvals when one is open; otherwise just shows the current version.
- Ack all — acknowledges every notification currently requiring one, in a single tap, with a haptic confirmation and a count of how many went through.
- Sweep — opens a confirmation sheet, then runs Clean Up Stale Branches across every project with git configured — the same completed/cancelled-task branch cleanup the Git page's button runs, reachable now without opening the desktop panel at all.
- Fleet — opens a full working-roster sheet: every mid-task agent, their live avatar, and what they're building, beyond the handful Today shows inline.
The look
The cockpit carries its own brand identity rather than reading like a generic settings screen: a monospaced display face (Share Tech Mono) for labels, stats, and a blinking ROBOCO_ wordmark; a hand-drawn duotone icon set in place of stock glyphs; and small, purposeful motion — the spend figure counts up, tabs rise in on switch, the sparkline draws itself in, sheets slide up from the bottom — all built from transform/opacity so it stays smooth on a phone and backs off automatically if you have reduced-motion set system-wide. Inside Telegram, the surfaces still repaint to your Telegram theme; the accent color and the brand type stay constant either way.
Signing in is automatic and cryptographic
There's no password to type inside Telegram. When you open /tg, Telegram hands the page a signed initData payload identifying you; RoboCo validates its HMAC signature against your stored bot token (Telegram's own WebApp validation algorithm), checks the payload isn't stale (ROBOCO_TELEGRAM_INITDATA_MAX_AGE_SECONDS, default 600 seconds), and confirms the Telegram user id in the payload matches the chat id you configured for the bridge. Once all three check out, it mints the exact same cloud-auth session cookie POST /api/auth/login issues for a normal panel login (see API auth) — so from that point on, the cockpit is an authenticated panel session, just running full-screen in Telegram. No password is ever typed inside Telegram, and the session respects the same sliding lifetime as a normal panel login. Sign-in attempts are rate-limited and recorded to the audit trail like any other auth event.
Setup requirements
The Mini App layers on top of everything above, plus cloud auth:
- Public HTTPS. Telegram only opens a Mini App over
https://— a bare LAN address won't do; the deployment needs a real public host (reverse proxy, tunnel, whatever gets you TLS). - Cloud auth armed.
ROBOCO_CLOUD_AUTH_ENABLED=truewith the seeded CEO login configured (see Cloud auth) — the Mini App mints a cloud-auth session cookie, so there's nothing to mint without it; startup refuses to come up if the Mini App flag is on and this isn't. LeaveROBOCO_PANEL_AGENT_TOKENunset, same reason as for cloud auth generally. ROBOCO_TELEGRAM_MINIAPP_ENABLED=true. Deliberately environment-only — like cloud auth itself, it's a security/TLS-coupled switch, not something on the panel's Feature Flags card.- Bridge credentials. The same bot token and chat id as the DM/command bridge above, stored in the panel.
- Point the bot's menu button at your Mini App. BotFather →
/setmenubutton→ your bot → button text, thenhttps://<your-host>/tg. No code involved — step 6 above.
/tg checks for Telegram's own launch context. Opened in a regular browser tab, it shows an "Open from Telegram" screen instead of the cockpit — there's no initData to validate, so there's nothing to sign in with. The one exception is a demo mode (/tg?demo=1) meant for evaluating the layout or taking screenshots: it renders the Today, Approvals, Board, and Inbox tabs against fixture data instead of your real fleet, with no backend or sign-in involved. Chat is the one tab demo mode can't fake, since it has no live conversations to show.
Mini App sign-in checks the initData's Telegram user id against the chat id you configured for the bridge. If that chat id belongs to a group, sign-in can never succeed — a group has no single Telegram user id to match. Use your own personal chat id here, even if you point the DM/command bridge at a group elsewhere.
Stale actions are refused the same way everywhere in RoboCo: approving or rejecting an item someone already decided — from the panel, from a DM button, or from a tap inside the Mini App's Approvals tab — is rejected rather than double-applied, because every surface calls the exact same guarded service path.
Required configuration
| Setting | Where it lives | Notes |
|---|---|---|
ROBOCO_TELEGRAM_ENABLED | Environment / panel flag | Master switch. Off ⇒ no Telegram API call is ever made. |
ROBOCO_TELEGRAM_INBOUND_ENABLED | Environment / panel flag | Sub-switch on top of the master switch above. Off ⇒ the bot only sends the three outbound DMs; it never polls, never reacts to a command, and the escalation DM and every held-draft-origination DM carry no buttons. |
| Bot token, chat id | Settings → Feature Flags, in the collapsible under the Telegram notifications flag row | Fernet-encrypted at rest, the same pattern as a project's git token or the X engine's OAuth secrets. Write-only — once saved, the panel never displays them again. The same credentials back all three layers. |
ROBOCO_TELEGRAM_TIMEOUT_SECONDS | Environment (default 10.0) | Per-request timeout for the outbound Bot API call. |
ROBOCO_TELEGRAM_MINIAPP_ENABLED | Environment only | Master switch for the Mini App (POST /api/telegram/webapp-auth). Not on the panel's Feature Flags card, on purpose — see the note below. Requires ROBOCO_CLOUD_AUTH_ENABLED=true; the orchestrator refuses to start if this is on without that. |
ROBOCO_TELEGRAM_INITDATA_MAX_AGE_SECONDS | Environment (default 600) | How old, in seconds, a Telegram-signed initData payload is allowed to be before Mini App sign-in refuses it as stale. |
The bot token and chat id are never read from environment variables — they live only in an encrypted database row, decrypted server-side only. Set them from the collapsible under the Telegram notifications flag row in Settings → Feature Flags.
ROBOCO_TELEGRAM_MINIAPP_ENABLED is security/TLS-coupled, the same reasoning as ROBOCO_CLOUD_AUTH_ENABLED itself — it changes what a request can authenticate as, so it stays environment-only rather than something a runtime toggle can flip mid-session. It isn't on the Feature Flags card.
Enable it
Settings → Feature Flags carries "Telegram notifications" and, right below it, "Telegram inbound commands + actionable buttons". Turn on the first for outbound DMs; also turn on the second for commands and one-tap buttons. Then expand the credentials collapsible under the Telegram notifications row and paste in the bot token and chat id from the BotFather setup above — all three layers share these. The Mini App's own switch isn't here — see the Environment tab.
Both feature flags persist in the settings store and apply on the next backend restart. Credentials take effect as soon as they're saved — no restart needed for those.
See the environment reference for the full flag list.
What changes when it's on
- With all flags off, or with no credentials stored, nothing changes — every escalation, completion, and held-draft origination behaves exactly as it does today, just without the DM.
- With the outbound flag on and credentials saved, an escalation to
awaiting_ceo_approval, a task completion, and a new item in any held-draft queue each send you one Telegram message: the subject, and a link back into the panel. - With the inbound flag also on, the escalation DM and every held-draft-origination DM gain Approve/Reject/Open buttons,
/status,/queue,/task,/agents,/blocked, and/usagestart answering you in the chat, and/secretary//newtaskopen live conversations bridged into the same runtimes the panel drives. - With cloud auth and the Mini App flag both on, the bot's menu button opens
/tg— the five-tab phone cockpit (Today, Approvals, Inbox, Board, Chat) with its own operations ring, live/ws/systemrefresh, and brand look — and signs you in automatically off Telegram's own signed launch data, minting the same session cookie a panel login would. - A Telegram outage, an expired token, or a bad chat id degrades silently on the Telegram side only — the escalation/completion/origination itself still goes through, and a poll cycle that fails is just retried on the next one.
Next
→ Notifications — where the same escalations and completions show up in the panel, including the ack-required expiry the Ack-all button clears. → Review & Quality — the findings ledger the Board tab's task sheet reads from. → Git — the stale-branch sweep the ops ring's Sweep button also runs. → Cloud auth — the session cookie the Mini App mints, and its own required setup. → Settings — where the flag and its credentials collapsible live. → back to Optional subsystems.