Home / Guides / Article
Ecommerce operations

Order and Inventory Sync: Keep One Sellable Quantity

Two channels can both accept an order for the same units if they copy a stock number and never reserve it. This guide separates on-hand stock from what a customer is still allowed to buy, then shows a test you can run before a busy week.

Updated: 2026-11-12 Β· TinyBuild

A stock sync fails when two places can sell the same unit. The website, a marketplace, a wholesale sheet, or the warehouse count each keep a number, and each number is treated as "what we can sell." This guide is about which number is allowed to change when an order starts, and how to test that with three orders. It is not a catalog of inventory apps, and it does not assume your store is on any one platform.

If you are about to run a promotion, the Black Friday technical checklist covers delayed and repeated order messages. This page is the step before that: deciding which quantity those messages are allowed to change.

The question a sync has to answer

Before you connect anything, write down the question the sync is for:

After an order starts in one channel, can another channel still sell those same units?

"Starts" matters. A paid and shipped order is too late to reserve stock. A checkout that is still waiting for payment, a marketplace order that is accepted but not shipped, and a wholesale line typed into a sheet are all moments when the sellable quantity has to move.

A morning export cannot answer that question. It is a copy of whatever the source system believed at export time. The next order, in either channel, needs the quantity after that copy was made.

Five quantities, not one stock number

Shopify's help center documents five inventory states. The names are Shopify's, and the split is useful even when the stock record is a spreadsheet.

StateWhat it meansCan a new order take it?
On handUnits physically at a location. Shopify defines this as committed + unavailable + available.No. This is a total, not an offer.
AvailableUnits you can sell. Not committed, not unavailable, and not incoming.Yes. This is the number a channel should offer.
CommittedUnits set aside for an unfulfilled order, a reserved draft order, or a transfer marked ready to ship.No.
UnavailableUnits at the location that are held back, such as damaged stock, quality control, or safety stock.No.
IncomingUnits on the way. Shopify says they are not sellable until received and moved to available.No, unless you deliberately sell against a future delivery and can name that rule.

Shopify also notes that editing the on-hand quantity changes available by the same amount, and editing available changes on hand by the same amount. A sync that "sets stock to the warehouse count" is editing a total. If that total already excluded orders another system had committed, available moves twice.

From 1 October 2026, Shopify's inventory reports measure stock by on-hand quantity instead of available. On hand includes committed and unavailable units, so a report can show a higher number than the older available figure. Figures from before that date stay on the available basis. If a spreadsheet was built from an older export, compare it with on hand only after you know which column the export used.

A worked example with two channels

This example is hypothetical. The counts are chosen so the failure is easy to see. It is not a client result.

A small parts seller has 12 filters at one location. Nothing is committed or unavailable, so available is 12. At 08:00 both the website and a marketplace read 12.

  • 09:10 a website checkout starts for 4 filters and is still unpaid. A correct reserve changes available to 8. The marketplace still shows 12, because it cached the morning file.
  • 09:20 the marketplace accepts two orders, 6 and 6, against that cached 12.
  • 09:30 the website payment completes for 4.

Units sold: 16. Units on the shelf: 12. The website may have done its own reserve correctly. The oversell came from the second channel selling a copy of available, not from a wrong product title or a failed payment.

The same 12 filters do not oversell if the marketplace has to ask the system that owns available, and that system commits units when the marketplace order is accepted. The second order for 6 then sees 2 left, not 12.

What Shopify and WooCommerce already reserve

You do not need a custom sync to get this split inside one store admin. You need one when a second channel never asks that admin.

On Shopify, an unfulfilled order, a reserved draft order, or a transfer marked ready to ship increases committed and reduces what is available. Incoming stock stays out of available until it is received. If a product is allowed to keep selling at zero, another order can still be accepted. Check that setting before you treat a negative available count as a broken integration.

WooCommerce documents a separate hold for unpaid checkout. With stock management on, Hold stock (minutes) keeps units for an order in Pending payment. When the limit is reached, that pending order is cancelled and the held stock goes back. WooCommerce says this hold applies to Pending payment, not to orders in On hold. Leave the minutes blank and the hold is off.

WooCommerce's order statuses then change what "held" means. Processing means payment has been received and stock has been reduced. On hold means the order is waiting for you to confirm payment, and stock has already been reduced, which is common for bank transfer. Cancelled returns line-item stock when stock management is on. Failed returns stock if it had already been reduced. A refund is a payment action. Do not assume it puts units back on the shelf unless the return process says so.

Those rules apply inside that store. A marketplace, a shop on another platform, or a sheet edited by hand does not inherit them.

Where a second system still double-sells

