Order Management

One Source of Truth for Inventory Across Stores, a Warehouse and Marketplaces: What It Actually Means

OmniOrders Team |

Retailers ask for one source of truth after the third time a customer bought something that was not there. The phrase usually gets taken to mean one number, held once, shown everywhere. That version gets built constantly, and it drifts every time, because the systems you sell through do not agree on what the number means. What can be built instead is a single position — how many units, at which location, in which state — from which every channel's number is calculated. The distinction sounds academic until you see where syncs break, at which point it is the entire problem.

The four places a unit can be "known"

A single unit of one SKU can be recorded in four different kinds of system at once, each with its own idea of what it is looking at.

The storefront holds a sellable number per location and, depending on how the product is configured, hides the buy button when that number hits zero. The marketplaces hold their own quantities, in their own shapes, and each has its own consequences for a seller who cannot ship. The warehouse system holds quantity by bin, with lots, expiry dates, damage and holds. The stores hold whatever the point of sale reports, which may be a single number per store with no state breakdown, or a full location record if the register shares the storefront's platform, and which changes every time it rings.

Four systems, four records of the same physical unit. None of them is lying. They are measuring different things.

"Available" already means three different things

Take the word "available" and look at how the platforms actually define it. These three publish their models; their own documentation is the source for everything in this section.

Shopify splits every location's stock into states and gives the arithmetic explicitly: the on-hand state "equals the sum of inventory quantities in the following states: available, committed, reserved, damaged, safety_stock, quality_control" (Shopify inventory quantities and states). Its merchant documentation states the same relationship the other way round: "on hand inventory is the sum of your Committed, Unavailable, and Available inventory" (Shopify Help Center). Available is therefore a residual, what is left after orders have claimed stock and after units have been moved into a held state. Notably, the committed state is maintained by the platform and is not settable through the API.

eBay models it differently. A seller sets a total ship-to-home quantity, and separately may distribute quantity across warehouses: the availability distribution container carries a merchant location key, "the quantity of the inventory item that is available at this inventory location", and an expected fulfilment time from that location (eBay AvailabilityDistribution, ShipToLocationAvailability). The documentation does not state a required relationship between the total and the distribution, which makes them two fields that can disagree without either being obviously wrong.

Walmart is per-node by design. Quantity is held for a SKU at each ship node, and the multi-node endpoints retrieve inventory details "across all ship nodes for a SKU" and update inventory "across multiple ship nodes" (Walmart Marketplace inventory documentation).

Three platforms, three shapes: a residual after states, a total alongside a per-location distribution, and a set of per-node quantities. A system that pushes the same integer to all three is not synchronising anything. It is handing the same number to three different formulas. Amazon is deliberately not covered here; the FBA availability model deserves its own treatment rather than a paragraph.

Why one number pushed everywhere goes out of sync

Once you see the shapes, the classic failures stop looking like bugs and start looking like arithmetic.

You move units into Shopify's safety_stock state so your own website will not sell them. Available drops accordingly. Your marketplace connector, reading a raw warehouse total, never sees that those units were set aside, and sells them.

A store associate sells the last two units on a register that reports hourly. For that hour, every channel is selling stock that has left the building. Nothing is out of sync in the software sense; every system faithfully holds the last number it was told.

A customer places an order. Shopify moves stock into committed, which reduces available immediately. Your warehouse system does not consider the unit gone until it is picked. Push the warehouse number back and you overwrite the reservation.

An order is cancelled, a return arrives, a transfer is received. Three events that return units to the pool at three different moments across four systems, and only one of those moments — putaway confirmation in the warehouse — is when the unit actually becomes pickable.

None of this is fixed by syncing more often. Each is a definitional mismatch, and a faster loop propagates the mismatch faster.

Faster syncing is not the fix

There is a sharper point underneath, and the clearest statement of it comes from the platform itself.

Shopify's "inventorySetQuantities" mutation defaults to a compare-and-set operation: you supply the quantity you believe is currently stored, and the update applies only if the persisted quantity matches it. You may opt out, and the documentation warns what happens when you do — skipping the compare check "can lead to inaccurate inventory quantities if multiple requests are made concurrently" (inventorySetQuantities).

