Support

Need help?

Most answers are already written down. For everything else, Oneop does not publish a response time — there is no SLA instrument behind one — so the fastest thing you can do is send a complete first message.

Search the help centreEmail support@oneop.ioOpen your workspace

Worth including

  • The email you log in with
  • The URL of the screen it happened on
  • What you expected, and what happened instead

Goes somewhere else

Inventory

Order-to-fulfilment automation without overselling

By The Oneop team7 min read

TL;DR

Order-to-fulfilment automation is safe only when every automatic transition that commits stock takes the reservation in the same transaction that records the decision. Automation that reads an availability number, then writes an allocation as a second step, will oversell under concurrency no matter how short the gap is.

An automation reads the stock report, sees eleven units available, marks four orders as ready to ship, and moves on. Nothing checked whether anyone else touched the number in between, and nothing wrote a commitment anywhere. Three days later the fourth order is short.

This is the specific failure mode of fulfilment automation: not a broken rule, but a rule that treated a read as if it were a claim. The physical process of picking and packing is covered in pick, pack and ship. This is the layer above it, where transitions happen without a person, and where the guarantees have to be stronger because nobody is watching each one.

The one rule that governs all of it

Automation must never allocate stock it has not reserved. Every other guideline in this article is a consequence of that sentence.

A reservation is a durable record that a specific quantity is committed to a specific order. An allocation decision made from a number that was merely read is a guess about what other processes are doing. The gap between the read and the write is where two orders claim the same unit, and automation makes that gap dangerous because it runs far more often than a human does, and at machine speed.

The practical test for any automated transition: if this step advances an order towards shipping, does it hold a reservation for every unit it is advancing? If the answer is no, the step is asserting something it has not secured.

What can run automatically, and what should not

TransitionAutomatic?Why
Accept an order and reserve stockYesOne correct outcome given availability, and it fails closed when stock is short
Split a short line into a backorderYesDeterministic: reserve what exists, record the remainder
Release reservations on cancellationYes, alwaysThe forgotten path. Unreleased reservations create phantom shortages
Advance status when a label is boughtYesThe carrier’s response is the authority, not an inference
Ship an order that is shortNoRequires a decision about partial shipment, freight cost and the customer
Substitute a different variantNoA commercial judgement, and frequently a returns liability
Override a failed reservationNeverConverts a caught error into a customer-facing one

The pattern separating the rows: automation is appropriate where the data fully determines the answer and where the failure mode is refusal. It is inappropriate where the correct answer depends on context the system does not hold, which is the general argument in when not to automate.

Where the reservation has to be taken

The reservation belongs at order acceptance, in the same transaction that records the acceptance. Not before, not after.

Reserving at picking is the common mistake, because picking feels like the moment stock is physically committed. It is not the moment the promise was made. Between the confirmation email and the pick list, the customer believes the units are theirs, and the system believes they are free. Every minute of that window is available for a second order to consume the same units.

Reserving at cart or quote stage is the opposite error. Unconverted demand holds real stock, and abandoned carts quietly starve orders that would have paid. If you reserve at quote stage, the reservation needs an expiry, and something has to sweep expired reservations back into availability.

The race condition, and what actually closes it

Two orders for the last unit arrive within the same second. Both read availability as one. Both pass the check. Both write a reservation. Availability is now minus one, or worse, it is zero and two orders believe they own the unit.

Shortening the gap does not fix this. Retrying does not fix this. Checking twice does not fix this. The only fix is to make the check and the write inseparable, either by locking the stock row before reading it, or by expressing the reservation as a conditional write whose condition is the availability test. A conditional update that only succeeds when on-hand minus reserved is at least the requested quantity returns zero rows when the stock is not there, and zero rows is the signal to fail the order.

Automation raises the stakes because it removes the accidental serialisation a human workflow provides. Two people rarely confirm two orders in the same millisecond. Two queue workers processing a webhook backlog do it routinely.

The failure case: one order, delivered twice

A storefront sends an order-created webhook. The receiving endpoint takes longer than the storefront’s timeout, so the storefront retries. Two deliveries, same order, same external identifier.

The import path looks up the external identifier, finds nothing on either request because both lookups run before either insert, and creates two internal orders. Both reserve stock correctly and atomically. Each reservation is individually sound. The customer is now committed to twice the goods, and the warehouse picks both because the two orders look like two orders.

