DEV Community

Cover image for Full practical guide on creating production-grade webhook receivers
Artem
Artem

Posted on

Full practical guide on creating production-grade webhook receivers

Throughout my career I have designed and implement tens of webhook handlers from Payments systems and delivery trackers to chat and real time communication backends and crypto services. Although all software providers use different rules, authentication mechanisms, and retry policies, the same fundamental knowledge applies to all cases.

In this tutorial I will share my experience on how to design, implement, test and deploy production-grade webhook handlers.

If you want to explore this topic in more depth and apply these guidelines in practice, I created a webhook-consumer-handbook repository. It includes practical patterns for building production-ready webhook consumers, along with implementation examples and reusable AI skills. Check it out.

What is the webhook and why it is needed?

For the official menaing you can refer to the wikipeadia article. I will explain it in easy words how I personally understand it. A webhook is an event or process that occurs when one server sends a notification to another server; thus, it is a way for one server to communicate information about a specific event to another server.

For example someone made an order through e-commerce system that you use, then you need to store information about the order in your database and send an email notification to the user and order was confirmed, then you want to use a webhook.

Webhook processing flow

First let’s look on how a good webhook receiver flow might look like:

Receive a request

The first and most important part is to add a POST endpoint to your server that will actually handle the request from the server.

Here you need to a select a reasonable internal name for the endpoint URL. Make sure that you store this URL in the environment variables and not hardcoded in the code, as this string should be added to your provider to send a webhook, so if something bad happens on the provider side, you can easily update the webhook name without redeploying your app.

Also, make sure your server supports TLS to prevent man-in-the-middle attacks where your data can be intercepted by an attacker.

Authenticating the request

Remember that your webhook endpoint is inetended to be reached only by server that you trust.

To verify incoming requests you can use different authentication methods:

  • HMAC signature verification: The provider signs the request body with a shared secret, and the consumer recomputes the signature and compares it against a value sent in a header.
  • Bearer token: The provider sends a static secret in the Authorization header, and the consumer verifies it before processing the event.
  • Basic authentication: The provider authenticates with an HTTP username and password, which the consumer validates on receipt.
  • Allow/block requests based on the sender IP address: Some providers publish a list of fixed IP addresses from which webhook requests are sent. The consumer can inspect the source IP address and allow or reject the request based on that list.

Also store tokens, passwords and keys in specialized secret managers like AWS Secrets Manager, do not hardcode them in your code and do not push to repositories.

Following these methods, will help you to build secure webhooks.

Validating the payload

Treat every webhook payload as untrusted input. Validate it before processing or storing it in your database.

Webhooks payload might change or be irrelevant for your specific use-case, it’s better to parse incoming payloads into explicit validator classes or DTO objects before business logic runs. This helps ensure that the application works with a known and predictable data shape instead of passing around raw JSON dictionaries or loosely typed objects.

Payload validation confirms that the request body has the structure and required fields that the consumer expects.

If payload validation fails, it is a good practice to return return a client error such as 400 Bad Request and log the error.

Idempotent webhook consumer execution

Webhook providers may deliver the same event more than once. This can happen because of retries, network issues or provider-side wrong delivery. A webhook consumer should treat repeated delivery as a normal case.

The consumer should use an idempotency key to detect whether the same event has already been accepted or processed. In many webhook integrations, the provider includes an event ID that can be used for this purpose.

A practical pattern is:

  • Build a key from the event identity: Use the provider event ID from the webhook payload.
  • Store processed keys: Keep the key in cache storage or database table.
  • Discard duplicates safely: If the same event arrives again and the key already exists, skip the business logic.
  • Return a successful status for duplicates: Respond with 200 OK for already handled events so the provider does not keep retrying delivery because of a non-2xx status.

When using a cache, make sure the key’s lifetime is at least as long as the provider’s retry window. For stronger guarantees, especially for financially sensitive operations, store idempotency records permanently or enforce uniqueness directly in the database.

Consistent state

Webhook events can arrive out of order. An older event may be delivered after a newer one, so the consumer should not always overwrite the current state with the latest received payload.

Store the event details and the event timestamp when it is available.

Before updating a record, check whether the incoming event represents a newer state. Do not move a completed payment back to pending because of a delayed webhook delivery.

For important updates, change the business record and save the idempotency record in the same database transaction. This prevents cases where the event is marked as processed but the business update was not saved.

Define valid state transitions in the application. This makes unexpected provider events easier to detect and log.

Recoverable processing

A webhook request should not depend on long business logic finishing before the provider receives a response.

Save the validated event in durable storage first, then return a successful response. Process the saved event in a background worker.

Keep a processing status for each event. If processing fails, store the error and retry the event later.

Retries must use the same idempotency rules. A retry can happen after part of the work was completed, so the consumer should be able to continue safely without creating duplicate records or actions.

You should be able to see failed events. Add logs and monitoring for webhook endpoints, and provide a way to replay a specific event after the issue is fixed.

With this approach you can recover failed events and process them again.

Testing webhooks locally

A webhook provider must be able to send requests to a public URL. A backend running on your local machine is not accessible from the internet by default.

We can use a tunneling tool such as ngrok or localtunnel to expose your local server through a temporary public URL. For example, if your application runs on port 8000, the tunnel will provide a URL that forwards incoming requests to your local backend.

Add the generated URL to the webhook settings in the provider dashboard. Include the webhook path used by your application.

You can then trigger events from the provider and inspect the requests in your local logs. This is very useful feature for local development and testing.

And finally, you can test your consumer even without configuring a tunnel, in the end a webhook is just an HTTP POST endpoint.

Copy a sample payload from the provider documentation and send it directly to your local endpoint using Postman or curl. You can test everything by sending invalid payloads or checking authentication mechanisms even without a dashboard.

Code examples

You can check complete webhook consumer examples in Go, Java and Python.

The examples show how the concepts from this guide can be applied in a real application. Use them as a reference and adapt the request validation, signature verification, idempotency and processing flow to your provider and project.

Final words

Webhooks are a powerful integration mechanism and should be implemented carefully, with reliability, efficiency and data safety in mind.

Apply these patterns when building a production webhook consumer to handle real delivery issues safely.

Top comments (0)