LUCENTCOMMERCEGET A FREE STORE AUDITFREE AUDIT

INTEGRATIONS · TECHNICAL · DATA · 28 JANUARY 2025 · 8 MIN READ

Webhooks, retries and the orders you never processed

Webhooks are how most Shopify integrations hear about orders. They are also delivered at least once, out of order, and sometimes not at all.

An order moving from storefront to ERP through a queue

A Shopify webhook is an HTTP POST that Shopify sends to your endpoint when something happens — an order is created, a product changes, a customer is updated. Shopify retries a failed delivery up to eight times over a four-hour window, and removes the subscription after repeated failures across a 24-hour period, which means an integration that returns errors quietly can stop receiving events entirely. Webhooks are delivered at least once and not guaranteed in order, so an endpoint that assumes exactly-once, in-order delivery will double-process some records and mis-sequence others.

IN SHORT

  • Webhooks are at-least-once, not exactly-once. Your endpoint must be safe to call twice with the same payload.
  • Delivery order is not guaranteed. An update can arrive before the create it depends on.
  • Persistent failures get the subscription removed — silently, from your point of view.
  • Return 200 fast and do the work asynchronously. Slow endpoints fail on timeout under load.
  • Reconcile against the API on a schedule. Webhooks are a notification system, not a source of truth.

What Shopify actually guarantees

It guarantees that it will try. It does not guarantee that the attempt succeeds, that it happens once, or that events arrive in the order they occurred.

That is not a Shopify shortcoming — it is how webhook delivery works everywhere, and the alternatives are worse. But integrations are routinely built as though delivery is reliable and ordered, and they work in testing because testing sends one event at a time to a healthy endpoint.

They fail during peak, when volume is high and your endpoint is slow, which is the worst possible time to discover the assumption.

At least once means design for twice

The same order-created webhook can arrive twice. If your handler creates a record in the ERP on receipt, you now have two. If it decrements stock, you have decremented twice.

The fix is idempotency, and it is not complicated. Take the stable identifier from the payload — the order id, plus the event topic — and record that you have processed it. On a repeat, return 200 and do nothing. Shopify also sends an event id header you can key on.

The part teams get wrong is where that record lives. If the deduplication table is in the same transaction as the work, it is reliable. If it is in memory, it survives exactly as long as the process does, which during a deploy is not long.

Order is not guaranteed, and it matters more than it sounds

A product update can arrive before the product create. An order fulfilment can arrive before the order. Under normal load this is rare; under burst load it is routine, because delivery is parallel.

Two defences. First, make handlers tolerant: an update for a record you have never seen should fetch the record from the API rather than erroring or creating a half-populated row. Second, use the payload’s own timestamps rather than arrival order when deciding whether an update is newer than what you hold.

Return 200 quickly, then do the work

Shopify expects a response within a few seconds. If your handler calls an ERP, transforms data and writes to a database before responding, it will time out under load — and a timeout is a failure, which starts the retry clock.

The pattern that survives is: verify the HMAC, write the raw payload to a queue, return 200. Everything else happens in a worker that can be slow, can retry on its own terms, and can fail without Shopify knowing or caring.

This also makes replay possible. When a downstream system has been broken for six hours, you want the last six hours of payloads sitting in a queue rather than gone.

  • Verify the HMAC before trusting anything in the payload. An unverified endpoint is an open write path into your systems.
  • Persist the raw body before parsing it, so a parsing bug is recoverable.
  • Return 200 for anything you have accepted, including duplicates you are ignoring.
  • Return 5xx only when you genuinely want a retry. Returning 5xx for a bad payload buys you eight pointless attempts and, if it persists, an unsubscribe.
  • Log the event id and topic on every request, or debugging at 2am is guesswork.

The failure nobody notices

This is the one worth acting on today. If your endpoint fails consistently, Shopify gives up after eight attempts and removes the subscription after repeated failures in a 24-hour period. From your side nothing happens — no error, no alert. Orders simply stop arriving.

The usual discovery route is someone in operations noticing that the ERP has fewer orders than Shopify does, often days later, often during a busy week.

Two things prevent it. Monitor the count of events received per topic per hour and alert on zero, because zero is a number that never occurs naturally on a trading store. And reconcile: on a schedule, query the API for records changed since your last sync and compare against what you hold. Webhooks tell you quickly; the API tells you truthfully.

What to check on an integration you inherited

If you have an integration you did not build, these five questions establish how much trouble you are in, quickly.

  • Is the HMAC verified on every endpoint, or only on some?
  • What happens if the same payload arrives twice — is there a deduplication key, and where does it live?
  • Does the endpoint return 200 before doing the work, or after?
  • Is there a reconciliation job, and when did it last find a discrepancy?
  • Is anyone alerted when events stop arriving?

Questions this raises

How long does Shopify retry a failed webhook?

Up to eight attempts over a four-hour window. If the endpoint keeps failing across a 24-hour period the subscription is removed, and no further events are delivered until it is recreated — which is why monitoring for the absence of events matters more than monitoring for errors.

Are Shopify webhooks delivered in order?

No. Delivery is parallel and order is not guaranteed, so an update can arrive before the create it relates to. Use the timestamps in the payload rather than arrival order to decide whether the data you are holding is stale.

Should an integration use webhooks or polling?

Both. Webhooks for latency, so the common case is fast, and a scheduled reconciliation against the API for correctness, so missed or mis-ordered events are caught. Webhooks alone will drift; polling alone is slow and expensive.

Why verify the HMAC on a webhook endpoint?

Because without it anyone who learns the URL can post arbitrary data to a system that writes into your ERP, your fulfilment or your accounting. The header is a signature of the raw body using your app secret — verify it before parsing, and reject anything that does not match.

NEXT STEP

Free store audit

A senior Shopify engineer reviews your storefront, theme performance and checkout, then sends a prioritised list of fixes.