Skip to content

ADR-0012 — Use long-polling (not webhooks) for Telegram updates in MVP-1

Accepted2026-04-28

Telegram offers two delivery modes for bot updates: long-polling (getUpdates) and webhooks (Telegram POSTs to your HTTPS endpoint).

MVP-1 hosts on a single VPS (ADR-0013) without a public-facing domain or TLS termination. Traffic is sub-100 messages/day, latency budget is “user doesn’t notice” (a couple of seconds is fine).

OptionSummaryProsConsOutcome
A — WebhookTelegram POSTs to https://bot.example.com/...Lowest latency; no polling overheadRequires public domain, TLS cert (Let’s Encrypt + renew), reverse proxy or aiohttp TLS, firewall rule, hardening against abuse — none of which add value at 100 msg/dayrejected
B — Long-pollingBot calls getUpdates with timeout=30Works behind NAT; no domain or cert needed; trivial to develop locally; aiogram defaultsSlightly more wasted CPU on idle polling; one extra second of latency in the worst caseaccepted

We will use Dispatcher.start_polling(bot) with aiogram’s default long-polling (timeout 30 s, skip_updates=False). Telegram queues updates for up to 24 h if the bot is offline; on restart we fetch the backlog and dedupe via app.processed_update.update_id.

Webhook is a future ADR when:

  • the bot must respond in < 1 s (currently no SLO requires it), OR
  • the bot fans out to many users (current scope: 2), OR
  • we add a web companion that already needs HTTPS (deferred to v1).
  • Zero infrastructure beyond the VPS + Telegram.
  • Local development is identical to production: same start_polling loop, no need for ngrok or local TLS proxy.
  • No surface area for inbound abuse (no public HTTP endpoint).
  • Continuous outbound HTTPS connection from VPS to api.telegram.org. Acceptable.
  • ~30 s of update backlog is possible after a restart; idempotency via processed_update.update_id makes redelivery safe.
  • When traffic > 1 msg/sec sustained or external onboarding ships in MVP-2, write a successor ADR proposing webhooks and revisit.
  • aiogram 3 docs — Dispatcher.start_polling.
  • Telegram Bot API — getUpdates semantics, including 24 h queue.