Modules

Inventory

Stock by warehouse, the kardex of every movement, adjustments and transfers.

Overview

Inventory keeps the quantity of each product in each warehouse and a kardex (stock_movement) for every change. Stock moves automatically when invoices are issued or cancelled, purchase orders are received, bills are registered and work orders are delivered. Through the API you can read stock levels and movements, and record adjustments and transfers.

Requires the Inventory module. An owner or admin turns it on in Settings > Modules. While it is off, every endpoint on this page returns 403 with type permission_error and code module_disabled.

Warehouses, adjustment reasons and inventory settings (for example allowing negative stock) are configured in the dashboard, in Settings > Inventory. The API lists them but does not create them.

The Stock Level Object

A stock_level has no id: it is keyed by product_id. quantity is the total of every warehouse, value = quantity x avg_cost, and low_stock is true when the product tracks stock, has a reorder_point and its total quantity is at or below it.

JSON
1{
2 "object": "stock_level",
3 "product_id": "itm_9f2c1a",
4 "item_code": "PRD-000012",
5 "name": "USB-C cable 2m",
6 "unit": "unit",
7 "currency": "USD",
8 "is_active": true,
9 "track_stock": true,
10 "reorder_point": 10,
11 "quantity": 42,
12 "avg_cost": 4.2,
13 "value": 176.4,
14 "low_stock": false,
15 "warehouses": [
16 { "warehouse_id": "wh_7k2", "warehouse_name": "Main warehouse", "quantity": 30 },
17 { "warehouse_id": "wh_8m3", "warehouse_name": "Store", "quantity": 12 }
18 ]
19}

Endpoints

MethodPathPermissionDescription
GET/v1/warehousesreadList warehouses (include_inactive)
GET/v1/stockreadStock levels (warehouse_id, product_id, low_stock). Cursor = product_id
GET/v1/stock/{product_id}readStock of one product by warehouse
POST/v1/stock/{product_id}writeSet track_stock and reorder_point
GET/v1/stock/reasonsreadAdjustment reasons (in, out or both)
GET/v1/stock/movementsreadKardex (product_id, warehouse_id, from, to)
GET/v1/stock/adjustmentsreadList adjustments (warehouse_id)
POST/v1/stock/adjustmentswriteAdd or remove stock in one warehouse
GET/v1/stock/transfersreadList transfers (warehouse_id = origin or destination)
POST/v1/stock/transferswriteMove stock between two warehouses

Check Stock

1# Products at or below their reorder point
2curl "https://api.corebill.io/v1/stock?low_stock=true" \
3 -H "Authorization: Bearer sk_live_your_api_key"
4
5# One product
6curl "https://api.corebill.io/v1/stock/itm_9f2c1a" \
7 -H "Authorization: Bearer sk_live_your_api_key"

With warehouse_id, quantity and value refer to that warehouse only and warehouses has a single entry; low_stock still uses the total. The list includes every product, also those with track_stock: false.

Create an Adjustment

An adjustment adds (positive quantity) or removes (negative quantity) stock in one warehouse: initial stock, a stock count, damage. unit_cost only applies to positive lines and updates the average cost. If the reason has a direction, it restricts the signs.

1curl -X POST "https://api.corebill.io/v1/stock/adjustments" \
2 -H "Authorization: Bearer sk_live_your_api_key" \
3 -H "Content-Type: application/json" \
4 -H "Idempotency-Key: count-2026-10-11" \
5 -d '{
6 "warehouse_id": "wh_7k2",
7 "reason_id": "rsn_1",
8 "notes": "Cycle count",
9 "lines": [
10 { "product_id": "itm_9f2c1a", "quantity": 20, "unit_cost": 4.2 },
11 { "product_id": "itm_4d", "quantity": -2 }
12 ]
13 }'
Response (201)
1{
2 "id": "adj_9x",
3 "object": "stock_adjustment",
4 "adjustment_number": "ADJ-000003",
5 "warehouse_id": "wh_7k2",
6 "reason_id": "rsn_1",
7 "notes": "Cycle count",
8 "reversal_of": null,
9 "lines": [
10 { "product_id": "itm_9f2c1a", "quantity": 20, "unit_cost": 4.2, "movement_id": "kmx_55a" },
11 { "product_id": "itm_4d", "quantity": -2, "unit_cost": 3.1, "movement_id": "kmx_55b" }
12 ],
13 "failed_product_ids": [],
14 "created_at": "2026-10-11T15:05:00.000Z"
15}
  • Up to 500 lines; a repeated product is summed
  • Every product must track stock (400 "Some products do not track stock.")
  • Removing more than available returns 409 resource_conflict ("Not enough stock for: ...") unless the company allows negative stock
  • lines lists the movements applied; failed_product_ids (only in this response) lists the lines that were rejected
  • Emits stock.adjusted, and stock.low when a product crosses its reorder point

Transfer Stock

1curl -X POST "https://api.corebill.io/v1/stock/transfers" \
2 -H "Authorization: Bearer sk_live_your_api_key" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "from_warehouse_id": "wh_7k2",
6 "to_warehouse_id": "wh_8m3",
7 "lines": [{ "product_id": "itm_9f2c1a", "quantity": 5 }]
8 }'

Quantities must be positive and the warehouses different. Not enough stock in the origin returns 409 resource_conflict. Emits stock.transferred.

Stock Movements (Kardex)

Every change is a stock_movement with a signed quantity, the resulting balance_quantity, the cost, and the source document (reference_type, reference_id and its human document_number).

1curl "https://api.corebill.io/v1/stock/movements?product_id=itm_9f2c1a&from=2026-10-01&to=2026-10-31" \
2 -H "Authorization: Bearer sk_live_your_api_key"
movement_typeSource
in / outAdjustments
transfer_in / transfer_outTransfers
sale / sale_returnIssued and cancelled invoices, work order deliveries
purchase / purchase_returnPurchase receipts and bills (and their cancellation)