> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Inventory Lifecycle

> Stock, reservations, and adjustments — the three-step protocol that prevents oversells.

# Inventory Lifecycle

Inventory in the embedded engine distinguishes **on-hand** from **available**. The gap between
them is reservations, and getting that protocol right is what prevents overselling.

## Setup

```js theme={null}
import { Commerce } from '@stateset/embedded';
const commerce = new Commerce('./store.db');
```

## Create an item

```js theme={null}
await commerce.inventory.createItem({
  sku: 'SHOE-RED-10',
  quantity: 250,
  location: 'WH-1',
});
```

## Read stock

```js theme={null}
const stock = await commerce.inventory.getStock('SHOE-RED-10');
```

## Reservations: the three-step protocol

This is the part worth understanding properly.

```js theme={null}
// 1. Hold the stock
const reservation = await commerce.inventory.reserve({
  sku: 'SHOE-RED-10',
  quantity: 1,
  orderId: 'ord_9412',
});

// 2a. The order went through
await commerce.inventory.confirmReservation(reservation.id);

// 2b. Or it didn't
await commerce.inventory.releaseReservation(reservation.id);
```

<Warning>
  A reservation that is never confirmed **or** released holds stock indefinitely. It will not time
  out on its own.

  Wrap it so both outcomes are covered, including on the error path:

  ```js theme={null}
  const reservation = await commerce.inventory.reserve({ … });
  try {
    await authorizePayment();
    await commerce.inventory.confirmReservation(reservation.id);
  } catch (err) {
    await commerce.inventory.releaseReservation(reservation.id);
    throw err;
  }
  ```
</Warning>

### Why reserve at all

Without a reservation, two concurrent checkouts both read available stock as 1 and both succeed.
Reserving makes the read-and-hold atomic, so the second checkout sees 0.

Order the calls as **reserve → authorise → confirm**. Reserving first closes the race; confirming
last means a declined card releases the stock rather than stranding it.

## Adjustments

For counted corrections, damage, and shrinkage:

```js theme={null}
await commerce.inventory.adjust({
  sku: 'SHOE-RED-10',
  quantityDelta: -3,
  reason: 'damaged in transit',
});
```

Adjustments are recorded as movements with an audit trail, not as a silent overwrite of a
quantity — so a later discrepancy can be traced to the adjustment that caused it.

## Cycle counts

For a systematic recount rather than a one-off correction, use the cycle-count workflow — `draft →
in-progress → completed`, with variance applied transactionally to location inventory and a
`cycle_count` movement record written. See
[Finance & Accounting](/stateset-icommerce/stateset-icommerce-finance#cycle-counts).

## Where reservations come from

| Trigger            | Effect                             |
| ------------------ | ---------------------------------- |
| Order created      | Reserve against the line items     |
| Payment authorised | Confirm the reservation            |
| Order cancelled    | Release the reservation            |
| Return received    | Restock, if the disposition allows |

## Related

* [Order Lifecycle](/stateset-icommerce/stateset-icommerce-order-lifecycle)
* [Returns Workflow](/stateset-icommerce/stateset-icommerce-returns-workflow)
* [Finance & Accounting](/stateset-icommerce/stateset-icommerce-finance) — cycle counts and valuation
