Duplicate Webhooks: Stop the Same Order Appearing Twice in Your CRM
Webhooks can arrive more than once. Design retries, deduplication and CRM writes so one payment does not create duplicate orders or notifications.

A payment succeeds, but your server's response gets lost. The payment provider sends the notification again. If every delivery creates a CRM order, you now have two orders for one purchase.
Turning off retries is not the fix. Make the operation idempotent: repeating the same request should not create an additional effect. This matters just as much when an AI agent adds a note, assigns a task or calls another tool in the workflow.
A notification is not a one-time delivery
A webhook is a request another service sends to your system when something happens. It does not promise that the message will arrive only once or in the order you expect.
The Stripe documentation describes automatic retries, possible duplicate events and out-of-order delivery. In live mode, automatic delivery attempts can continue for up to three days. These are Stripe's documented behaviors, not universal rules. Check the documentation for each integration you use.
The useful requirement is not “we receive every message once.” It is “each confirmed payment creates one order, even if the notification arrives again.”
Example: from payment to CRM
Imagine a workflow that records an order, creates a sales task and sends a confirmation. This is a design example, not a report of a client test.
Before writing code, define two different identities:
- The event identifier, to recognize another delivery of the same message.
- The business operation key, to recognize the same intended result even when different events request it.
For an order, a key could combine the provider account, payment identifier and the action “create order.” Do not use only the customer's email address. One person can make two legitimate purchases.
Likewise, do not merge every event about a payment into one operation. A confirmation and a refund are different actions. The key must capture the intent, not just the object.
Keep a record that survives retries
A practical design separates receiving an event from processing it:
- Verify the webhook's signature and authenticity using the provider's instructions before trusting its contents.
- Store the event durably, with a unique constraint on its identifier scoped to the provider and account.
- Acknowledge receipt only after durable storage succeeds. If you cannot save it, do not claim to have accepted it.
- Let a separate worker process pending events and apply business rules.
- Record results, CRM identifiers and the status of each effect so interrupted work can resume.
“Search first, then create” is not enough. Two workers can search at the same time, both find nothing and both create an order. You need a unique constraint or an atomic reservation of the operation key.
Track at least pending, in-progress, completed and failed work. An event that has been recorded but not completed must not simply be discarded. It may be the work you need to recover after a crash.
The hard part: did the CRM already create the order?
The CRM might create an order before the connection drops, leaving you without its identifier. Retrying blindly can create a duplicate. Marking the work complete before the call can lose it instead.
If the CRM supports idempotency keys, reuse the same key for every attempt at the same operation, following its retention window and constraints. Otherwise, check whether it supports a unique external identifier and an update-or-insert operation based on that identifier.
If neither option exists, you need reconciliation: inspect the external state and resolve uncertain outcomes. A local tracking table alone does not justify an “exactly once” promise.
Emails and sales tasks are separate effects too. One order does not guarantee one email. Give each important effect its own key and status. AWS's guide to idempotent APIs explains why identifying intent explicitly makes retries safer.
Where AI fits
An agent can classify a request or suggest confirmation copy. It should not decide whether a payment has already been recorded by recalling a conversation.
Agent memory is not an operations ledger. Give the agent a tool that enforces the checks on the server. A second attempt should retrieve the existing order, not rely on a prompt saying “do not do this twice.”
Tests to request before release
- Deliver the same event twice. Is there still just one order?
- Deliver two copies simultaneously. Does the unique constraint hold?
- Stop processing after the CRM creates an order. How do you recover its identifier without creating another?
- Deliver events out of order. Do you consult the authoritative state when needed?
- Resume a failed event. Do you complete only the missing effects?
- Check ledger retention against retry windows. How long can you recognize a duplicate?
These are not tests Studio Futuro has run on this integration. They are acceptance criteria for your workflow, to be applied in a test environment with non-sensitive data.
To start with the process rather than the tools, read our AI audit for small businesses. For repeatable agent procedures, continue with DevOps skills.
Your next step
Pick one automation and write down what should happen if the same request arrives twice. If the answer is “hopefully it won't,” the system is still missing a rule.
Talk to us about designing a workflow with checks and error recovery.
In short: Receiving an event once is not the goal: each important action must be safe to repeat without creating a second result.
Newsletter
AI that actually works, in your inbox.
New pills, the tools we use and what we learn on real projects. Few emails, only when there is something useful.


