Shopify Webhooks Explained (and Why They Fail in Production)
July 28, 2026 · Wizovia
Shopify Webhooks Explained (and Why They Fail in Production)
A webhook is Shopify's way of telling your code that something happened. Instead of you polling the Admin API every minute asking "any new orders?", Shopify sends an HTTP POST to a URL you registered the moment the event occurs. An order is paid, a customer is created, a product is updated, a fulfillment is created, an app is uninstalled: each of these can fire a webhook carrying a JSON payload that describes what changed.
That is the whole idea, and it is genuinely useful. It is also where a lot of production bugs live, because a webhook is a network call from someone else's server to yours, and every assumption you make about it being reliable, in order, and delivered exactly once is wrong.
How a Shopify webhook actually works
When you subscribe to a topic, you give Shopify a topic name (orders/paid, orders/create, fulfillments/update, app/uninstalled, and so on) and an endpoint URL. From then on, when that event happens, Shopify makes an HTTPS POST to your URL.
A few properties matter for anyone building against this:
- The payload is a snapshot, not a live handle. It reflects the state at the moment the event fired. By the time you process it, the underlying object may already have changed again.
- Delivery is not guaranteed to be in order. Two updates to the same order can arrive out of sequence. Do not assume the second POST you receive reflects the newer state.
- Delivery is at-least-once, not exactly-once. Shopify can and will send the same event more than once. Your handler has to expect duplicates.
- Shopify retries on failure. If your endpoint does not answer with a fast success response, Shopify treats the delivery as failed and retries over a period of time before giving up.
- If deliveries keep failing, the subscription can be removed. Persistent failures lead Shopify to stop sending, at which point you are silently blind to events until someone notices.
That last point is the one that turns a small bug into a data-integrity incident.
Why they fail in production
Most webhook failures are not exotic. They come from a handful of predictable mistakes.
You did too much work before responding. Shopify expects a fast acknowledgement. If your handler verifies, parses, writes to a database, calls a third-party API, sends an email, and then returns, you are gambling that all of that finishes inside the timeout window. Under load, or when a downstream API is slow, it does not, so Shopify records a failure and retries, and now you are processing the same event repeatedly while also looking unreliable.
You skipped HMAC verification. Every genuine webhook carries a signature header that is an HMAC of the raw request body, computed with your app's shared secret. If you do not verify it, anyone who learns your endpoint URL can POST fake events and your app will act on them. The common bug here is verifying against the parsed or re-serialized body instead of the exact raw bytes Shopify sent. The signature is over the raw payload, so any framework middleware that reads and rebuilds the body before you compute the HMAC will break verification.
You assumed exactly-once delivery. Without idempotency, a duplicate orders/paid webhook charges a customer twice, sends two shipping labels, or increments a counter twice. Shopify sends a unique event identifier with each delivery. If you do not record which identifiers you have already handled and short-circuit repeats, duplicates become real-world mistakes.
You trusted ordering. Code that assumes a create arrives before an update, or that the latest POST is the freshest data, will corrupt state the first time the network reorders two messages. The safe pattern is to treat the payload as a hint that something changed, then re-fetch the authoritative record from the Admin API when correctness matters.
Your endpoint had a bad day and nobody knew. A deploy that returns errors for ten minutes, an expired certificate, a downstream outage that makes your handler time out: any of these produces a run of failed deliveries. If you are not watching, the missed events are simply gone, and your database quietly drifts out of sync with Shopify.
How to build webhooks that survive
None of this is hard once you accept that the transport is unreliable. The pattern that holds up is the same one used for any at-least-once message system.
- Verify first, on the raw body. Compute the HMAC over the exact bytes received, compare it to the header, and reject anything that does not match before you parse or trust a single field.
- Acknowledge fast, process later. Do the minimum inside the request: verify, confirm the topic, drop the payload onto a queue or into a jobs table, and return success immediately. Let a background worker do the slow work of writing records and calling other services.
- Make every handler idempotent. Record the event identifier, and if you have seen it before, stop. Design the actual work so that running it twice produces the same result as running it once.
- Re-fetch when it counts. For anything financial or inventory-related, use the webhook as a trigger and read the current state back from the Admin API rather than trusting a payload that may be stale or out of order.
- Reconcile on a schedule. Because deliveries can be lost entirely, run a periodic job that pulls recent orders, fulfillments, or whatever you depend on, and heals any gaps. This is your safety net for the days a subscription silently stopped.
- Monitor and alert. Track delivery failures and processing errors, and alert a human before a quiet outage becomes a week of missing data. Also subscribe to app/uninstalled so you clean up when a merchant leaves.
Webhooks are the right tool for reacting to Shopify events, and for most stores they work exactly as advertised. The trouble starts at scale and at the edges, where "usually delivered, usually once, usually in order" is not good enough. Treat them as an unreliable stream you must verify, deduplicate, and reconcile, and they become dependable.
If you are already seeing double-processed orders, drifting inventory, or events that vanish under load, that is usually a sign the queue-and-reconcile layer is missing. This is the kind of plumbing Wizovia builds and hardens for merchants who have outgrown a naive handler, and it is almost always cheaper to get right than to debug after the fact.
Fighting chargebacks on Shopify? Our own app, ChargebackWiz, does this work automatically — on a success-fee model.
Talk to us