Aiogram Webhook
Aiogram Webhook
Handles the webhook layer for aiogram bots. Registers the endpoint, calls Telegram setWebhook, verifies incoming requests, and manages engine lifecycle.
Install
Requires Python 3.10+, aiogram 3.14+, a bot token from @BotFather, and a public HTTPS URL.
uv
pip
uv add "aiogram-webhook[fastapi]"
uv add "aiogram-webhook[aiohttp]"
pip install "aiogram-webhook[fastapi]"
pip install "aiogram-webhook[aiohttp]"
Omit the extra if you build a custom adapter or use route/security helpers only.
Minimal app
Replace BOT_TOKEN, https://example.com, and webhook-secret. base_url must be the HTTPS origin Telegram can reach.
FastAPI
aiohttp
from contextlib import asynccontextmanager
import uvicorn
from aiogram import Bot, Dispatcher, Router
from aiogram.filters import CommandStart
from aiogram.types import Message
from fastapi import FastAPI
from aiogram_webhook import FastAPIAdapter, SingleBotEngine, WebhookConfig
from aiogram_webhook.route import Route
from aiogram_webhook.security import IPCheck, Security, StaticSecretToken
router = Router()
@router.message(CommandStart())
async def start(message: Message) -> None:
await message.answer("OK")
dispatcher = Dispatcher()
dispatcher.include_router(router)
bot = Bot("BOT_TOKEN")
engine = SingleBotEngine(
dispatcher,
bot,
web=FastAPIAdapter(),
route=Route(base_url="https://example.com", path="/telegram/webhook"),
security=Security(IPCheck(), secret_token=StaticSecretToken("webhook-secret")),
webhook_config=WebhookConfig(drop_pending_updates=True),
)
@asynccontextmanager
async def lifespan(app: FastAPI):
await engine.set_webhook()
yield
app = FastAPI(lifespan=lifespan)
engine.register(app)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
from aiohttp import web
from aiogram import Bot, Dispatcher, Router
from aiogram.filters import CommandStart
from aiogram.types import Message
from aiogram_webhook import AiohttpAdapter, SingleBotEngine, WebhookConfig
from aiogram_webhook.route import Route
from aiogram_webhook.security import IPCheck, Security, StaticSecretToken
router = Router()
@router.message(CommandStart())
async def start(message: Message) -> None:
await message.answer("OK")
dispatcher = Dispatcher()
dispatcher.include_router(router)
bot = Bot("BOT_TOKEN")
engine = SingleBotEngine(
dispatcher,
bot,
web=AiohttpAdapter(),
route=Route(base_url="https://example.com", path="/telegram/webhook"),
security=Security(IPCheck(), secret_token=StaticSecretToken("webhook-secret")),
webhook_config=WebhookConfig(drop_pending_updates=True),
)
async def set_webhook(app: web.Application) -> None:
await engine.set_webhook()
app = web.Application()
app.on_startup.append(set_webhook)
engine.register(app)
if __name__ == "__main__":
web.run_app(app, host="0.0.0.0", port=8000)
What happens at runtime
When a user sends /start:
- Telegram
POSTs JSON to your public URL. - Security checks the secret token and source IP.
- The engine feeds the update to aiogram in the background.
- Your handler calls
message.answer("OK")through the Bot API. - Telegram gets an empty
200from the webhook request itself.
Step 4 is a separate HTTP call — normal for background mode.






