Application development

Reliable webhooks: prevent duplicate event processing

Receiving a webhook is not enough. Understand signatures, duplicates, ordering and the gap between acknowledgement and completion through a testable model.

  • Application development
  • 4 min read
  • 10. 09. 2026
  • practical recommendations for business IT
Editorial card with Slovak webhook text and two messages leading to one result.
Editorial illustration with Slovak text.

An order system receives a notification that a payment status changed. It processes the notification, but its response is lost in transit. When the same notification arrives again, a poorly designed integration may create a second warehouse task. This is a model scenario, not a customer story.

A webhook is a notification one service sends to a prepared endpoint in another application when an event occurs. It lets systems react without constantly asking for changes. Reliability, however, requires more than returning HTTP 200. Authenticity, acceptance and completion of the business operation are separate questions.

Development and integration of business applications reliable webhooks
A custom application should simplify a specific process, handle data securely and fit the company environment.

Message delivery is not a completed order

An HTTP response acknowledges a request's result according to the provider's contract. In an asynchronous design, a message can be safely accepted while the actual work waits in a queue. A company therefore needs two views: whether the notification arrived and whether the requested operation happened.

An integration design should distinguish accepted, processing, completed and failed items. These states are a design aid, not mandatory Stripe or GitHub interface labels. What matters is that support can explain a stalled order and safely resume processing.

Verify the sender first

GitHub recommends HTTPS and a webhook secret. Implement signature verification exactly as the particular provider specifies. Knowing a public endpoint URL does not prove sender authorisation. Secrets do not belong in URLs, ordinary logs or customer examples.

For Stripe webhooks, preserve the original request body for signature verification. Arbitrarily changing JSON before checking can break verification. This does not mean raw bodies should be archived indefinitely in application logs. Design access and retention around the integration's needs.

A valid signature is not the whole check: next verify event type and whether the application supports it. Before introducing a new type, test expected and unknown content. An unknown field is not necessarily an attack, but it must not silently trigger an unauthorised business action.

One event may arrive more than once

Stripe explicitly warns about duplicates and does not guarantee delivery order. Do not infer identical behaviour for every provider. Each integration has its own retry and recovery rules that need checking in documentation.

Idempotent processing means repeating the same intended operation does not create another unwanted effect. Simply asking “does this ID already exist?” before writing without protection is insufficient. Two concurrent requests can both receive a no. We therefore suggest database uniqueness combined with transactional processing.

Event identity and business operation identity are different. Two distinct events can concern the same order. Besides delivery deduplication, a rule is needed for permitted order state transitions. An older message should not overwrite a newer confirmed state without checking.

A model with two deliveries and one result

Imagine a synthetic event called event-demo-001. It arrives twice. The following is an explanatory design, not deployable code or a real provider event format:

  1. Verify the signature, type and basic data.
  2. Persist an inbox item with a unique key comprising provider, account and event ID.
  3. Acknowledge acceptance only after safe storage. Redelivery must not create a second inbox item.
  4. Process the item in the background, protecting the business change against concurrency and repetition.
  5. Record the outcome and allow controlled retries after errors.

If the process crashes after acceptance but before processing, the persisted item must remain discoverable. Recording “we have seen this ID” is insufficient if the work never finished. When making another request to an external system, repetition must also be handled at that boundary.

An idempotency key for outgoing Stripe API requests is a different tool from tracking incoming webhooks. One does not automatically replace the other. Similarly, the Competing Consumers pattern explains why queue processing must consider idempotency.

A practical integration acceptance test

In a test environment, send the same synthetic event twice, including concurrently. Then simulate a queue worker failure and resume processing. Finally, change the order of related events. Define the expected business outcome beforehand; otherwise, success cannot be evaluated.

Record test identifiers, states and results without secrets or unnecessary personal data. A green endpoint response is not a sufficient criterion. The test should also show whether the issue reaches someone able to act.

What to request from the supplier

Ask for an explanation of signatures, duplicates, failure recovery and monitoring of unfinished events. Assign an operational owner and a safe retry procedure. Through custom application development and API integrations, Yenwa can start by tracing one critical event from receipt to its outcome in the target system.

Sources and further information

  1. Receive Stripe events in your webhook endpoint — Stripe
  2. Best practices for using webhooks — GitHub
  3. Idempotent requests — Stripe
  4. Competing Consumers pattern — Microsoft

Do you want to solve a similar topic in your organisation?

The article is a good starting point. If you want a concrete plan for licences, accounts, cloud, security or school IT, send us an enquiry.