Skip to content

Order reservations

When a paid order routes to a location in a multi-location store, Shopify reserves the finished-good variant at that location — but the BOM components it would consume are still sitting in available. Until the order is actually fulfilled, another order can sell those same components out from under it. Order reservations close that window: at routing-complete, Assemblified moves the resolved component quantities from available to reserved at the routed location, and consumes them on fulfillment.

This is an opt-in store-wide setting. Without it, components stay available until fulfillment and are deducted then — the deduct-at-fulfillment behaviour.

  • What it does and why
  • Prerequisites and how to enable
  • The five Shopify webhooks that drive it
  • Lifecycle states
  • How each material kind behaves (Shopify, virtual, pre-assembled)
  • Order edits: added units, removed units
  • Failure handling and retry
  • Seeing reservations on a material: the Inventory ledger and History
  • Seeing the order in Shopify’s inventory history
  • Manual cleanup of remaining reservations
  • Reservation backfill for orders that predate the setting
  • How it differs from safety-stock reservations
  • Edge cases & gotchas

For every BOMBill of materialsA bill of materials tells Assemblified how to build one unit of a finished good. When a customer orders the finished-good variant, Assemblified deducts the right component quantities from inventory automatically. Read more → line item on an incoming order, Assemblified expands the BOM (recursing through assembly BOMs, applying waste percentages) to the flat list of materials and pre-assembled draw-down. When Shopify completes routing for the order’s fulfillment orderFulfillment orderShopify's unit of fulfillment work: when a customer order is routed, Shopify splits it into one fulfillment order per fulfilling location, each carrying the line items that location will ship. Assemblified listens to the fulfillment-order lifecycle events (routing complete, hold, release, move, cancel) to drive per-location features like order reservations. Read more → , those resolved components are reserved at the routed location:

  • Shopify-linked materials — available drops and reserved rises by the same amount, in Shopify’s own inventory buckets.
  • Virtual materials — Assemblified’s own available figure drops; a history entry records the reservation.
  • Pre-assembled stock — when a BOM component is itself a finished-good with a pre-assembled shelf, the shelf is drawn down first, then any remainder draws from materials.

When the order is fulfilled, the reserved quantities are consumed — reserved drops, the shelf doesn’t bounce back to available, and the inventory is gone for good. If the order is cancelled, refunded, or rerouted before fulfillment, the reservation is released — quantities return to available. The one exception is a refund or cancellation where you choose Don’t restock: then the refunded units are consumed right away instead of released, because you told Shopify they are gone (see Refunds & cancellations → “Don’t restock”).

The default multi-location behaviour deducts components at fulfillment, which means between order-paid and shipment the storefront still shows those components as sellable. In a fast-moving store this opens an over-promise window: a flurry of orders for two BOMs that share a bottleneck component can all succeed at checkout, then collide at fulfillment.

Turning on order reservations trades visible storefront stock against safety. The tradeoff:

With reservationsWithout reservations
Component available drops at routing-complete (typically right after payment).Component available drops only at fulfillment.
Storefront reflects the true buildable count immediately.Storefront may temporarily over-promise during the routing→fulfillment window.
Concurrent orders for shared components contend at routing, not at fulfillment.Contention surfaces at fulfillment time, after both orders are paid.

Use it when over-promising during the routing→fulfillment window is a real problem — high order velocity, components shared across multiple BOMs, fulfillment lag that’s measured in days rather than minutes.

The switch lives at Settings → General, in the section headed Multi-location sensitive adjustments, and it reads “Track BOM materials as Shopify-reserved between routing and fulfillment.”

  1. Turn on “Respect order and fulfillment locations when available” first. It is the parent switch of that section; everything below it stays disabled until it is on.
  2. Switch “Track BOM materials as Shopify-reserved…” on. The section spells out the tradeoff underneath: visible Shopify storefront stock drops at routing time rather than at fulfillment.
  3. New orders from this point forward route through the reservation flow. In-flight orders that were already routed before the change keep the deduct-at-fulfillment behaviour.

With the switch off, the same section describes what you get instead: “Deduct at fulfillment: routing records a per-fulfillment-order intent with no stock movement, and each fulfillment deducts available stock at its own location, so split orders and partial shipments reduce at the correct location.”

Five Shopify fulfillment-order lifecycle webhooks drive the flow. They are registered automatically on install — no manual setup.

