Back to blog

Webhook Event Handling: Best Practices, Patterns & Examples

Learn webhook event handling best practices, security, and reliability with examples—build robust endpoints and process events with confidence.

Introduction

Webhook event handling is the process of receiving an incoming webhook, verifying that it is authentic, parsing its JSON payload, and turning the event into reliable internal work. A service such as GitHub or Azure Event Grid sends a webhook when something happens; your system receives the request, checks the signature, and decides what to do next.

That distinction matters. A webhook is the delivery mechanism, while the event handler is the code that processes the event. In an event-driven architecture, the provider pushes updates when something changes instead of waiting for your app to poll for updates on a schedule.

This guide explains how webhook event handling works, how to create a webhook endpoint, how to secure it, and how to make it reliable in production. It also links to deeper resources on webhook basics, webhook endpoint setup, webhook payload structure, and webhook reliability best practices.

What webhook event handling means and how it works

Webhook event handling starts when an external system emits an event and sends an HTTP POST to your webhook endpoint. Your application verifies the request, reads the payload, and routes it to the correct event handler, which then triggers downstream business logic such as updating an order, syncing a customer record, or starting a CI job.

A webhook is the delivery mechanism, an event is what happened, and the handler is your code that reacts. In GitHub webhooks, a push event means a repository changed; GitHub sends the delivery, your app inspects delivery headers, the event type, and the event ID, then processes that push once. This is event-driven architecture in practice: the provider pushes updates immediately, unlike polling, which repeatedly asks for changes on a schedule.

Design for at-least-once delivery, because duplicate deliveries can happen and must be handled safely.

Create, secure, and validate a webhook endpoint

Create a dedicated webhook endpoint route, such as /webhooks/github, and accept the HTTP method the provider sends, usually HTTP POST. Keep the endpoint stable and easy to monitor, and return a fast 2xx response after signature verification and enqueueing, not after full processing. For setup patterns, see the webhook endpoint setup guide and the webhook basics tutorial.

Read the raw request body before parsing, then verify an HMAC using the provider’s shared secret so the computed hash matches the signature header. Use timestamp validation to reduce replay attack risk, and require TLS/HTTPS; add IP allowlisting only when the provider publishes stable ranges.

Validate the content type, parse the JSON payload safely, and apply schema validation to required fields before any business logic. Reject malformed requests with the correct HTTP status codes early. This pattern works in Node.js, Python, Ruby, PHP, Java, Go, and serverless functions such as AWS Lambda, Google Cloud Functions, and Azure Functions.

Handle event types, retries, and duplicate deliveries reliably

Route multiple event types with a dispatch table, switch/case, or separate handler functions so one endpoint can process order.created, order.updated, and payment.failed cleanly. Treat unknown event types as non-fatal: log them, send them to a fallback path, or park them for review instead of failing the whole delivery. For payload shape details, see webhook payload structure.

Use webhook reliability best practices patterns that assume at-least-once delivery: duplicates are normal, so build for idempotency. An idempotent handler can receive the same event twice without double charging a card or creating duplicate records. Deduplicate with an event ID, delivery ID, or provider key before side effects, and store processed IDs with delivery attempts for auditability.

Webhook retries usually happen after non-2xx responses, timeouts, or transient failures, often with backoff. Keep the HTTP handler thin, push work into a message queue or background jobs, and move repeated failures to a dead-letter queue for later inspection.

Process webhook events asynchronously and observe them in production

Use the enqueue-and-acknowledge pattern: verify the signature, validate the payload, store the raw event if needed, enqueue it to a message queue or background jobs system, then return a 2xx response immediately. This keeps webhook event handling fast and avoids provider timeouts. Synchronous processing is risky because a slow database, API, or payment service can stall the endpoint, trigger retries, and create duplicate deliveries.

Background jobs isolate intake from business work, so a spike from GitHub, Azure Event Grid, or another provider does not cascade into your core app. Add retry logic for transient failures and use a dead-letter queue for jobs that keep failing. For webhook reliability best practices, this separation is a core pattern.

For observability, log the event ID, delivery ID, correlation ID, handler name, status, and latency with structured logging. Do not log secrets, signatures, or full sensitive payloads; redact fields like tokens, card data, and personal details. Trace one delivery from provider ID to internal job ID, then alert on rising error rates, long latency, and repeated retries.

Test and troubleshoot webhook event handling

Use ngrok or a similar tunnel to expose your local webhook endpoint during webhook testing, so GitHub, Stripe, or another provider can reach localhost. Test against the real route from webhook endpoint setup and compare the incoming payload with the webhook payload structure.

