Repository navigation
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.
| 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.
- Set
APP_ENCRYPTION_KEYin the API environment — 32 random bytes, base64 (openssl rand -base64 32). Without it the integration refuses to store a bot token. - Create a bot with @BotFather and copy its token.
- 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.
- 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.
- Open Account › Notifications and click Link Telegram.
- 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. - To unlink, send
/stopor click Unlink in the web app.
One chat per user; a chat already linked to someone else is refused.
| 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.
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)"]
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.
- 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.
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 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.
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.
/newchatstarts 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.
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.
Decisions: ADR-0046 · ADR-0055 · ADR-0057 · ADR-0061 · Spec: #22, #500, #576, #578, #580, #594, #620
Nautelle · Website · Discord · Engineering docs · MIT
Start
Work
Cost and health
Surfaces
Operate
Reference