Read that warning for what it is. The platform assumes more than one thing may write this field, and hands you a primitive for detecting it. That is a concurrency problem, and syncing faster does not touch a concurrency problem. In our experience most "it keeps going out of sync" reports turn out to be last-write-wins between two systems that both believe they own the number, which changes how quickly a wrong value arrives rather than whether it does.

The fix has a shape: one writer per field, everyone else reads; and where a second writer is unavoidable, compare before setting and treat a mismatch as an event to investigate rather than an error to retry.

What has to be true for a single position

A position is one row of fact: how many units, at which location, in which state, as of when. For that to hold across stores, a warehouse and marketplaces, five things have to be true.

  1. Every location is a real location. A store holding sellable units is a location with its own quantity, not a rounding error against the warehouse.
  2. States are recorded, not just totals. On hand, committed, unavailable and incoming are different facts. A system that stores only a total has to guess at the other three, and it guesses differently from Shopify, which publishes its states and their arithmetic.
  3. Movements are captured where they happen. Receipts, picks, transfers, returns and counts are the events that change reality. Whichever system captures them is the only honest candidate for holding the position.
  4. Each channel's number is derived, not copied. Buffer, location eligibility and channel rules are applied on the way out. The derivation is written down and can be explained after an oversell.
  5. One writer per field. Everything else reads, or compares before it writes.

Meet those and the channels can show different numbers while remaining consistent, which is usually what is meant by the request for one source of truth. Miss the third and you have a very fast copy of somebody's guess.

Where to look first when it drifts

Roughly the order we find them in, not a measured ranking:

  • Two writers on one field. A channel app and your inventory system both push quantity. Find out which one wrote last.
  • Stores treated as a lump. Store stock that sells in real time but reports on a schedule.
  • Buffers applied in two places. Units moved into a held state on the platform and a buffer subtracted again by the connector, or by neither.
  • Returns and cancellations. Units coming back at different moments in different systems.
  • Listings mapped many-to-one. Two marketplace listings on one SKU, each given the full quantity.
  • Failed writes nobody sees. A rejected update is a silent divergence until someone oversells.

Most of these are diagnosable from the position record and effectively undiagnosable from a screen showing one number per SKU. The last one needs something else: a log of the writes that were attempted and refused.

Where an order system sits in this

OmniOrders is an order management system for brands and retailers selling through many channels. The integrations page lists 137 integrations across sales channels, fulfillment, shipping and ERP/accounting, which is the connective part of the problem described above.

Which location should ship a given order is a routing decision, but it is one made on top of this position rather than instead of it: see which location should ship the online order. For the connector layer across channels, see multichannel inventory management, and for the store side, retail inventory management.

Frequently asked questions

Does one source of truth mean every channel shows the same number?

Usually not. Channels define availability differently and set stock aside at different moments, so identical numbers everywhere are worth a second look: either nothing is being held back anywhere, or nothing is being calculated. What should be identical is the position underneath, meaning units, location and state. Each channel then receives a number derived from that position, using its own rules and whatever buffer you have set for it.

Why does my stock keep going out of sync even though it syncs every few minutes?

In our experience the usual cause is not frequency, it is two systems writing the same field. If a channel app and your inventory system both push a quantity without checking what was there first, the later write wins whether or not it was right. Shopify's set-quantity mutation is compare-and-set by default, and its documentation warns that skipping the check can produce inaccurate quantities when requests are made concurrently.

Where should the single position actually live?

In whichever system records the physical events: receipts, picks, transfers, returns, counts. A sales channel knows what it sold, and some model incoming stock as well, but the system that captures the movement is the one to trust on timing, because it knows when the pallet was received and where a unit was moved from. Putting the record there is what lets each channel be calculated rather than copied.

Start your 14-day free trial

See how OmniOrders connects your sales channels, 3PLs, and carriers into one operational layer — free for 14 days, no credit card.

← Back to Blog