Notice that the reservation logic was not at fault. The defect is one layer up: order creation was not idempotent. Webhook delivery is at-least-once by design, so the uniqueness of an external order identifier has to be enforced by a database constraint, not by a read followed by a write. The lookup-then-insert pattern has exactly the same race as check-then-reserve, and it needs exactly the same remedy.

What an automated order flow should record at each step

Automation that cannot be audited is automation you will eventually switch off in a panic. Each automatic transition should leave behind what it decided, what it read, and what it wrote: the quantity reserved per line, the quantity backordered per line, and the document that caused the movement.

A stock movement row that records quantity and value before and after gives you a history you can read backwards. A status field alone gives you a present tense with no explanation, which is unusable at the moment you most need it.

How Oneop handles the automated order path

In Oneop, the transitions that commit stock are built into the order lifecycle rather than configured as rules, which is a deliberate constraint. Converting an accepted quote runs as a single transaction that creates the sales order and reserves stock together, and it is idempotent: a unique constraint on the workspace and quote pair means a second conversion call returns the existing records instead of creating a second order.

The reservation itself is a conditional update that only succeeds when on-hand minus reserved covers the quantity. Zero rows returned means insufficient stock, which throws and rolls the whole transaction back, leaving no partial order behind. Where only part of a line can be reserved, the remainder is written as a backordered quantity on the line and the order is held in backorder rather than confirmed.

Shipping re-validates under a lock. It takes the sales-order row with a locking read, confirms the order is still shippable, and aborts if a concurrent request has already advanced it, so a duplicated ship request cannot ship twice. The same transaction decrements stock, posts cost of goods sold to the ledger, raises the invoice and advances the status.

One honest limit. Oneop’s automation engine triggers on record events in the CRM, projects and help desk, on SLA breaches and on a schedule. It is not where order-to-fulfilment stock transitions live, and we would rather say so than imply a trigger list we do not have. The stock guarantees in the inventory module come from the order service, enforced at the write.

The audit to run this week

Take your three most common automatic order transitions and, for each one, answer whether it holds a reservation for every unit it advances, and whether running it twice produces the same result as running it once. The mechanics of reservations themselves are in inventory reservations explained, and the arithmetic they protect is in available-to-promise.

Frequently asked questions

Which order transitions are safe to automate?

Transitions that are deterministic and reversible, or that fail closed. Accepting an order and reserving stock, splitting a short line into a backorder, advancing status when a carrier confirms a label, and releasing reservations on cancellation are all safe, because each has one correct outcome given the data. Transitions that require a judgement about a customer relationship, a partial shipment or a substitution should stay with a person.

Where should stock be reserved in an automated order flow?

At the moment the order is accepted, inside the same database transaction that records the acceptance. Reserving later, at picking or at shipping, means the period between acceptance and picking is a window in which the same units can be promised again. Reserving earlier, at cart or quote stage, ties up stock against demand that may never convert.

What happens when two orders arrive for the last unit at the same time?

One of them must lose at the write, not in the interface. The correct implementation makes the availability check and the reservation a single atomic operation, so the second order's check runs against the state the first one left behind. If the check and the write are separable, both orders can pass the check before either reserves, and both customers receive a confirmation for stock that exists once.

How do duplicate webhooks from a storefront cause overselling?

Webhook delivery is at-least-once, so the same order notification can arrive twice. Without an idempotency key, the second delivery creates a second order, and that second order reserves stock again for units that were already committed. The fix is a uniqueness constraint on the external order identifier, enforced by the database rather than by a lookup before the insert.

Should automation ever override a failed stock reservation?

No. A failed reservation is the system correctly reporting that the units are not available, and an automation that proceeds anyway has converted a caught error into an uncaught one. The right automatic responses are to create a backorder, offer a substitute, or hold the order for a human. Forcing the allocation is the one action that guarantees a customer will be disappointed later.

Written by

The Oneop team

Writing from the team that builds Oneop — an AI customer platform with web chat and email on top of a real CRM, help desk, ledger and warehouse.

Related reading

Start on the free plan and see the real thing

No credit card. Free plan, 2 seats.

Free forever · 2 seats · No credit card