Skip to main content
A storefront window holding three boxes faces a warehouse holding twelve of the same box, with four separate marker badges sitting in the gap between the two.

Shopify inventory sync problems: state, location, timing and ownership

In the integrations we work on, inventory sync discrepancies usually resolve to one of four things: two systems describing different states, different locations, different moments, or two writers on one field. Here is how to tell which, and what evidence proves it.

Rizwan Qaiser
Rizwan Qaiser·September 14, 2026·11 min read·LinkedIn

A Shopify inventory sync discrepancy is a disagreement between the quantity Shopify reports and the quantity another system reports for the same item, at the same location, at the same moment. In the integrations we work on, four causes account for nearly every discrepancy that turns out to be real. That is our own pattern from our own work, not a platform statistic. The four are:

  • State. The two numbers describe different things. Shopify’s available quantity excludes committed and unavailable units, so it is not a warehouse on-hand count.
  • Location. Both numbers are correct but they belong to different locations. Shopify tracks inventory per location and does not pool it.
  • Timing. Both numbers were true, at different moments. Webhook delivery is neither ordered nor guaranteed, and a batch push has a window.
  • Ownership. Two systems write the same field and the later write wins.

Work through those four in that order before you touch sync frequency. A faster sync repeats a wrong rule more often and destroys the evidence you need to find it.

Sources checked September 2026. Every platform claim below links to Shopify’s current documentation beside the claim.

This guide is for you if you run Shopify alongside at least one other system that holds stock numbers: an ERP, a WMS, a 3PL, Shopify POS, a marketplace connector such as Amazon or eBay, or a supplier feed. Someone has told you the numbers do not match, and you need to decide whether that is a broken integration, a wrong business rule or expected behaviour.

You will need Shopify admin access with inventory permissions, the ability to read the other system’s audit trail or integration log, your bundle or kit definitions and your returns and restock rules where those apply, and a named person for each system who can answer what it believes it wrote and when.

This guide describes native Shopify behaviour and documented Admin API behaviour. It does not describe how any particular app behaves internally, because that cannot be verified from outside. It also cannot be answered by a public storefront scan: a scan sees a product page, not inventory levels, adjustment history or integration logs.

What each Shopify inventory state means and which system writes it

Most reported discrepancies compare a warehouse on-hand number against Shopify’s available number. Those two are supposed to differ. Shopify defines on hand as all the units at a location, and available as the units you can sell, which excludes committed and unavailable stock. In the Admin API, on hand is the sum of the available, committed, reserved, damaged, safety stock and quality control quantity states.

Shopify inventory states and the system that usually writes each one. Confirm the writer against your own stack before acting on this.
StateWhat it countsWhich system usually writes it
On handEvery unit physically stocked at that locationReceiving, stock counts, and integrations that set or adjust on hand
AvailableUnits you can sell. Excludes committed and unavailable units, and excludes incomingShopify, as orders are placed and fulfilled, plus any integration that writes available
CommittedUnits set aside for unfulfilled orders, draft order reservations, and transfers marked ready to shipShopify only. The Admin API cannot adjust or move this state
Reserved, damaged, safety stock, quality controlUnits still stocked but not sellable. The admin groups all four as UnavailableApps and staff, through adjust and move operations
IncomingUnits on the way from inventory transfers or apps. Not sellable until received and changed to availablePurchasing and receiving flows, transfers, and apps
Shopify inventory states and the system that usually writes each one. Confirm the writer against your own stack before acting on this.

Two details cause false alarms often enough that we check them first. The first is that the admin shows one merchant-facing label, Unavailable, where the API exposes four separate states, so a number that looks missing in the admin is often sitting in safety stock or quality control. The second is that Shopify manages the committed state itself through order creation, fulfilment and draft order reservations, and an integration cannot write it. If your ERP expects to own a reservation concept, it has to model that as one of the unavailable states instead.

Agree the definition before you call it a failure. Ask each system which state its number represents. A warehouse on-hand count and a Shopify available count can differ by exactly the committed and unavailable units and both still be correct.

Location mismatches: right number, wrong place

Shopify tracks stock per location, and a location is any place you sell, fulfil or stock product. Dropshipping apps and third-party logistics services can also appear as locations. Shopify states plainly that each location’s inventory is independent and cannot be shared or pooled with other locations.

That has a consequence people often miss. A store-wide total can be perfectly correct while the storefront still says sold out, because customers can only complete checkout for products stocked at locations that deliver to their shipping zone. Shopify’s own wording is that you cannot oversell inventory across shipping zones. Stock sitting at a location with no shipping rate in the buyer’s zone is real stock that no buyer in that zone can reach.

1 item on a pallet feeds separate stock bins labelled Warehouse, Retail store and 3PL. An integration block writes into the Warehouse bin and that arrival carries a warning marker. A dashed boundary called Shipping zone encloses only Warehouse and Retail store, leaving the 3PL bin faded outside it.
Each location holds its own quantity and Shopify does not pool them. Stock held at a location outside the buyer's shipping zone is real stock that the buyer cannot reach.

