Webhooks and ERP: prevent duplicate orders
Separate event identity from order identity. A worked example covers retries, concurrent delivery and a lost ERP response.

Treat repeated notifications as a normal condition of your shop integration. A webhook is an HTTP message initiated by the sending system. If acknowledgement is missing, the message may arrive again. That must not create an additional ERP order.
Stripe documents repeated events and the possibility of separate events about the same object. Check the actual sender's rules. The following example is fictional and does not describe the Stripe API.
Two identifiers with different jobs
Shop shop-1 has order W-240. Event evt-17 reports its creation. The ERP gives the order identifier E-901. The event ID identifies a repeated delivery; the shop and order IDs together identify the business transaction. A later evt-18 can report a change to that same order.
Incoming message or failure | Expected result |
|---|---|
First evt-17 | Create ERP order E-901 and map it to W-240. |
Repeated evt-17 | Recognise the existing transaction; no further order. |
Two simultaneous copies of evt-17 | Enforce uniqueness so only one processing job is created. |
evt-18 for W-240 | Associate the change with the existing order and assess it under the change rules. |
ERP creates the order but its response is lost | Outcome initially uncertain; reconcile using the same transaction identity before creating again. |
Persist acceptance
First authenticate the message using the sender's verification procedure. Then store the event durably before acknowledging acceptance. A uniqueness rule on sender and event ID prevents simultaneous copies from creating separate jobs. A lookup before insertion alone is insufficient under concurrency.
Further processing must reliably follow from this durable store. A worker can pick up saved, pending events, for example. The transaction then remains discoverable after a crash between receipt and processing.
One possible response after persistence is:
{
"eventId": "evt-17",
"orderId": "W-240",
"status": "queued"
}This example response uses HTTP status 202. It acknowledges entry into processing; it does not confirm an ERP order. If durable acceptance cannot be ensured, the receiver must not report successful acceptance.
Protect the ERP call against repetition too
Use a stable creation key such as shop-1:W-240:create if the destination supports such keys. Keep it unchanged for a safe retry. Stripe demonstrates this principle for its own API; exact semantics and retention depend on the destination.
Without that support, another reliable mapping is needed, such as an external order identifier whose uniqueness is enforced at the destination, plus an outcome lookup. If a timeout leaves creation uncertain, keep the transaction pending reconciliation. A new random identifier would make a retry look like a new creation.
Event storage and order creation in two systems do not automatically form one transaction. Track acceptance, processing, uncertain outcome and confirmed completion separately. In this example, creation is complete only once the mapping to E-901 is confirmed.
Accept the integration with failures included
Resend evt-17 consecutively and simultaneously. Interrupt the response after successful ERP creation. Then test evt-18 as a genuine change. Expect one creation, a preserved mapping and a visible reconciliation status for unknown outcomes. Also define how long identifiers must be retained to cover possible retries.
Sources
Read more
All articles




APIs & Integration with ASES
ERP, CRM and shop cleanly connected

