Shopify Webhooks Missing Orders? How to Build an Order Sync That Survives Retries
A common complaint I hear goes like this: “Our ERP is missing an order from Tuesday.” Everyone assumes Shopify dropped it. Sometimes that is true, but more often the order arrived and something in the receiving system quietly failed.
Whichever it was, the fix is not to hope harder. It is to build the receiver so that a missing, late or repeated webhook does not matter.
What a webhook actually promises
Shopify delivers webhooks at least once. That sentence hides three habits you have to plan for.
First, you can receive the same event twice. If your server was slow to answer, Shopify may send it again even though you handled it.
Second, events can arrive out of order. An update can land before the create you expected, especially when two events happen close together.
Third, delivery is not guaranteed forever. Shopify retries for a limited time when your server fails or times out, and after that the event is gone. Check the current Shopify documentation for the exact retry window, because it has changed over the years.
None of this is a flaw. It is how nearly every webhook system works. The flaw is writing a receiver that assumes none of it happens.
Answer fast, work later
Shopify expects a quick response, on the order of a few seconds. If your handler talks to a slow ERP, a 3PL and an email service before answering, it will time out on a busy day and Shopify will count that as a failure.
So the handler should do almost nothing:
- Check the signature on the request, so you know it came from Shopify.
- Save the raw payload and the event identifier.
- Put a job on a queue.
- Answer with a success status.
Everything else happens in a background worker. In my own stack that means Sidekiq with Redis, but any decent queue does the same job.
Make each job safe to run twice
Idempotent is the word for it. A job is idempotent when running it a second time changes nothing.
In practice, keep a record of which events you have already processed, keyed on the event identifier that Shopify sends. When a duplicate arrives, you notice the key exists and skip the work. For the downstream system, use the Shopify order number or order ID as the key whenever you can, so creating the same order twice updates one record instead of making two.
If you only do one thing from this article, do this one.
Plan for the wrong order
If an update event shows up before you have seen the order, do not crash and do not guess. Fetch the current order from the Shopify API and work from that. The webhook is a hint that something changed, and the API is the truth about what the order looks like now.
A habit that helps: whenever your handler needs the full picture, treat the payload as a signal and read fresh data.
Add a reconciliation job
This is the piece that turns “probably fine” into “provably fine.” Once an hour, ask Shopify for every order created or updated in the last day or two. Compare that list with what your system has. Anything missing gets processed through the same worker as a normal webhook.
Because your workers are idempotent, this job can overlap with real webhooks and nothing breaks. It simply fills in the gaps. It also gives you a clear answer to the question that always comes up after an incident: did we lose anything?
Retries, dead letters and alerts
Workers fail. A downstream API goes down, a field is missing, a product has no SKU. Retry with growing delays, and after a set number of attempts move the job to a place where a person can see it. Then alert someone.
A queue full of silent failed jobs is just a slower way of losing orders. I would rather get a message about three stuck orders on Tuesday afternoon than discover thirty on Friday.
A quick self check
Ask yourself these questions about your current setup:
- If Shopify sent the same order twice, would we create two records?
- If our server was down for an hour, would we notice, and would the missing orders be recovered?
- If a downstream API rejected an order, who finds out, and when?
If you cannot answer one of them confidently, that is where I would start. For a real example of a sync where small data differences caused big downstream problems, see Why Custom Packing Slips Are So Hard.
Want help hardening an order sync? Email kevin@kevinhq.com and tell me what is downstream of Shopify, such as an ERP, a 3PL or a custom app. I will tell you where I would look first.