Skip to content

Telegram

Archi edited this page Oct 20, 2026 · 2 revisions

Telegram

The Nautelle bot puts the things only a person can clear in your pocket: approve a pull request or a tool call, answer a session's question, browse and run Board cards, start new work by text, photo or voice, and talk to the AI Assistant. A Mini App opens the full web app inside Telegram.

Overview

You can How
Get notified the same notifications as the in-app centre, each with its own buttons
Approve or deny pull requests and action requests, straight from the notification
Answer a session one button per option of a question a card session shows, or reply with text
Work the Board /board, /card <n> — move, run, stop, feedback, accept, finish
Start new work /new runs the New work intake interview; photos and voice messages are accepted
Ask the assistant any other text goes to the AI Assistant as you
Open the app the menu button opens the Board as a Telegram Mini App

The bot works in private chats only and uses long polling, so the API needs no public webhook.

Setup (admin)

  1. Set APP_ENCRYPTION_KEY in the API environment — 32 random bytes, base64 (openssl rand -base64 32). Without it the integration refuses to store a bot token.
  2. Create a bot with @BotFather and copy its token.
  3. Open Admin › Integrations › Telegram, paste the token and save. The API verifies it and stores it encrypted; the page then shows only the bot username, the polling status and the number of linked users.
  4. Use the test button to send a test message to your own linked chat.
Variable Purpose
APP_ENCRYPTION_KEY seals the bot token at rest (required)
APP_URL public web URL used in message links; must be https for Mini App buttons
TELEGRAM_INIT_DATA_MAX_AGE_S how old Mini App sign-in data may be (60–86400, default 3600)
SPEECH_TO_TEXT_URL, SPEECH_TO_TEXT_API_KEY, SPEECH_TO_TEXT_MODEL voice input (optional; model default whisper-1)
TELEGRAM_POLL_TIMEOUT_S long-poll timeout in seconds (default 30)

Every variable is documented in apps/api/.env.example.

Linking your account

  1. Open Account › Notifications and click Link Telegram.
  2. Follow the t.me/<bot>?start=<code> link; the bot receives /start <code> and binds the chat to your account. The code is single-use and valid for 10 minutes.
  3. To unlink, send /stop or click Unlink in the web app.

One chat per user; a chat already linked to someone else is refused.

Commands

Command What it does
/project pick the project this chat acts on (/project <name> switches by repository name)
/board [column] browse the Board: columns with card counts, then cards, 10 per page
/card <n> show a card: title, kind, column, session state, pull request, a short summary, and the actions its state allows
/new [description] start new work: the intake interview
/newchat start a new conversation with the AI Assistant
/help list the commands
/stop unlink this chat from Nautelle

A user with exactly one project has it selected automatically. Every Board reply names its project first. Buttons on a message carry their own project, so switching projects never redirects a button already in the chat.

How a request flows

flowchart LR
  T["Telegram update"] --> R{"Router"}
  R -- "button press" --> B["Button handler"]
  R -- "reply to a bot prompt" --> P["Reply handler<br/>(feedback, intake answer)"]
  R -- "/command" --> C["Command"]
  R -- "other text" --> A["AI Assistant turn"]
  B & P & C & A --> G["Guard: linked, active user<br/>+ project role"]
  G --> S["Same services as the web app<br/>(audited, via: telegram)"]
Loading

Every press, reply and command is checked from scratch: the chat must be linked to an active user, reads need any project member, and writes need operator or higher. Every action is audited the same way as on the web, with via: telegram.

Working the Board

  • Move opens a column picker; a move the Board refuses shows the Board's reason.
  • Run dispatches the card; Stop asks for a confirm tap first.
  • Feedback asks for the text as a reply to the bot's message.
  • Accept and Finish each ask for a confirm tap.
  • Inbox suggestions can be accepted or dismissed.
  • After an action the card message is edited in place to its new state.

A viewer sees cards but gets no write buttons.

Answering a session

When a card session is waiting on a question, the card.waiting notification carries one button per option. Press one to answer, or reply to the message with text — sent as the "Other" answer while the question offers it, otherwise typed into the session. See Claude Code Integration › Messaging a running session.

New work: text, photos and voice

/new runs the same intake interview as the web's New work button and ends in candidate cards in the Board's Drafts.

  • Photos are stored as ordinary attachments of the intake (same checks, size limit and quota as a web upload). A photo sent before the description waits until the plan is created.
  • Voice messages are sent to the configured Whisper-compatible speech-to-text service, the transcript is shown back as "Heard: …", and it is used as the description or as the answer. The audio is never stored.

Note

Without SPEECH_TO_TEXT_URL, voice input is off and the bot says so.

Talking to the assistant

Any text that is not a command and not a reply to a bot prompt goes to the AI Assistant, as you, with your project permissions.

  • One conversation per chat, shared with the web — it appears in the assistant's rail, where you can continue, rename or delete it. /newchat starts a new one.
  • A new conversation starts on the chat's current project, or on Auto.
  • The bot shows "typing…" while it works; long replies are split between lines.
  • If the assistant created action requests, the reply carries their Approve / Deny buttons. Nothing is approved by the reply itself.
  • A photo with a caption is attached to the conversation.
  • A chat has at most one turn at a time; a message sent meanwhile is refused, not queued.

Important

The bot never sends free-form text to a runner. Free text becomes an assistant message; the assistant reaches the fleet only through its tools, and a stop, merge or handoff only creates a pending action request.

Mini App

With an https APP_URL, the bot's menu button opens /tg, and card messages get an Open in app button to the card. The Mini App signs in with Telegram's signed init data and gets an ordinary web session, so every route and role check is the web's own. Only a linked, active account can sign in; otherwise it points you to Account › Notifications. With a non-https APP_URL, messages keep plain links instead.

See also

Decisions: ADR-0046 · ADR-0055 · ADR-0057 · ADR-0061 · Spec: #22, #500, #576, #578, #580, #594, #620

Clone this wiki locally