WebhookWhat it triggers
fulfillment_orders/order_routing_completeCommit the reservation at the assigned location — or hold it, if the fulfillment order arrived already on hold.
fulfillment_orders/placed_on_holdRelease the reservation back to available, and mark it held so the next event can re-activate it.
fulfillment_orders/hold_releasedCommit again — re-reserves the same components at the same location.
fulfillment_orders/movedRelease at the source location and commit at the destination, atomically. If the destination commit fails, the source release is rolled back so no stock is permanently lost.
fulfillment_orders/cancelledRelease, recording the fulfillment-order cancellation as the reason, then commit the replacement fulfillment order Shopify creates for the same items.

These supplement the orders/updated webhook, which still handles order-edit and refund paths, and the fulfillments/* topics that report the shipment itself.

A reservation moves through:

  • Active — committed, holding stock at the routed location.
  • Held — was active until the fulfillment order went on hold; the stock has been returned to available and is waiting for the hold to be released.
  • Consumed (or partially consumed) — fulfillment, or a partial fulfillment, consumed the reserved quantities.
  • Released — cancelled, refunded or rerouted; stock returned to available.
  • Failed — the commit was attempted but the Shopify write failed for every line. The local record is kept so it can be retried.

Every transition is recorded: created, held, hold released, consumed, released, rerouted out, rerouted in, Shopify synced, Shopify sync failed, Shopify reverted, failed.

Material kindOn commitOn releaseOn consume
Shopify-linkedShopify available → reserved at the routed location.reserved → available at the same location.reserved decrements; the inventory is permanently consumed.
Virtual (Assemblified-only)Assemblified’s own available figure decrements; a history entry is written. No Shopify call.Available increments; a history entry is written.A history entry is written; the stock was already deducted at commit.
Pre-assembled draw-downThe BOM’s pre-assembled shelf decrements at the routed location before any raw-material commit happens.Pre-assembled shelf increments back.Recorded only; the shelf decrement at commit is final.

A single reservation carries lines of all three kinds when the BOM mixes them — the reservation is the atomic unit, not the line.

When you edit an order in Shopify and add a line or raise a quantity, Assemblified reserves the added units at the time of the edit, at the location of the fulfillment order Shopify puts them in. They are consumed exactly once, when that fulfillment order ships. This holds however far along the order is:

  • Nothing shipped yet. The added units join the existing reservation, or get a reservation of their own if Shopify places them in a new fulfillment order.
  • After a partial or full shipment. The added units are reserved at the edit and consumed at their own shipment. They are not deducted at the moment of the edit.
  • While the order is on hold. Shopify places the added units in a separate fulfillment order, and Assemblified reserves them there. When the hold is released and Shopify merges the two fulfillment orders, the reservation follows without moving stock a second time. Units that land in a fulfillment order that is itself on hold are reserved when the hold is released.
  • After a move. If a fulfillment order was moved to another location, Shopify places an added line in a new fulfillment order at the order’s original location, not with the moved one, and the units are reserved there.
  • After an earlier refund. An added line or a raised quantity is still recognised as an edit and reserved.

A bill set to Only sell pre-assembled quantitiesOnly sell pre-assembledA per-bill toggle (Enhanced plan) that caps Shopify-displayed availability at the pre-assembled stock count. Buildable quantity is hidden — customers can only buy what's on the shelf. One of three mutually exclusive toggles: switching it on switches dynamic adjustment and maintain inventory level off. Read more → has its pre-assembled shelf drawn down at the edit, the same way the original order drew it down.

The same exceptions apply as for any reservation: a bill a location rule disables at that location reserves nothing there, a location set to Pre-assembled only draws from that location’s shelf only, and an assembly BOM set to Only consume pre-assembled draws from its own shelf rather than its components.

Removing a line, lowering a quantity, refunding or cancelling releases what was reserved for those units exactly once, whether the units came with the original order or with an edit. See Refunds & cancellations.

With order reservations off, added and increased units are recorded at the edit like the rest of the order, and nothing moves until they ship. When Assemblified recalculates a finished good’s sellable quantity in the meantime, those units count as already sold, so they are not offered for sale again.

Shopify writes can fail (rate-limited, session expired, in-flight network drop). When the Shopify push for a commit fails:

  • The local reservation is still recorded — what you asked for is preserved.
  • The affected lines are marked as failed to sync.
  • A “Retry Shopify sync” action replays the failed lines without re-pushing successful ones.

Retrying the sync is the right path; re-editing the reservation also fixes it but is the long way around.

Open a material and you can follow its reservations two ways.

The Inventory ledger tab lists every reservation that is still open for that material, right now: the order number (linked), its state (Active, On hold or Partly fulfilled), the location, how much it still holds and since when. It includes reservations made before the tab existed. A Shopify update failed badge means the change to Shopify for that reservation did not go through; Not yet sent means it has not been sent yet. A quantity shown as at least N belongs to an older hold where Assemblified cannot tell exactly how much the hold gave back; the real figure may be higher. See The material page.

The History tab shows each reservation step as its own row, with the order linked: Reserved 4 for order #1042, Reservation on hold for order #1042, Reservation resumed for order #1042, Released 2 from order #1038, Fulfilled 4 for order #1042, Written off 1 for order #1042. The line underneath says why (for example Order cancelled, Refund or Fulfillment on hold), so a hold and a cancellation never look alike. A step that moved no available stock still has its row: a reservation in tracking mode, or a fulfillment of units that were already reserved. The status badge says whether Shopify took it: Done, Waiting for Shopify, Not sent, Not applied or Shopify update failed. See The material page for each status.

A Retry Shopify sync adds no row of its own: it settles the step it retried. Once Shopify takes the change, that step’s row reads Done, and the attempt that failed is still listed among its entries.

History reaches back past the last 30 days into older, archived records as you page. Reservation steps appear as their own rows from the update that introduced the grouped History onward; a step from before that update shows only where it moved stock.

A reservation that stays open after its order is done

Section titled “A reservation that stays open after its order is done”

Reservations close on their own when Shopify tells Assemblified the order was fulfilled, cancelled, refunded or rerouted. If one of those messages never arrived, the reservation stays open and keeps holding stock. You will see it on the material’s Inventory ledger tab next to an order that is already done.

  1. Check the order in Shopify. Make sure it really is fulfilled, cancelled or refunded, and note whether the units left your stock.
  2. Open the cleanup dialog. On Materials, tick the material and choose Inventory → Reserved quantities.
  3. Choose what happens to the units. Select the order’s rows and choose Return to stock if the units are still on your shelf, or Write off stock if they are gone.

The reservation then disappears from the Inventory ledger, and the step appears in the material’s History.

Seeing the order in Shopify’s inventory history

Section titled “Seeing the order in Shopify’s inventory history”

When Assemblified changes stock in Shopify because of one specific order, Shopify’s own inventory adjustment history names that order. This covers every reservation step (reserve, release, fulfill, return to stock or write off, and restoring units after a cancellation) and the changes to a finished good’s available quantity that Assemblified makes because of a single order (see Dynamic adjustment).

  • The row shows the order number. It reads, for example, Inventory correction (#1234) or Inventory reserved (#1234).
  • The link is in the row’s activity details. Open the row’s activity popover in Shopify: the order name there is a link that opens the order. This works for open orders and for orders that were cancelled and archived.
  • A deleted order shows as plain text. If the order no longer exists, the popover shows its reference as plain text with no link. The stock change itself is not affected.
  • Older rows stay as they were. Changes made before this update keep their earlier description; nothing is rewritten.

Changes that are not caused by one order — a manual adjustment, a work order or build, a safety-stock reservation, a change that covers several orders at once, or an order from another store in a multi-store group — appear in Shopify exactly as before.

To review a material’s remaining order reservations, tick it on the Materials list and choose Inventory → Reserved quantities in the bar along the bottom. (That entry only appears while multi-location sensitive adjustments are on.)

The dialog lists one row per order and location, with what is Reserved, how much is Already used, the row’s Age and its Status — including badges for a hold that has not reached Shopify, a sync that failed, a partially used row and a row Shopify will not change. Narrow it with the Location filter, then answer “What should happen to the selected rows?”:

  • Return to stock — move the reserved units back to available.
  • Write off stock — take them out of inventory. The dialog warns before you do: writing off lowers on-hand stock.

Download changes takes the planned movements away for checking, before and after the run. Review them before applying.

You can clean up different materials from the same reservation in separate operations. A repeated request for an already completed operation returns the recorded result without moving stock again. If an attempt fails or reports an uncertain result, compare the reserved quantity in Shopify with the dialog’s planned changes before starting another attempt — the dialog prints both.

Reservation backfill: orders that never got a reservation

Section titled “Reservation backfill: orders that never got a reservation”

Switching the setting on does not reach backwards. Orders processed before it was on are still waiting for fulfillment with no reservation behind them, so their components are still sitting in available. Settings → Tools → Reservation backfill is the three-stage tool that fixes that, and it is gated on the same setting — with multi-location sensitive adjustments off, the page explains itself and every control is disabled.

  1. Scan. Candidate orders verifies each waiting order live against Shopify — still unfulfilled, not cancelled — and returns a table with a Verdict per row, summarised as “N eligible · N need review · N skipped”. Eligible rows are tickable; a partially fulfilled order needs review and has to be opted in by ticking its own Include box; skipped rows carry no checkbox at all. A row already partly covered says so — “N of M fulfillment orders already reserved”.
  2. Preview. Preview reservations (N) produces a read-only dry run through the same resolver the live path uses: Aggregated material movements (material, movement, quantity, location) and a Per-order detail table underneath, down to dropped lines and skipped inactive bills. Nothing has moved yet. Archive it as PDF, CSV or XLSX — this is the record of what you are about to do.
  3. Execute. Execute backfill (N order(s)) asks for confirmation, then queues the work and polls until the executed orders have left the waiting state. If anything changed between the scan and the execute, a warning says so before you continue.

There is also a Sheet filter: edit the Detail sheet you downloaded in stage 2, drop it back in (XLSX or CSV), and the run is narrowed to exactly the materials that survived your edit. The page reports what the sheet kept out of the total, names any rows it could not match, and names any order in the sheet that is in progress and therefore still needs its Include box ticked.

The steps above cover orders Assemblified recorded as waiting for fulfillment. Orders that were already open when you activated their bills — or when you installed Assemblified — were never handled at all: activating a bill only affects orders Shopify sends from then on. The same page has a second source for them, Open Shopify orders, which finds them in Shopify, reserves only the units still to ship, and asks what to do with any order Assemblified already deducted materials for. After that they behave like every other reserved order. See Bring open orders under Assemblified after a stocktake.

Order reservations vs safety-stock reservations

Section titled “Order reservations vs safety-stock reservations”

The two features share the word “reservation” but model different things. Don’t confuse them.

Safety-stock reservationsOrder reservations
Triggered byOperator action (“hold N units’ worth of components”)Shopify fulfillment-order lifecycle (automatic)
OwnerA finished good or an assembly BOMAn order + fulfillment-order + location
LifespanIndefinite — operator releases itBounded — released or consumed when the FO closes
Shopify bucketSafety stockReserved
Where it is setThe Reservations tab on the billOne store-wide switch

They can coexist. A BOM can have an active safety-stock reservation holding 10 units’ worth of components and still create order reservations for incoming orders against the same components — the safety stock has already been moved out of available, so the orders just see the remaining pool.

See Safety-stock reservations for the operator-driven variant.

  • Single-location stores are a no-op. Everything here depends on multi-location sensitive adjustments being on; with it off, all five webhooks short-circuit and the reservations switch is disabled.
  • Switching it off doesn’t strand in-flight reservations. Existing reservations stay as they are and still consume on fulfillment or release on cancel. Incoming events simply stop creating new ones. A later edit to an order that already has a reservation is booked the way that order was booked, whichever way the switch is set now. The one exception: while order production still holds built units for an order, or a confirmation is not finished, the switch cannot be turned off until those orders are shipped, cancelled or finished.
  • “Held” is a state, not a hold. Held is what a reservation becomes after it was placed on hold and its stock was already returned to available; it waits there for the hold to be released. A fulfillment order that arrives at routing time already on hold never commits in the first place.
  • Reroute is atomic. A move releases at the source and commits at the destination as a unit. If the destination commit fails, the source release is rolled back; you never end up with two active reservations for the same scope.
  • Late-arrival guard. If a fulfillment event arrives before its routing-complete (rare, but possible under retries), the consume step does nothing rather than over-decrementing, and the deduct-at-fulfillment path handles the order normally.
  • One product in a reserved and an unreserved fulfillment order of the same order. If a fulfillment order has no reservation (for example because Assemblified never received its event) and it ships a product that another fulfillment order of the same order has reserved, that shipment deducts nothing. The components stay too high by the shipped units, and the other fulfillment order keeps its reservation. This is a known limit.
  • Inactive bills are skipped. An inactive bill produces no reservation lines; its components stay available for the duration of that order.
  • The visible storefront drops at routing, not fulfillment. This is the whole point — but worth flagging for merchants used to the legacy timing. Marketing campaigns timed around shipment dates will see the stock disappear earlier than they may expect.