Skip to content

Webhooks and ERP: prevent duplicate orders

Separate event identity from order identity. A worked example covers retries, concurrent delivery and a lost ERP response.

Tobias Krüger2 min read
Bell attached to a webhook symbol, with a curved blue retry arrow.

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

Written by

  • Tobias KrügerEditor for Systems IntegrationTobias Krüger covers how different software systems work together at ASES. In his articles he explains how ERP, CRM and online shops are connected and how to deal with errors in data exchange.

Follow ASES

Read more

All articles

APIs & Integration with ASES

ERP, CRM and shop cleanly connected