Telegram Bot API

Layer mapping: operational sink. Per cortana-north-star scope discipline, Telegram is NOT signal substrate - it’s a sink the hub publishes events to. Useful for paper-trading observability and later live alerting; NOT load-bearing for the SPY hunter to function.

The Bot API is the simplest of Telegram’s three programming surfaces (Bot API / TDLib / MTProto). Pure HTTPS, no MTProto knowledge required, no phone number, no client lib mandatory. Right choice for MK3.

1. Surface comparison

APIUseMK3 fit
Bot APIBuild bots over simplified HTTPSYes - the right choice.
TDLibBuild full Telegram client appsNo
MTProtoLow-level protocol; full client featuresNo
Gateway APISend SMS-equivalent OTP via TelegramNo

2. Base contract

PropertyValue
URL patternhttps://api.telegram.org/bot<TOKEN>/<METHOD>
Token format123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 (numeric:string)
AuthToken in URL path (NOT header, NOT first-message)
MethodsGET or POST
Content typesapplication/x-www-form-urlencoded, application/json, multipart/form-data (for files)
Response envelope{ "ok": true, "result": ... } or { "ok": false, "description": "...", "error_code": N }

3. Setup (one-time)

3.1 Create the bot

  1. Message @BotFather on Telegram.
  2. Send /newbot. BotFather asks for a name + a username (ending in bot).
  3. BotFather returns the bot token. Save to .env immediately as TELEGRAM_BOT_TOKEN; never commit. Anyone with the token has full control of the bot.

3.2 BotFather command catalog (the useful ones)

CommandPurpose
/newbotRegister a new bot
/mybotsList your bots + manage them
/tokenRegenerate the auth token (revokes the old one)
/setdescriptionLong description shown in chat header
/setabouttextShort bio on profile
/setuserpicProfile picture
/setcommandsRegister slash-commands (e.g. /pause, /status)
/setprivacyToggle whether the bot reads non-command messages in groups

3.3 Get your chat_id

You need the destination chat_id to send messages.

  1. Send any message to your bot from your Telegram account.
  2. Call https://api.telegram.org/bot<TOKEN>/getUpdates once.
  3. Find result[0].message.chat.id in the JSON. Save as TELEGRAM_CHAT_ID in .env.

For group chats, add the bot to the group, send a message there, and the chat_id will be negative.

4. Methods MK3 will actually use

Out of 100+ Bot API methods, MK3 needs ~10. The rest (games, payments, stickers, inline mode, business mode, stars) are not relevant.

4.1 Send methods

MethodPurposeKey params
sendMessagePlain text or formatted textchat_id, text, parse_mode, reply_markup
sendPhotoImage (chart screenshots, P&L visuals)chat_id, photo, caption, parse_mode
sendDocumentArbitrary file (Parquet shards, trade logs, postmortem PDFs)chat_id, document, caption
sendMediaGroupBatch of photos/videos as one messagechat_id, media[]
sendChatActionShow “typing…” or “uploading…” statuschat_id, action
editMessageTextEdit a previously-sent message in placechat_id, message_id, text

4.2 Receive methods (commands from phone)

MethodPurposeTradeoff
getUpdatesLong-poll for new messagesSimple; no HTTPS endpoint needed
setWebhookPush to your HTTPS endpointFaster; requires public HTTPS with valid cert

For MK3, prefer getUpdates polling. No need to expose an HTTPS endpoint; the bot lives behind the same firewall as the trading engine. Polling is also more robust to brief outages because Telegram queues updates.

setWebhook ports: 443, 80, 88, 8443.

5. Message formatting

Two modes for parse_mode:

ModeBoldItalicCodeLink
MarkdownV2*bold*_italic_`code`[text](url)
HTML<b>bold</b><i>italic</i><code>code</code><a href="url">text</a>

