> ## 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 Quickstart

> Create stock, reserve units, reject an oversized hold, and verify release and adjustment using the embedded Node.js engine.

Use **`@stateset/embedded` 1.35.1** to explore stock availability in a fresh in-memory
database. This example needs no API key or external service. Every stock change has an
assertion, so a successful run verifies the behavior as well as showing the calls.

## Prerequisites

Use Node.js **20.20.0+** and npm **10+**. In a new directory:

```bash theme={null}
mkdir stateset-inventory-example
cd stateset-inventory-example
npm init -y
npm install @stateset/embedded@1.35.1
```

See [SDK installation](/guides/sdk-installation) for native-module troubleshooting.

## Run the complete example

Save this as `inventory-demo.mjs`:

```javascript theme={null}
import assert from 'node:assert/strict';
import { Commerce } from '@stateset/embedded';

const commerce = new Commerce(':memory:');
const sku = 'MUG-001';
await commerce.inventory.createItem({
  sku, name: 'Ceramic mug', initialQuantity: 10,
});

async function checkStock(label, onHand, allocated, available) {
  const stock = await commerce.inventory.getStock(sku);
  assert.ok(stock);
  assert.deepEqual(
    [stock.totalOnHand, stock.totalAllocated, stock.totalAvailable],
    [onHand, allocated, available],
  );
  console.log(`${label}: on hand=${onHand}, allocated=${allocated}, available=${available}`);
}

await checkStock('Created', '10', '0', '10');
const hold = await commerce.inventory.reserve(
  sku, 3, 'checkout', 'demo-checkout-001', 300,
);
assert.equal(hold.status, 'pending');
await checkStock('Reserved', '10', '3', '7');

await assert.rejects(
  () => commerce.inventory.reserve(sku, 8, 'checkout', 'demo-checkout-too-large', 300),
  { code: 'INSUFFICIENT_STOCK' },
);
await checkStock('Oversized hold rejected', '10', '3', '7');

await commerce.inventory.releaseReservation(hold.id);
await checkStock('Released', '10', '0', '10');

const accepted = await commerce.inventory.reserve(
  sku, 2, 'checkout', 'demo-checkout-002', 300,
);
await commerce.inventory.confirmReservation(accepted.id);
await checkStock('Confirmed', '10', '2', '8');

await commerce.inventory.adjust(sku, -1, 'Damaged during stock count');
await checkStock('Adjusted', '9', '2', '7');
```

```bash theme={null}
node inventory-demo.mjs
```

**Success:** all assertions pass and you see:

```text theme={null}
Created: on hand=10, allocated=0, available=10
Reserved: on hand=10, allocated=3, available=7
Oversized hold rejected: on hand=10, allocated=3, available=7
Released: on hand=10, allocated=0, available=10
Confirmed: on hand=10, allocated=2, available=8
Adjusted: on hand=9, allocated=2, available=7
```

## Understand the quantities

| Field | Meaning in this example |
| - | - |
| `totalOnHand` | Physical units recorded in the database |
| `totalAllocated` | Units held by reservations |
| `totalAvailable` | On-hand units minus allocated units |

The returned quantities are strings. `initialQuantity`, the reservation quantity, and the
adjustment delta are numbers in this version of the Node API.

A rejected hold leaves the existing stock unchanged. Releasing the first reservation restores
availability. Confirming the second keeps its two units allocated: **confirmation does not
ship goods, decrement on-hand stock, or release the hold**. The final adjustment records one
damaged unit; it is a separate inventory correction.

## Choose who owns the reservation

This example creates manual checkout holds. In the [orders quickstart](/guides/orders-quickstart),
order creation with `stockPolicy: 'reject_if_insufficient'` already reserves the order's units.
Do not add another manual hold for those same units without an explicit handoff strategy.

`reserve` takes positional arguments: SKU, quantity, reference type, reference ID, and an
optional lifetime in seconds. Here, `300` gives the pending hold an expiration timestamp five
minutes after creation. A reference ID links a hold to your workflow; this example does not
establish it as an idempotency key. Save the returned reservation ID before later operations.

For expiration handling and method signatures, see the
[inventory lifecycle reference](/stateset-icommerce/stateset-icommerce-inventory-lifecycle).

## Troubleshooting

| Symptom | Check or next step |
| - | - |
| `INSUFFICIENT_STOCK` | Read available stock, including existing allocations; a retry without a stock change will still fail |
| `getStock` returns `null` | Verify the SKU and database instance; create the item before reserving |
| Availability stays reduced after confirmation | Expected for 1.35.1; confirmation keeps the allocation |
| Availability is lower than expected | Look for manual holds in addition to reservations created by orders |
| Adjustment call fails | Use `adjust(sku, quantityDelta, reason)`; a negative delta reduces on-hand stock |
| Data disappears after exit | `:memory:` is temporary; use a file path when you want persistence |

## Next steps

* [Manufacturing](/guides/manufacturing-quickstart): record production batches and inspect stock separately.

* [Warehouse receiving](/guides/warehouse-quickstart): record partial inbound quantities and verify the separate inventory boundary.

* [Order reservations and cancellation](/guides/orders-quickstart): let the order workflow manage its own stock holds.

* [Inventory lifecycle](/stateset-icommerce/stateset-icommerce-inventory-lifecycle): check method signatures and integration boundaries.

* [Persisted embedded quickstart](/stateset-icommerce/stateset-icommerce-quickstart): inspect records from a second process.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.