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

# Inventory

> Read stock levels and adjust them without a full product update.

`https://exclusivo.one/api/v1` · scopes `inventory:read`, `inventory:write`

Only products with `trackInventory: true` appear here. A product with tracking
off never sells out and isn't listed.

## Read stock

```http theme={"system"}
GET /api/v1/inventory
```

| Parameter    | Type    | Default |                                      |
| ------------ | ------- | ------- | ------------------------------------ |
| `product_id` | string  | —       | Just this one                        |
| `low_stock`  | boolean | `false` | Only items at or below the threshold |
| `threshold`  | integer | `10`    | What counts as low                   |

```bash theme={"system"}
curl "https://exclusivo.one/api/v1/inventory?low_stock=true&threshold=5" \
  -H "Authorization: Bearer ex_live_your_key_here"
```

```json theme={"system"}
{
  "data": [
    {
      "productId": "prod_abc123",
      "name": "Ceramic Mug",
      "inventory": 3,
      "trackInventory": true,
      "lowStock": true
    }
  ],
  "count": 1
}
```

This one doesn't paginate — you get every tracked product with a `count`.
`threshold` only affects the `lowStock` flag and the `low_stock` filter.

## Adjust stock

```http theme={"system"}
POST /api/v1/inventory
```

Needs `inventory:write`. Send `product_id`, plus **either** `adjustment` **or**
`set`.

| Field        | Type    |                                      |
| ------------ | ------- | ------------------------------------ |
| `product_id` | string  | Required                             |
| `adjustment` | integer | Relative. Negative to decrease       |
| `set`        | integer | Absolute                             |
| `reason`     | string  | Logged. Defaults to `API adjustment` |

<CodeGroup>
  ```bash Relative theme={"system"}
  curl -X POST https://exclusivo.one/api/v1/inventory \
    -H "Authorization: Bearer ex_live_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{ "product_id": "prod_abc123", "adjustment": -2, "reason": "Damaged in transit" }'
  ```

  ```bash Absolute theme={"system"}
  curl -X POST https://exclusivo.one/api/v1/inventory \
    -H "Authorization: Bearer ex_live_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{ "product_id": "prod_abc123", "set": 50, "reason": "Stocktake" }'
  ```
</CodeGroup>

### Which one to use

<Warning>
  **Use `adjustment` for anything that might run concurrently.** It applies as an
  atomic increment, so two adjustments arriving together both land.

  `set` overwrites. Two concurrent `set` calls means one silently wins, and a
  `set` racing a customer's purchase can put back stock that was just sold.

  Keep `set` for stocktakes, where overwriting is what you actually mean.
</Warning>

Stock never goes below zero — both paths clamp at `0`.

## The log

Every adjustment records the previous value, the new one, the reason, the source
and **which key made it**. That's what you use to work out why stock is wrong.

## The low-stock webhook

Crossing the threshold downwards fires `inventory.low`.

It fires on the crossing, not on every decrement below it — so you get one
alert, not a stream. That webhook threshold is a fixed **10**, independent of the
`threshold` parameter on the read endpoint.

→ [Webhooks](/api/webhooks)

## Errors

| Status |                                                    |
| ------ | -------------------------------------------------- |
| `400`  | No `product_id`, or neither `adjustment` nor `set` |
| `403`  | Wrong scope                                        |
| `404`  | Not in your shop                                   |
| `429`  | Rate limited                                       |
