# Stock and prices

> How to get stock per shop and avoid showing on the storefront what is not there.

Stock and price in BILLZ exist only **per shop**.

## Where stock comes from

The `GET /v2/products` method returns two arrays in every product:

```json
{
  "shop_measurement_values": [
    { "shop_id": "...", "shop_name": "Nike Store", "active_measurement_value": 5 }
  ],
  "shop_prices": [
    {
      "shop_id": "...",
      "retail_price": 1000000,
      "retail_currency": "UZS",
      "supply_price": 600000,
      "promo_price": 500000,
      "promos": [
        { "id": "...", "name": "discount 50%", "discount_type": "PERCENTAGE", "discount_value": 50 }
      ]
    }
  ]
}
```

`active_measurement_value` is the stock available for sale at the location, net of held sales; this is the value to use for the storefront.

Total stock breaks down as follows:

| Measure      | What it includes                                          |
| ------------ | --------------------------------------------------------- |
| active       | available for sale                                        |
| **inactive** | products **in transit** between shops plus **held sales** |
| total        | active + inactive                                         |

> **An unfinished supply is not part of inactive stock**
>
> Its quantity is in `import_started_measurement_value` of the `/v2/product-search-with-filters` method.

`/v2/products` has no breakdown, only `active_measurement_value`. The detailed measures are visible in `/v2/product-search-with-filters`: `total_in_transfer_measurement_value`, `total_sold_measurement_value`, `total_written_off_measurement_value`, `total_imported_measurement_value`, `total_transfered_measurement_value`, `total_transfer_arrived_measurement_value`, `import_started_measurement_value`, as well as `is_small_left` and `small_left_measurement_value`.

Returns are not exported as a separate number per product.

`promo_price` is taken into account **only when `promos` is filled**.

> **The promo price may lag behind the promotion itself**
>
> `promo_price` and stock are updated in the output with a delay. A critical price is re-checked at checkout.

How it is calculated: `PERCENTAGE` → `retail_price − retail_price × discount_value / 100`; `FIXED PRICE` → `discount_value` as is. If the product has several promotions in this shop, the minimum price is taken, and only the winning promotion comes in `promos`. Promotions of other types (1+1, carousel, by receipt total, by item count) do not form `promo_price`.

`supply_price` comes as zero and `supply_currency` as an empty string if the key role has no permission to see the cost price; it is off by default — see [key role permissions](/en/reference/role-permissions/).

> **Zero cost price also happens when the permission is granted**
>
> The permission check runs with a 10-second timeout; on failure the fields are zeroed the same way.

## Typical storefront strategies

| Strategy                          | When it fits                                    |
| --------------------------------- | ----------------------------------------------- |
| Sum of stock across all shops     | one shipping warehouse, shops are storefronts   |
| Stock of a single “online store”  | there is a dedicated location for online orders |
| Maximum across locations + pickup | the customer picks a shop at checkout           |

The price is taken from the same location whose stock is counted.

## Update frequency

A stock change on every sale gets into the [delta](/en/guides/sync-catalog/#delta) of `GET /v2/products`.

The overall limit is 2 requests per second; exceeding it returns `429`.

Stock of single-unit items is re-checked at checkout.

## Reservation

Reservation in BILLZ is a **held sale**: `active_measurement_value` is reduced by the held quantity and restored when the hold is released.

How it is done:

1. create a sale — `POST /v2/order` with body `{shop_id, cashbox_id}`;
2. add items — **only** `POST /v2/order-product/:order_id` (see [online order](/en/guides/ecommerce-order/));
3. hold the receipt — `POST /v2/order/create_postpone` with `order_id` and `time` in the body; the `time` field is required and has a strict format.

To bring a held sale back into work — `POST /v2/order/return-postpone`; after that the receipt is paid like a regular sale. More about receipt stages in the [sale lifecycle](/en/concepts/sale-lifecycle/).