Order routing is the other half. When an order is placed, Shopify assigns it to a location based on your routing configuration and available inventory. If any single location can fulfil the whole order, the units come from there. If none can, the order is split across locations or the highest priority location oversells the item.

So when a number looks wrong, check the location before anything else. Look at the inventory level for that item at that specific location rather than the product total. In the Admin API this is the InventoryLevel object, which connects one inventory item to one location and carries the quantity states and an updated timestamp. If the integration is writing to a location id that is not the one serving the customer, no amount of retrying will fix the storefront.

Timing: lag, batch windows and the checkout hold

Three timing mechanisms produce discrepancies that look like failures and are not.

The first is webhook coverage. Shopify exposes inventory webhook topics for inventory items and inventory levels, and documents that changes to the committed, reserved, damaged, safety stock and quality control states do not trigger webhooks. An integration that listens only to inventory level updates sees the available quantity fall when an order is placed, but it is never told how that quantity was consumed, and it gets no event at all when units move between the unavailable states. If your reconciliation logic needs to know why a number moved, the order and fulfilment webhooks carry that, and the inventory level webhook does not.

The second is delivery behaviour. Shopify’s webhook guidance states that ordering is not guaranteed within a topic or across topics for the same resource, and that delivery is not always guaranteed. It recommends using the webhook id header to ignore duplicate deliveries, and running reconciliation jobs that periodically fetch data so the app stays consistent. If your integration treats arrival order as business order, an older quantity can land after a newer one and win.

The third is the checkout window. Shopify checks the cart against inventory levels at each checkout step, but inventory is held only when the customer submits payment information, and if a payment fails the hold is released until the customer reaches the payment method page again. Between add to cart and payment submission there is no hold. On a fast-moving item that window is where overselling happens, and it is native behaviour rather than a sync fault.

Add your own batch window to those three. If the other system pushes a full file every fifteen minutes, a fifteen minute disagreement is the design, not a defect. Set a freshness requirement per flow and measure the actual delay before you decide anything is broken.

Write down the expected staleness for each flow. If nobody has agreed how fresh the number must be, every difference looks like a bug and no fix can be accepted as complete.

Ownership: two writers on one field

The hardest discrepancies are the ones where nothing failed. Two systems both wrote a legitimate value and the second overwrote the first.

This happens most often with the available state. A warehouse system pushes an absolute available quantity on a schedule. A second app also writes available. A staff member corrects a count in the admin. Shopify POS staff run a count, and inventory adjustments made with the POS Quick count extension sync automatically to the admin. Each of those is a valid change. Together they are a last write wins race, and the write that loses is often a manual correction made earlier the same day.

Shopify gives you a defence. The inventorySetQuantities mutation sets absolute values and supports compare and set: pass the quantity you expect to be present and the mutation only applies if the persisted quantity still matches, returning an error if it does not. Shopify’s documentation warns that opting out of that check with ignoreCompareQuantity can lead to inaccurate quantities when requests are concurrent. The adjust and move mutations offer the same protection through changeFromQuantity, which fails with a stale error rather than overwriting.

The design rule that follows is simple to state and unpopular to implement. For each combination of item, location and state, name exactly one writer. Everything else reads. Where you genuinely cannot reduce to one writer, use compare and set so a stale write fails loudly instead of silently winning.

How to diagnose the discrepancy, in order

Run these in sequence and stop at the step that explains the difference.

  1. Freeze one case. One variant, one location, one timestamp. Record both numbers and, beside each, the state name that number represents. A discrepancy you cannot state this precisely is not yet a discrepancy.
  2. Compare like for like. Put Shopify’s available against the other system’s sellable figure, or on hand against on hand. Recompute on hand as available plus committed plus reserved plus damaged plus safety stock plus quality control. In our experience many reports end here.
  3. Pull the Shopify adjustment history for that variant. It records date, activity, created by, and the resulting unavailable, committed, available, on hand and incoming values. Shopify shows the last 180 days per variant. In the admin this is Products, then the variant, then Inventory, then the adjustment history for that variant. Anything older than 180 days lives in the Inventory adjustment changes report under Analytics.
  4. Read the created by column. It names the staff member, app or sales channel behind each change. In an ownership dispute this is the column that settles the argument, because it names the writer without anyone having to reconcile two logs.
  5. Query the inventory level for that item and location. In the Admin GraphQL API, read the inventory level for that item and location and ask for the quantity states by name. The quantities field takes a names argument, so request available, committed and on_hand explicitly, and read updatedAt on the same object. Compare that timestamp against the other system’s last successful write for the same item and the same location id.
  6. Pull the other system’s log for the same window. What did it send, when, to which location id, was it an absolute value or a delta, and what response came back? A 200 response is not proof that the intended value persisted.
  7. Classify before fixing. Name which of the four causes the evidence supports, and write it down. If the evidence supports none of them, keep gathering rather than pushing another file.