These are the breaks worth fixing before you add another sales channel.

  • The sheet is on hand, the store is available. Warehouse staff count boxes. The website is selling what is left after unpaid and unfulfilled orders. Copying one number onto the other creates or hides a reservation.
  • Two owners. The store reduces stock at payment, and a script also sets stock from the marketplace every hour. Each write assumes it is the only writer.
  • The same order arrives twice. A repeated webhook or a repeated marketplace notification creates a second reservation. The API guide covers why a stable order id matters. Here the effect is two commits for one sale.
  • A cancel and a refund take the same path. An unpaid cancel should release a hold. A shipped refund should restock only when the unit actually comes back.
  • One location in the sheet, two in the store. Available is per location. A single cell cannot represent both unless you name which location it is.

A sales dashboard does not fix this. It tells you what already sold. The sales dashboard guide is for that later question. If the only stock record is a workbook several people edit, the Excel-to-web-app guide is about replacing the workbook. An inventory sync can still start from that workbook if you stop letting it be a second seller.

A test you can run with three orders

Use a test product with a quantity you can afford to mess up. Write the four numbers before you start: on hand, committed or held, unavailable, available. Write the number the other channel is currently offering.

  1. Unpaid or unfulfilled. Start an order for 2 and do not ship it. Available, or the WooCommerce hold, should drop by 2. The other channel should no longer offer the old available count. If it still does, it is selling a copy.
  2. The same order again. Replay the paid event, or submit the same external order id again. Committed quantity should stay on one reservation. A second reservation means a repeated message was treated as a new sale.
  3. Release the unpaid order. Cancel the pending checkout and wait for the hold to end. Those 2 units should be offerable again. Then, on a separate fulfilled test order, refund or restock only through the path you use in real life, and record whether available actually increases.

Save the three outcomes next to the order ids. "The plugin is installed" is not a result. The result is whether available moved once per real order.

When a connector is enough, and what to build first

Stay on the store's own inventory if every sale already commits there. That includes a marketplace or point-of-sale channel that sells through the same admin and the same locations. A custom sync is worth a quote when a second system accepts orders without asking for available, or when people correct stock by typing a total over the top of committed orders.

The first slice is a ledger for one product family at one location, not an ERP.

  • One row per external order, stored once under that channel's order id.
  • Available calculated as on hand minus committed minus unavailable. Incoming stays out unless you have written the preorder rule.
  • A list of orders that did not get a reservation, so someone can see them without opening two admins.
  • No purchasing, accounting, or multi-warehouse optimization in this slice.

A clickable ledger with sample products can be shown in about three days. Connecting it to a live store, including the real order ids and the failure list, is a separate scope. Bring the two places a sale can start, an example of the stock record you trust, and what should happen on cancel versus refund.

FAQ

What is the difference between on hand and available?

On hand is the physical total at a location. Available is the part a new order may take. Shopify defines on hand as committed plus unavailable plus available. Incoming is not included in on hand and is not sellable until it is received.

Does WooCommerce hold stock for every unpaid order?

The Hold stock setting applies to Pending payment orders from checkout, for the number of minutes you enter. It does not apply to On hold. On hold already has stock reduced, and you still need to confirm payment. Blank minutes disables the hold.

Can a spreadsheet and a website share one quantity?

Only if one of them is allowed to change available, and the other reads that result. Two editable copies of "12 in stock" will both sell the 12. A sheet can remain the warehouse count if it is treated as on hand and it does not overwrite committed orders.

When is a custom inventory sync worth building?

When a second channel accepts orders without reserving the quantity your store calls available, or when stock corrections type a new total on top of orders that are already committed. A sale that already stays inside one admin usually does not need a second ledger.

What should the first version include?

One product family, one location, one reservation per external order id, an available quantity that excludes committed and unavailable units, and a visible list of orders that failed to reserve. Purchasing and accounting can wait.

How is this different from the Black Friday checklist?

The checklist tests a live buying path: payment, discounts, repeated messages, and who fixes a failure. This guide decides which stock number those messages should change so two channels cannot sell the same unit.

Sources and scope

Inventory state names and the on-hand formula follow Shopify's inventory states documentation, including the note that editing on hand changes available by the same amount. The 1 October 2026 change from available to on hand in inventory reports is from Shopify's inventory reports page. WooCommerce hold-stock behavior is from WooCommerce product settings, and the Processing, On hold, Cancelled, and Failed stock notes are from WooCommerce order statuses. Pages were checked on 12 November 2026. Platform settings change, and apps can alter them. This is an operations guide for a small team, not a promise that a particular app prevents overselling.

Which two systems both think they own the quantity?

Tell us where a sale can start and which record your team trusts today. We look at that flow and quote a first slice that keeps one available quantity. Consultation is free. A focused demo uses sample products; a live connection to your store is scoped separately.

Share your idea β†’