Webhook Handler Pattern
Receive, verify, and process webhooks from external services safely.
Pattern
// Pseudocode — Stripe-style webhook handler
function handleWebhook(request):
// 1. Verify signature
signature = request.headers["X-Signature"]
payload = request.rawBody
if !verifyHmacSha256(WEBHOOK_SECRET, payload, signature):
return 401, { error: "Invalid signature" }
// 2. Check timestamp freshness (prevent replay)
event = parseJson(payload)
if event.timestamp < now() - 5_minutes:
return 400, { error: "Event too old" }
// 3. Idempotency check
if db.exists("webhook_events", { eventId: event.id }):
return 200, { status: "already_processed" }
// 4. Store event before processing
db.insert("webhook_events", {
eventId: event.id,
type: event.type,
payload: event,
receivedAt: now(),
status: "pending"
})
// 5. Return 200 immediately
// 6. Process asynchronously
queue.publish("webhook_processing", { eventId: event.id })
return 200, { status: "accepted" }Rules
- Always verify signatures with constant-time comparison.
- Return 200 quickly. Process heavy work in background.
- Store raw event for debugging and replay.
- Make processing idempotent — same event ID processed only once.
- Log: event type, event ID, processing result.