MarkdownV2 requires escaping every reserved character (_, *, [, ], (, ), ~, `, >, #, +, -, =, |, {, }, ., !) with \. Forget one and the message fails. HTML is easier for programmatically-built messages because you only escape <, >, &.

For MK3 alerts: use HTML by default.

text = (
    f"<b>FILL</b> {side} {qty} {symbol} @ {price}\n"
    f"<code>setup={setup_name} regime={regime}</code>"
)

6. Inline keyboards (command surface from the bot itself)

InlineKeyboardMarkup attaches buttons to a sent message. Each button has either url (opens a link) or callback_data (triggers a callback_query back to your bot when pressed).

{
  "inline_keyboard": [
    [
      {"text": "Pause", "callback_data": "pause"},
      {"text": "Resume", "callback_data": "resume"}
    ],
    [
      {"text": "Status", "callback_data": "status"}
    ]
  ]
}

callback_data is limited to 64 bytes. Use short tokens (pause, resume, status, flatten) and route them on receive.

7. Rate limits

Telegram’s published guidance (verify on first real use):

ScopeLimit
Global (across all chats)~30 messages/sec
Per chat~1 message/sec (sustained)
Per group~20 messages/min (sustained)
Bulk notificationsaim under 30/sec; throttle if uncertain

On 429 (Too Many Requests), the response includes a Retry-After header (seconds) and a parameters.retry_after field in the JSON body. Always honor it.

For MK3 alert volume (a few dozen messages/day during paper), these limits are not a concern. Code defensively anyway:

async def send_with_retry(method, params, max_retries=3):
    for attempt in range(max_retries):
        resp = await client.post(url, json=params)
        if resp.status_code == 429:
            retry_after = int(resp.headers.get('Retry-After', '5'))
            await asyncio.sleep(retry_after)
            continue
        return resp
    raise RuntimeError('Telegram rate limit exhausted')

8. File upload limits

ModeLimit
Cloud API (default)50 MB per file
Local Bot API server (self-hosted)2000 MB per file

For MK3, 50 MB covers any chart screenshot, daily P&L log, or postmortem PDF. Self-hosting the Bot API server is not warranted unless we ever push Parquet shards via Telegram (don’t).

9. Common errors

HTTP codeerror_codeMeaningHandling
200-Success-
400variousMalformed request (escape error, bad chat_id)Fix and retry
401401Token invalidCheck TELEGRAM_BOT_TOKEN
403403Bot blocked by user / kicked from groupSkip; do not retry
404404Bad URL (typo in method name)Fix
429429Rate limitedHonor Retry-After
500variousTelegram server errorRetry with backoff

10. MK3 use cases

EventMethodNotes
Trade entry / fillsendMessage (HTML formatted)setup_name, symbol, side, qty, price
Trade exit / closedsendMessageAdd P&L delta
Daily P&L summarysendPhotoPlotly chart rendered as PNG
Stop-out / kill switch firedsendMessage (with alert prefix)Urgent; include cause
News circuit-breaker firedsendMessage”Suspended for 5 min:
Trading halt detectedsendMessageFrom uw-api-websockets trading-halts channel
Postmortem reportsendDocumentPDF or markdown attachment
End-of-day archive shardsendDocument (optional)Parquet path; not the file itself

11. Control surface (commands from phone)

Setup-Hunter-level controls via slash-commands + inline buttons:

CommandAction
/statusCurrent positions, P&L, regime, last 5 fills
/pauseSuspend new entries (existing positions managed)
/resumeRe-enable new entries
/flattenClose all positions immediately (with confirm button)
/setup <name>Toggle a named setup on/off
/logLast N events from the kill-switch log

Register these with BotFather via /setcommands. Implement the handlers in the Telegram Actor (see section 12).

Confirm before destructive actions. /flatten sends an inline keyboard with [Confirm] [Cancel] and only acts on the Confirm callback. Avoid the fat-finger problem.

12. Nautilus integration shape

A single Actor: TelegramSinkActor.

  • Subscribes to relevant message-bus topics (FillEvent, PositionClosed, RiskEvent, TradingHalt, NewsCircuitBreaker).
  • Publishes via outbound HTTPS to api.telegram.org.
  • Receives via getUpdates polling (a background asyncio task).
  • Routes commands by mapping callback_data + slash-commands to internal commands published back to the bus (PauseStrategy, ResumeStrategy, FlattenPositions).

Determinism per nautilus-custom-data: timestamps from update.message.date (Telegram-provided) ts_event; receive time ts_init. No replay concerns since Telegram is a sink, not a data source.

Per CLAUDE.md hard rule: the Telegram Actor implementation is a Codex handoff, not Claude code. The handoff input set is this page + nautilus-dev-adapters + cortana-north-star for scope.

13. Security guidance

  • Token in .env only; never committed, never logged.
  • Rotate token via BotFather /token if exposed (revokes the old one).
  • Bot must be added to a specific chat - by default, only members of that chat can send commands. Lock down to a single private chat with you (and maybe one trusted person).
  • Validate callback_query.from.id against an allowlist before acting on any control command. Don’t trust the bot’s authorization model; verify the sender too.
  • getUpdates returns ALL recent updates; if anyone else messages the bot, you’ll see those updates. Filter by from.id allowlist before processing.

14. Library recommendations (Python)

  • python-telegram-bot - the most popular, supports async and webhooks. Full feature coverage.
  • aiogram - async-first, ergonomic, lower-level. Good for custom routing.
  • httpx (no Telegram-specific library) - 4 endpoints + retry logic is a few hundred LoC and avoids a dep. Worth considering for MK3’s minimal scope.

Recommendation: start with raw httpx since MK3 only needs ~6 methods. Add a library only if the inline-keyboard routing logic gets messy.

15. Known gaps / footguns

  • MarkdownV2 escaping is fragile - prefer HTML.
  • callback_data is 64 bytes max - don’t put structured data in it; use short tokens and look up state separately.
  • chat_id for groups is negative - sign matters.
  • Bot must initiate first for some operations - users have to message the bot first OR the bot has to be added to a group before it can send anything there.
  • Webhook needs HTTPS with valid cert - use polling unless there’s a strong reason otherwise.
  • No native delivery guarantee on sendMessage - the API returns ok=true once accepted but the user could have blocked the bot (403). Track 403s and stop retrying for that chat.

16. Source

  • Bot API reference: https://core.telegram.org/bots/api
  • Bots overview: https://core.telegram.org/bots
  • Root: https://core.telegram.org/
  • BotFather: https://t.me/botfather

cortana-north-star nautilus-dev-adapters 2026-05-15-mk3-setup-hunter-architecture