Real-Time APIs · Webhooks
Receiving Webhooks Reliably
Verify, persist, acknowledge fast and process asynchronously.
As a consumer: verify the signature over the raw body, check the timestamp tolerance, persist the event (deduplicated by event ID), return 2xx quickly (within a few seconds), and process asynchronously. Handle duplicates and out-of-order delivery (re-fetch the resource or compare versions). Monitor failures — providers disable endpoints that fail for days.
Receiver checklist
| Step | Why |
|---|---|
| Verify signature on raw body | Authenticity and integrity |
| Check timestamp tolerance | Replay protection |
| Persist + dedupe by event id | At-least-once delivery |
| Respond 2xx fast | Avoid provider timeouts and retries |
| Process async, idempotently | Robustness, ordering independence |
@app.post("/hooks/acme")
async def acme_webhook(request: Request, db: DB):
body = await request.body()
verify_standard_webhook(body, request.headers, secret=settings.acme_secret) # raises on bad sig/timestamp
event = json.loads(body)
inserted = await db.execute(
"INSERT INTO inbound_events (id, type, payload) VALUES ($1, $2, $3) ON CONFLICT (id) DO NOTHING",
request.headers["webhook-id"], event["type"], body.decode())
if inserted == "INSERT 0 1":
await queue.enqueue("process_acme_event", request.headers["webhook-id"])
return Response(status_code=204)
import { Webhook } from "standardwebhooks";
const wh = new Webhook(process.env.ACME_WEBHOOK_SECRET!);
app.post("/hooks/acme", express.raw({ type: "application/json" }), async (req, res) => {
let event;
try { event = wh.verify(req.body, req.headers as Record<string, string>); } // signature + timestamp
catch { return res.status(400).end(); }
const created = await inbound.insertIfAbsent(req.get("webhook-id")!, event);
if (created) await jobs.add("process-acme", { id: req.get("webhook-id") });
res.status(204).end();
});
Common pitfalls
- Parsing/re-serializing JSON before verification.
- Slow synchronous processing causing retries.
- Assuming events arrive in order.
Key points
- Verify, persist, ack, process
- Dedupe by event id
- Tolerate reordering
Further reading: Stripe: Webhooks best practices
Practice questions
Why should a webhook receiver respond before processing the event?
Answer
Providers time out after a few seconds and retry; slow processing causes duplicate deliveries and possibly endpoint disabling.
How can consumers handle out-of-order webhook events?
Answer
Re-fetch the current resource state from the API, or compare a version/timestamp and ignore events older than what's stored.
Open in the app to mark lessons complete, take notes and track your progress.