Inventory hold: the row that exists so the second buyer is refused
In short
An inventory hold is a record, not a UI state: it names one unit of supply, one claimant, one expiry and one reason, and while it is live no other hold or order may overlap that unit. It ends exactly 3 ways — promoted to an order, expired, or released — and a system that cannot say which of the 3 happened cannot explain to a buyer why they lost a slot.
Key takeaways
- A hold is a first-class row with 4 mandatory fields, not a flag on a listing or a value in a session.
- Subject, claimant, expiry and reason: drop any one and the system can no longer explain a refusal.
- The invariant — no 2 live holds may overlap one unit — belongs in the database, not in application code.
- A hold ends promoted, expired or released, and the terminal state must be recorded rather than inferred.
An inventory hold is a row whose only job is to own exclusivity on one unit of supply for a bounded time. It exists so that between the moment a buyer says yes and the moment payment settles, a second buyer looking at the same slot, seat, room or item is refused rather than accepted into a conflict somebody resolves later. Four fields make that possible: which unit, for whom, until when, and why.
Insisting it is a record rather than a screen state matters because every hard question about a transaction is a question about this row. Why was I refused? Why did my basket empty? Why does the seller's calendar show a booking that produced no money? Where the hold lives in a session or a cache entry, none of those can be answered.
The four fields a hold cannot work without
| Field | What it carries | What breaks when it is missing |
|---|---|---|
| Subject — the unit of supply | Listing reference plus the thing held: an interval, a seat, or a quantity against countable stock | Overlap cannot be tested, so exclusivity becomes a race and 2 buyers both succeed |
| Claimant | A buyer id, a guest session, or a seller in the case of a dispatch offer | The buyer is blocked by their own hold when they return to finish |
| Expiry | An absolute instant in UTC, set at creation | Abandoned baskets remove supply permanently, unnoticed until a seller complains |
| Reason or origin | Checkout, dispatch offer, admin block, payment retry, seller maintenance | Operations cannot tell a stuck checkout from a deliberate block, so the fix is always to delete rows |
| State, with the instant of transition | Live, promoted, expired or released | The lifecycle is inferred from absence, and absence is indistinguishable from a bug |
| Idempotency key | A stable key from the client action that created it | A double-tapped checkout creates 2 holds on one unit, and the second blocks the first |
The subject field is where most modelling errors start, because it inherits whatever shape supply has. Bookable time makes the subject an interval, testable for overlap only if availability is stored as intervals rather than a grid of day cells. Countable stock makes it a quantity, and the invariant arithmetic rather than geometric. Markets selling both need both.
A hold ends exactly three ways
- Promoted. Payment succeeded and the hold becomes an order. The agreed terms are frozen here — the listing snapshot a buyer actually bought is captured at promotion, and the fields that survive are in what belongs in an order record.
- Expired. The expiry instant passed with no promotion. The unit returns to the market and the row stays, marked expired, because it is the only evidence a buyer was here and did not finish.
- Released. Something ended it early — a deliberate abandon, a hard payment failure, a seller withdrawal, an operator clearing it. Release is distinct from expiry: different causes, different fixes.
How the expiry actually fires is a separate and harder problem, covered in expiring a hold without trusting a cron. How long the window should be is answered by conversion data rather than taste, in how long a checkout hold should live.
What a hold is not
- Not a payment authorisation. An authorisation reserves money; a hold reserves supply. A system that conflates them cannot hold a slot for a bank transfer.
- Not a cart line. A cart is a buyer assembling intent; a hold is a claim on the market. Items can sit in a cart holding nothing, which is usually correct.
- Not exclusive to buyers. A dispatch offer to a courier is a hold on that person's time, with a short expiry and automatic release on decline. Which sellers receive the offer at all is decided upstream by rules such as the dispatch radius that is not a circle.
An automated claimant is still a claimant. An agent accepting jobs for a seller creates the same row under the same constraint, with a reason field naming the agent and an expiry short enough that a failed automation does not take supply off the market. Machine actions passing through the same durable structures as human ones is the containment principle behind AI agents in production and our AI agents and automation work.
Most consistency questions in a market route through this row. The rest of the schema sits under marketplace architecture and the transaction data model, and what we build around it is in our marketplace work.
Frequently asked questions
Short answers to the follow-ups this page tends to raise.
What is an inventory hold in a marketplace?
It is a temporary, expiring record giving one claimant exclusive rights to one unit of supply while a transaction completes. It carries the unit held, who holds it, when the claim lapses and why it exists, and while it is live nothing else may overlap that unit. On payment it is promoted to an order; otherwise it expires or is released and the supply returns to the market.
What is the difference between a hold and a confirmed booking?
A hold is provisional and expires by itself; a booking is durable and ends only when somebody cancels it. The hold exists to survive the seconds between intent and payment, so it is designed to disappear. The booking is the record the business runs on afterwards, carrying the terms, the money and the obligations.
Can a hold live in the session or a cache instead of the database?
No, and the reason is exclusivity rather than durability. A hold must be visible to every other request in the system, and must survive a process restart, a cache eviction and a buyer switching devices. An entry that vanishes silently produces double bookings that look random and cannot be reproduced, because the evidence expired with the entry.
Should a hold block the seller as well as other buyers?
Yes — a live hold must block every claimant, including the seller's own calendar and any admin action, or the invariant is not an invariant. What differs is the interface: a seller should see that a unit is held and when it lapses, and request a release with a reason rather than writing over it.
- holds
- inventory
- data model
- checkout
The work behind this page
Builds from our portfolio that this page draws on.
Low Latency Food Ordering Platform
Unified events operations platform: vendor management, order tracking, payments, automated settlements.
MarketplaceDockQueue
A dock scheduling and yard management platform that lets carriers self-book dock appointments, assigns the right door on arrival, and tracks every trailer's dwell and detention live.
LogisticsWorking on something in this space?
Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.
Start the conversation