Skip to content
All blog
Shopify API Integration Reconciliation

Shopify webhooks and why they are not enough

Chong 7 min read

Webhooks are how a Shopify integration stays current without asking constantly. The store tells you when something changes and you react to it, which is enormously more efficient than polling.

They are also the source of most silent data loss in ecommerce integrations, because their guarantees are weaker than almost everyone assumes.

What Shopify actually promises

Read the guarantees carefully, because each one implies a design requirement.

Delivery is attempted, not guaranteed. Shopify retries a failing endpoint for a period and then gives up. Repeated failures can result in the subscription being removed entirely, which means a webhook that was working can stop existing without anyone doing anything.

Order is not guaranteed. An order-updated notification can arrive before the order-created one for the same order. Code that assumes creation precedes update will break, occasionally and confusingly.

Duplicates happen. The same event can be delivered more than once. This is normal, not an error.

Payloads can be stale. By the time you process a notification, the object may have changed again. The notification tells you something happened; it is not authoritative about the object's current state.

Each of those is reasonable for a platform delivering at Shopify's volume. Together they mean a webhook is a hint that something changed, not a reliable record of what changed.

What that forces you to build

Four requirements, none optional.

Idempotency. Processing the same event three times must produce the same result as processing it once. In practice that means recording which event identifiers you have seen and ignoring repeats, or writing every handler so that reapplying it changes nothing.

Without this, a duplicated order-paid event creates a duplicate journal entry, revenue is overstated, and the cause is invisible because both entries look legitimate.

Order independence. Handle events by their content rather than by their sequence. An update for an order you have never seen should create it rather than fail. This is more robust than trying to buffer and reorder, and it is less code.

Signature verification. Every notification carries a signature proving it came from Shopify. Verify it, on every request, without exception. An endpoint that accepts unsigned notifications accepts fabricated ones, and the consequences run all the way to fraudulent orders in your books.

Fast acknowledgement. Shopify expects a prompt response and treats a slow one as a failure. So the handler must acknowledge immediately and do the actual work afterwards, on a queue. A handler that processes synchronously will start timing out under load, which triggers retries, which increases load — the failure mode is a spiral rather than a slowdown.

The reconciling pull, which is the actual answer

Because delivery is not guaranteed, an integration built only on notifications will eventually be missing records, and it will not know which ones.

The pattern that works is both mechanisms together, doing different jobs.

Notifications for freshness. They keep the system current within seconds and they cost almost nothing.

A periodic full pull for completeness. On a schedule, fetch every order in a recent window and compare against what you hold. Anything present at Shopify and absent locally was a missed notification. Import it and, ideally, record that it was missed — see Shopify bulk operations and when to use them.

That comparison is the single most valuable piece of an integration and the most commonly omitted. It is what makes the difference between books that are complete and books that are complete as far as anybody knows — see keeping automated books healthy.

The same reasoning applies to your payment gateway's notifications, which carry the same caveats — see gateway API or settlement file.

How the failure actually presents

Worth describing, because recognising it saves a long investigation.

Everything works. Orders flow through. Then a month-end total is slightly out — not wildly, by a handful of orders. Nobody can find an error, because there is no error: a few notifications were not delivered during a brief outage at your end, Shopify retried, your endpoint was still down, and eventually it stopped.

Those orders exist in Shopify and do not exist in your books. Nothing anywhere flags a difference, because both systems are internally consistent. The only thing that would have caught it is a comparison, and the only thing that will find it now is the same comparison run over the affected period.

A missing order is invisible by nature. You cannot notice the absence of a record you never knew about, which is why the completeness check has to be structural rather than something someone remembers to do.

What to ask about this

For a vendor, three questions.

Do you use webhooks, a scheduled pull, or both. How do you detect a missed notification. What happens if my endpoint is unavailable for a day.

An answer of webhooks alone is a real gap, and it is one you will discover at a month end rather than at implementation — see why integration depth beats feature count.

Common questions

Are Shopify webhooks reliable?

Delivery is attempted rather than guaranteed. Shopify retries a failing endpoint for a period and then stops, and repeated failures can remove the subscription altogether. Notifications can also arrive out of order, be delivered more than once, and carry a payload already superseded by a later change, so each of those has to be handled in the integration.

Why must webhook handlers be idempotent?

Because the same event can be delivered more than once as normal behaviour. If processing it twice creates two journal entries, revenue is overstated and the cause is invisible since both entries look legitimate. Handlers therefore need to record which event identifiers have been seen, or be written so that reapplying them changes nothing.

Do webhooks alone keep an integration complete?

No. Because delivery is not guaranteed, an integration built only on notifications will eventually be missing records without knowing which. The working pattern is notifications for freshness plus a periodic pull of every order in a recent window, compared against what is held locally, so anything missed is detected and imported.

How does a missed Shopify webhook show up?

Usually as a small unexplained difference at month end — a handful of orders present in Shopify and absent from the books. Nothing flags it, because both systems are internally consistent and you cannot notice the absence of a record you never knew existed. Only a deliberate comparison over the period finds it.


Related: what it takes to build a Shopify connector · Shopify bulk operations and when to use them · keeping automated books healthy


See what you could build

Start a free trial and describe what your business needs in plain language — SmartB Studio builds the module for you.

Start free trial
Get started

No credit card · Cancel anytime · Your data stays yours