Three item shapes sit across all four causes and are worth naming before you start. A bundle or kit means one sale consumes several inventory items, so the two systems may both be right about different items. An item that is not stocked at a location has no inventory level there at all, which is a different failure from writing to the wrong location. And a refund, cancellation or return restocks on one side before the other hears about it. Ask one more question of the other system while you are there: does it decrement on order, or on pick? Those two answers are hours apart and both are correct.

Do not re-push the feed first. A manual resync overwrites the adjustment trail that identifies the writer and hides the cause until it recurs.

Fixes ranked by how often they resolve the problem

The ordering below reflects what has most often resolved reported discrepancies in our integration work. It is our observed experience, not a measured dataset, and your stack may invert it.

Fixes in the order we try them, with the evidence that closes each one. Observed experience, not a benchmark.
FixWhen it appliesEvidence that it worked
Compare the correct state at the correct locationThe two systems are reporting different states or different locationsBoth numbers reconcile once committed and unavailable units are accounted for
Correct the location mappingWrites land on the wrong location, or on a location with no shipping rate in the buyer's zoneThe storefront availability changes in the affected zone and the inventory level moves at the intended location
Reduce to one writer per item, location and stateTwo systems write the same state at the same locationRoutine changes in adjustment history show a single created by value
Turn on compare and setConcurrent writers you cannot removeStale writes fail with an error instead of overwriting a newer value
Add a reconciliation job on updated timestampsMissed, duplicated or out of order webhook deliveriesScheduled comparison finds and repairs drift without anyone pushing a manual resync
Set the overselling policy deliberatelyThe storefront sells past zero, or hides stock you are holdingOverselling events match the policy you chose rather than surprising the team
Fixes in the order we try them, with the evidence that closes each one. Observed experience, not a benchmark.

On that last row: the continue selling when out of stock setting lets a variant sell at zero and below. Shopify notes it does not apply to orders placed in Shopify POS. POS staff can sell below zero regardless of the setting, and POS warns them before they sell an item that is not available. If you want a buffer instead, hold it in safety stock rather than deflating the available number, so the reserve stays visible to everyone looking at the record.

Shopify inventory sync questions

Why does our warehouse count not match the available quantity in Shopify?

Usually because they are different states. Add committed and unavailable units such as safety stock, damaged and quality control back to Shopify’s available figure before treating the gap as a fault. If the numbers still differ, check that both refer to the same Shopify location.

Why did the store oversell when Shopify showed stock?

Three documented behaviours can produce it. Shopify holds inventory only when the customer submits payment information, so there is a window between adding to cart and paying where two buyers can claim the same unit. The continue selling when out of stock setting allows sales at zero and below. And when no single location can fulfil an order, Shopify can split it or let the highest priority location oversell. Check which of the three applies before you assume the integration inflated the number, and check the adjustment history for a write that raised available just before the oversell.

Should we sync inventory more often?

Only after you know the cause. Frequency fixes staleness. It does not fix a wrong state definition, a wrong location or two systems writing the same field, and a faster loop overwrites the adjustment history you need to diagnose those. Set a freshness requirement per flow, measure the real delay, then decide.

Can anyone diagnose this without access to our systems?

No. The evidence lives in Shopify’s adjustment history and inventory levels and in the other system’s integration log, and none of that is visible from a public storefront. A useful diagnosis needs admin access with inventory permissions, the other system’s audit trail, and a named owner for each system who can say what it wrote and when.

Turn the classification into a decision

Once you know which of the four causes you have, the repair is usually a decision rather than code: which system owns this state at this location, how fresh the number has to be, and what happens when two writes collide. Our Shopify ERP integration guide covers how to record those ownership decisions and turn them into acceptance tests before go-live, and the Shopify and NetSuite integration guide works through the same questions for that specific pairing.

Autonomous builds and repairs Shopify integrations and custom apps, including the inventory flows described here. Bring the frozen test case, the adjustment history and the other system’s log, and the conversation starts from evidence rather than from a guess.

Filed under

ecommerce-operationsintegrationsinventoryshopify

Make your Shopify systems easier to operate

Discuss the records, business rules and handoffs your integration needs to support.

Explore integration support
Continue reading
A storefront on the left and a third party warehouse on the right, joined by five paths carrying a pallet, an order card, a parcel, a stock count and a returned item, with operators standing at the handoffs and an exception card below them.

Shopify & Ecommerce

Shopify 3PL integration: handoffs, exceptions and go-live tests

A Shopify 3PL integration is five handoffs and a set of ownership decisions. Name who reconciles a short receipt, who resolves short picks, split locations and address changes, and who grades a return, before the first order ships.

Rizwan QaiserSep 14, 2026
9 min read
Loading page