What is a webhook handler?
A webhook handler is the server-side function that processes a verified webhook event and runs the business logic. The correct shape: read the raw body before any parser touches it, verify the signature, parse JSON only after verification passes, look up the event ID in your idempotency store and 200-immediately if it's a retry, persist to a queue, return 200, then process asynchronously in a worker. Handlers should be small — business logic lives in the worker that drains the queue, not in the handler. Handlers that do work inline turn every flaky downstream service into a webhook timeout.
The handler is the function in your code that runs when a webhook arrives — function handleStripeWebhook(req, res) or its equivalent. It is the smallest unit of webhook processing. Everything else (HTTP server, routing, signature verification, queue) exists to deliver verified events to handlers.
A correct handler has a strict shape:
1. Read the raw body before any parser touches it — express.raw({ type: 'application/json' }), not express.json(). This is the single most common bug in webhook integrations.
2. Verify the signature against the raw body. If it fails, return 401 immediately. Do not log the secret. Do not log the raw body unless you are sure it doesn't contain sensitive data.
3. Parse the body to JSON only after verification passes.
4. Look up the event ID in your idempotency store (Redis, Postgres, anything durable). If it exists, return 200 immediately — this is a retry, you've already processed it.
5. Persist the event to a queue or "events to process" table. Mark the event ID as seen.
6. Return 200.
7. Process asynchronously in a worker that pulls from the queue.
Handlers should be small. The business logic — sending the receipt email, updating the order status, calling the analytics API — does not live inside the handler. It lives in the worker that drains the queue. The handler's only job is to safely accept the event and acknowledge receipt fast enough to avoid a retry.
Handlers that try to do everything inline get progressively more brittle as the system grows. Three years in, the email service is slow on Tuesdays, the analytics API rate-limits during peak hours, the database has occasional spikes — and your webhook endpoint is timing out for all of those reasons, causing duplicate deliveries, causing your customer to be charged twice, causing a support ticket. The fix is structural: handlers ack fast, workers do work.
Handler observability matters. Log every entry with the event ID, the type, and the handler decision ({ event_id, type, action: 'queued' | 'duplicate' | 'sig_mismatch' }). When something breaks in production, you will need to grep for the event ID and reconstruct what happened in milliseconds.
See Webhook Handler in real traffic
WebhookWhisper captures every webhook with full headers, body, signature, and timing — so concepts like webhook handler stop being abstract and become something you can inspect.
Start Free