Aiogram Webhook

Handles the webhook layer for aiogram bots. Registers the endpoint, calls Telegram setWebhook, verifies incoming requests, and manages engine lifecycle.

What it does

FastAPI & aiohttp

Register a webhook endpoint in your existing app. Only the wiring changes.

Single-bot & multi-bot

SingleBotEngine for one bot. TokenEngine to route many bots by token in the URL.

Typed Route & Security

Build setWebhook URLs with Route. Verify secret tokens and IP ranges with Security.

Install

Requires Python 3.10+, aiogram 3.14+, a bot token from @BotFather, and a public HTTPS URL.

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.

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:

  1. Telegram POSTs JSON to your public URL.
  2. Security checks the secret token and source IP.
  3. The engine feeds the update to aiogram in the background.
  4. Your handler calls message.answer("OK") through the Bot API.
  5. Telegram gets an empty 200 from the webhook request itself.

Step 4 is a separate HTTP call — normal for background mode.