Stock reservations & holds
How stock is held from the moment an order lands to the moment it ships — hold states, what kits and drop-ship lines do differently, why Shipped is not what moves stock, and how holds go stale.
A reservation — a hold — is Foundry’s claim on stock for one order line. Holds are what stop two channels selling the same unit, what a pick list is built from, and what turns into a stock movement when an order ships.
Almost every inventory question that looks strange has an answer somewhere in this lifecycle: a SKU that reads as reserved with nothing open, a kit whose parts print oddly on a pick list, a shipped order whose stock never came down.
Where holds come from
Orders create holds. Work orders do not.
This is the single most common misconception, and it points the wrong way round. Every order line places a hold the moment the order lands — from a channel webhook, an order sync, or Storefront API checkout. Simple items included. There is no admin-side order creation, so there is no other route in.
Releasing a work order reserves nothing; Release only moves that work order from Draft to Released. If anything the arrow runs the other way — a new hold on an assembly is what can trigger a work order to be created.
One exception, and it is narrower than it sounds: a variant with inventory tracking switched off still gets a hold row, but that hold does not increment reservedQuantity. The claim is recorded; it just doesn’t count against sellable stock.
What a hold does to your numbers
| On hand | physically in a warehouse, across every bin |
| Reserved | the sum of open holds |
| Available | on hand − reserved — what channels are told, and what inventory.availability_changed reports |
So a hold does not move stock. It reduces what you can sell, not what you have. Nothing leaves on-hand until the order is fulfilled.
The states a hold passes through
| State | Meaning |
|---|---|
PENDING | placed, not yet assigned to a source |
ALLOCATED | assigned to a specific supplier or warehouse, awaiting fulfilment |
COMMITTED | fulfilled — the stock has moved (or was drop-shipped) |
CANCELLED | released without shipping — order cancelled, refunded, or the line removed |
PARTIAL | prepared against a partial refund but not fully used |
PENDING, ALLOCATED and PARTIAL are open — they count toward Reserved. COMMITTED and CANCELLED are finished and count toward nothing.
The distinction that matters most: COMMITTED means the stock already moved. A committed hold is never released, never re-committed, and never counted again. A refund arriving after the goods shipped is a returns question, not a hold to free.
Shipped is a label. Fulfilled moves stock.
Changing an order’s status to Shipped — by hand, or from a channel’s own status — does not touch inventory. It records what the channel says.
Mark Fulfilled, or a shipment synced back from ShipStation, is what draws stock down: out of the warehouse that can cover the order, each SKU’s primary bin first, then the fullest. That is what commits the hold and writes the SALE movement you see in activity history.
This is deliberate — a channel flipping a label should never silently move your inventory — but it does mean a shipped-status order can still be holding stock. See When holds go stale below.
Kits and assemblies differ at the hold
A bundle (kit) holds no stock of its own — it is a virtual SKU that exists on no shelf. An order line for a kit therefore places no hold on the kit. Foundry flattens it to leaves and holds each component, at per-kit quantity × line quantity, attributed back to that order line. Nested kits flatten all the way down.
An assembly is the opposite: the finished unit is a real thing on a real shelf, so the hold sits on the assembly itself. Its components are consumed when it is built, on the work order, not when it is sold.
That difference is why the two print differently on a pick list: a kit prints as a heading with its parts listed beneath, and an assembly prints as one line.
Drop-ship lines
A line the supplier ships direct never enters your warehouse, so there is nothing to deduct. On fulfilment its hold is committed without any stock movement, and the activity log says so.
A hold counts as drop-ship if it has been allocated to a supplier flagged as a drop-shipper, or already carries a purchase-order line. The purchase order alone is not the test — it is created later, and a hold allocated to a drop-shipper with no PO yet would otherwise be treated as stocked and deduct inventory you never held.
What the pick list reads
A pick list is built from the holds, not from the bundle or BOM as it stands today.
That is worth knowing because it has a consequence: change a kit’s recipe after an order has landed and the existing holds still describe the old one, so the pick list does too. Foundry rebuilds an unshipped order’s holds when its bundle or BOM changes, but an order that has already shipped keeps the holds it was picked against — which is correct, because that is what actually left.
Rows are ordered by bin, so the list follows a walk through the warehouse rather than the order the holds happened to be written.
When holds go stale
A hold should end when its order does — fulfilment commits it, cancellation releases it. Two things break that:
- an order that shipped before hold commitment existed, or whose fulfilment webhook was missed entirely; and
- a fulfilment pass that ran and skipped one hold — a timeout, a transient failure — leaving the rest of the order committed.
Either way the hold stays open forever, inflating Reserved and understating what you can sell on every channel. Because build demand reads open holds and not order status, a shipped order can also keep appearing as a customer waiting on a build.
Settling them is one action, and it applies the right remedy per hold:
- Committed when the order shipped and some of its other holds are already committed. That combination is evidence a fulfilment pass ran on that order and missed this one, so the units left the building and the stock has to come down — as a real
SALEmovement against the order, not a silent adjustment. - Released otherwise. A cancelled or refunded order never shipped the goods, and a shipped order with no committed sibling gives no evidence a pass ever ran, so what happened to the stock is unknown. Deducting on a guess would invent shipments.
Run it as a dry run first: it reports exactly which holds it found and how they split between the two.
One caveat worth being blunt about. Releasing a hold frees the claim, it does not draw stock down. If units genuinely shipped and their holds were released rather than committed, on-hand is overstated and a physical count is what trues it up.
Working it out on a real SKU
When a number looks wrong, go in this order:
- Reserved higher than expected? Look for open holds whose orders already finished. That is the stale-hold case above.
- Stock never came down on a shipped order? Check whether it was ever fulfilled, not just marked Shipped — and check the stock timeline for a
SALEmovement. No movement means no fulfilment. - A kit looks unreserved? It will be. Look at its components — the hold is on them.
- Reserved sitting at zero with an order open? Check whether the SKU has inventory tracking switched off, and whether the line resolved to this variant at all.
Related
- Core concepts — where stock, orders and channels sit relative to each other
- Bundles — buildable quantity, component holds, and nested kits in full
- Activity history & stock timeline — every movement against a SKU, with its running balance
- Webhook events —
inventory.availability_changed,order.status_changed,order.shipped