Add unit tests for signature verification, raw request body handling, parsing, and schema validation. Verify that your code rejects tampered HMAC signatures, preserves the exact raw request body, and fails cleanly on malformed JSON or missing fields.

Use provider test modes and replayed samples from docs such as GitHub Docs to run integration tests against real deliveries. Cover invalid content type, unauthorized requests, duplicate deliveries, and webhook retries that arrive twice or more.

Troubleshoot by HTTP status codes: 400 usually means bad payload or parsing, 401/403 means auth or signature verification failure, 5xx means app-side errors, and timeouts usually mean slow processing or network issues. Structured logging should capture event ID, delivery ID, signature result, and handler outcome so you can isolate provider-side, network-side, or application-side failures quickly.

Webhook event handling best practices

  • Give webhook handlers least-privilege access to downstream services, and keep the shared secret separate from app credentials in a dedicated secret store.
  • Use HTTPS with TLS, verify the signature before any business logic, and return a fast 2xx response once the event is safely accepted.
  • Make every side effect idempotent. Use event IDs or dedupe keys so at-least-once delivery and retry logic do not create duplicate orders, emails, or refunds.
  • Process work asynchronously, then monitor failures with structured logging, metrics, and alerts so delivery issues show up in your webhook reliability dashboards before customers do.
  • If a provider documents delivery headers, event type, event ID, or retry behavior, capture those fields in your logs and internal traces.

Common webhook delivery failures

Webhook delivery failures usually fall into a few categories:

  • Timeouts: the handler takes too long before returning a 2xx response.
  • Signature verification failures: the raw request body was altered, the shared secret is wrong, or timestamp validation failed.
  • Parsing errors: the content type is wrong, the JSON payload is malformed, or schema validation rejects the event.
  • Application failures: database errors, queue outages, or downstream API failures cause 5xx responses.

When a delivery fails, providers typically retry according to their own webhook retries policy, often with backoff. That is why the handler should acknowledge quickly and move work to asynchronous processing.

How to secure a webhook endpoint

A secure webhook endpoint should verify authenticity, limit exposure, and reduce replay risk.

  • Require HTTPS and modern TLS.
  • Verify the signature with HMAC and a shared secret.
  • Validate the timestamp to reduce replay attack risk.
  • Check the content type and reject unexpected payload formats.
  • Apply schema validation before business logic.
  • Use least-privilege credentials for any downstream systems.
  • Log security-relevant events without exposing secrets.

Security is not just about blocking bad requests. It is also about making sure a valid request cannot be replayed, duplicated, or misrouted into the wrong handler.

How to test webhook event handling locally

Local webhook testing usually combines a tunnel, sample payloads, and automated tests.

  1. Run your app locally and expose the endpoint with ngrok.
  2. Configure the provider to send events to the public tunnel URL.
  3. Replay sample events from provider docs such as GitHub Docs.
  4. Test signature verification using the exact raw request body.
  5. Confirm that duplicate deliveries are deduplicated and that retries do not create duplicate side effects.
  6. Verify logs, correlation IDs, and queue messages so you can trace the full path.

For more on payload shape and parsing, see webhook payload structure.

Language and platform examples

Webhook event handling follows the same core pattern in most stacks, but the implementation details differ.

  • Node.js: use middleware that preserves the raw request body before JSON parsing.
  • Python: read the raw bytes first, then verify the signature before decoding JSON.
  • Ruby: ensure the framework does not consume the body before verification.
  • PHP: access the raw input stream directly before parsing.
  • Java: use the request body stream carefully so signature verification sees the original bytes.
  • Go: read and restore the body if multiple layers need access.
  • Serverless functions: AWS Lambda, Google Cloud Functions, and Azure Functions often pair well with a queue-based design.

These patterns also apply to GitHub webhooks and Azure Event Grid integrations.

Conclusion

Strong webhook event handling follows a simple flow: receive the HTTP POST, perform signature verification, validate the payload, enqueue the work, and return a fast 2xx response. That sequence protects your endpoint from spoofed requests, keeps providers from retrying unnecessarily, and gives your application room to finish the real work through asynchronous processing.

Reliability comes from designing for failure. Use idempotency and deduplication so repeated deliveries do not create duplicate side effects, make handlers retry-safe, and route poison messages to dead-letter handling when an event keeps failing. Add observability at every step so you can trace what arrived, what was accepted, and what was processed later.

This is also why webhooks are more efficient than polling for event-driven systems: the provider pushes only when something changes, instead of forcing your app to repeatedly ask for updates. That reduces wasted requests and lets your system react faster with less overhead.

If you want to tighten your implementation, revisit the webhook basics tutorial, webhook endpoint setup, webhook payload structure, and webhook reliability best practices.