First Webhook
Deployment checklist. This page explains why each step matters — copy-paste code is on the home page.
What changes from polling
| Part | Change |
|---|---|
| Handlers, routers, FSM | None — move as-is |
| Process bootstrap | Web framework + engine instead of start_polling() |
| Public URL | Telegram must reach HTTPS |
set_webhook() |
Registers that URL with Telegram |
Security |
Verifies incoming requests at the public endpoint |
Tip
Develop handlers with long polling locally; switch to webhooks for deployment. Only bootstrap code changes.
1. Handlers stay ordinary aiogram
from aiogram import Dispatcher, Router
from aiogram.filters import CommandStart
from aiogram.types import Message
router = Router()
@router.message(CommandStart())
async def start(message: Message) -> None:
await message.answer("Webhook is alive")
dispatcher = Dispatcher()
dispatcher.include_router(router)
2. Wire engine and adapter
The engine connects dispatcher, bot, adapter, route, and security in one place — copy the minimal app from the home page.
Two registrations
| Step | Call | Purpose |
|---|---|---|
| Local route + lifecycle | engine.register(app) |
Registers POST route and wires engine startup/shutdown in your web framework. |
| Telegram delivery | await engine.set_webhook() |
Tells Telegram the public HTTPS URL for future updates. |
Run set_webhook() from framework startup after the endpoint is reachable. For TokenEngine, call add_bot(token) per bot instead.
Adapter-specific details: FastAPI · aiohttp
3. Expose a public HTTPS URL
Telegram delivers updates only over HTTPS to an internet-reachable host.
| Environment | Typical approach |
|---|---|
| Local development | Tunnel (ngrok, cloudflared) or staging server |
| Production | Reverse proxy (nginx, Caddy) or platform load balancer |
Route(base_url=...) must match the public origin — scheme, host, and port if non-standard.
Warning
Do not use http://localhost in base_url unless Telegram can reach it. For local work, put the tunnel's HTTPS URL in base_url.
Checklist before set_webhook()
4. Register with Telegram
Call after the endpoint is reachable:
await engine.set_webhook() # SingleBotEngine
await engine.add_bot("123456:ABCDEF") # TokenEngine
The engine forwards WebhookConfig fields and the secret from Security, so Telegram registration and request verification stay aligned. Details: WebhookConfig.
How do I know Telegram accepted it?
set_webhook() returns True on success. Inspect live state:
info = await bot.get_webhook_info()
print(info.url, info.last_error_message)
Empty url or a set last_error_message means fix connectivity or TLS before debugging handlers.
5. Verify end-to-end
- Send
/startin Telegram. - Confirm the handler reply arrives.
- On failure, check Errors and application logs (
aiogram_webhooklogger).
Request flow diagram: Dispatch modes.
Where to go next
| Topic | Page |
|---|---|
| Secret token and IP checks | Security |
allowed_updates, drop_pending_updates |
WebhookConfig |
| Project file layout | Single-bot app |
| Several bots on one service | Multi-bot app |