Skip to content
Receiver guide

Build a webhook receiver in FastAPI

A FastAPI endpoint that awaits the raw body for signature verification, compares digests safely, and offloads work to a background task.

FastAPI makes a webhook receiver a few lines, and Python’s hmac module handles verification. The important part is awaiting the raw body before anything parses it.

A minimal receiver

Read await request.body() for the raw bytes, compare the HMAC with hmac.compare_digest (constant-time), return 200, and offload work with BackgroundTasks. A bad signature returns 401.

import hmac, hashlib, os
from fastapi import FastAPI, Request, BackgroundTasks, HTTPException

app = FastAPI()
SECRET = os.environ["WEBHOOK_SECRET"].encode()

@app.post("/webhooks")
async def receive(request: Request, tasks: BackgroundTasks):
    body = await request.body()          # RAW bytes, not await request.json()
    sig = request.headers.get("x-webhook-signature", "")

    expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):   # constant-time
        raise HTTPException(status_code=401, detail="bad signature")

    tasks.add_task(process, body)         # process asynchronously
    return {"ok": True}                   # fast 2xx

Acknowledge fast, process async

BackgroundTasks runs process after the response is sent, so the provider gets its 200 quickly. For work that must survive a restart, use a real task queue (Celery, RQ, arq) instead.

Point the provider’s webhook at a HookWatch endpoint and every delivery is captured with its response. When a handler raises, you can inspect the failure and replay the delivery once it’s fixed.

Get started

Start debugging your webhooks.

Point one endpoint at HookWatch, capture a failure, and replay it once it’s fixed. Free during beta.