Catalogue and inventory over the API

The endpoints to read and write stock, suppliers, warehouses, counts and transfers, and to read the stock signal and the day's work.

On this page
  1. Stock per SKU
  2. Importing stock from a file
  3. The stock signal and the day's work
  4. Suppliers
  5. Warehouses
  6. Physical counts
  7. Transfers between warehouses
  8. Other inventory resources

All of these routes are under /inventory. Reads work with a read key; writes need a write key. If the key is limited to some warehouses, it only sees and touches those.

Stock per SKU

Method and routeWhat it doesKey
GET /inventory/stock/pagePaged stock list. Parameters: limit, offset, q (search), warehouse.read
GET /inventory/stockAll stock at once (unpaged).read
GET /inventory/stock/lookupLooks up stock records.read
GET /inventory/stock/{sku}One record.read
GET /inventory/stock/{sku}/historyA SKU's stock history.read
PUT /inventory/stock/{sku}Creates or replaces the record. current_stock is required.write
PATCH /inventory/stock/{sku}Changes some fields of a record that already exists. Never creates one.write
DELETE /inventory/stock/{sku}Deletes the stock record.write
PATCH /inventory/stock/{sku}/product-typeChanges the product type (finished good, component…).write
GET / PUT / DELETE /inventory/stock/{sku}/suppliers[/{supplier_id}]The suppliers linked to a SKU.read / write
curl "https://app.stockai.es/api/v1/inventory/stock/page?limit=50&offset=0&warehouse=principal&q=SKU-0" \
  -H "Authorization: Bearer $STOCKAI_KEY"
# Create or replace a SKU's record (current_stock is required)
curl -X PUT https://app.stockai.es/api/v1/inventory/stock/SKU-001 \
  -H "Authorization: Bearer $STOCKAI_KEY" -H "Content-Type: application/json" \
  -d '{"current_stock":120,"min_stock":20,"lead_time_days":7,"unit_cost":2.5,
       "moq":12,"supplier":"Distribuidora Andina","warehouse":"principal"}'

# Change one field of a record that already exists (never creates one)
curl -X PATCH https://app.stockai.es/api/v1/inventory/stock/SKU-001 \
  -H "Authorization: Bearer $STOCKAI_KEY" -H "Content-Type: application/json" \
  -d '{"current_stock":118}'

Fields PUT/PATCH accept: display_name, current_stock, min_stock, lead_time_days, unit_cost, moq, supplier, notes, sale_price, category, family, brand, unit_of_measure, barcode, warehouse. The response is the full record:

{
  "sku": "SKU-001", "display_name": "SKU-001", "warehouse": "principal",
  "current_stock": 120.0, "min_stock": 0.0, "lead_time_days": 7,
  "unit_cost": 2.5, "moq": 12.0, "supplier": "Distribuidora Andina",
  "product_type": "finished_good", "service_level": 0.95,
  "lead_time_set_by": "user", "unit_cost_set_by": "user", "moq_set_by": "user",
  "updated_at": "2026-10-01T08:00:00+00:00"
}

The *_set_by fields say who set each value (user, file, supplier_rule…). What you write through the API counts as your own data.

Importing stock from a file

# 1. Preview: what was detected and what would be written. Writes nothing.
curl -X POST https://app.stockai.es/api/v1/inventory/bulk/preview \
  -H "Authorization: Bearer $STOCKAI_KEY" -F "file=@stock.csv"

# 2. Import. Optional fields: mapping (JSON text), warehouse, thousands_dot, only_fill_missing
curl -X POST https://app.stockai.es/api/v1/inventory/bulk \
  -H "Authorization: Bearer $STOCKAI_KEY" -F "file=@stock.csv" -F "warehouse=principal"

/inventory/bulk/preview touches no row: it tells you which columns it detected (detected_mapping), which it did not use and what it would write. If a sync profile owns any of those fields, it warns in synced_overwrites. GET /inventory/template.csv downloads the template with the recognised headers.

The stock signal and the day's work

GET /inventory/status
The stock signal per SKU: signal, coverage, reorder point and recommended quantity. Optional parameters: session_id (without it, the active session), signal, supplier, abc, by_warehouse, q, skus, without_forecast, service_level, sort, order, limit, offset.
GET /inventory/morning-briefing
The day's work: risks, recommendations with their reason, demand changes and KPIs. Parameters: session_id, service_level.
GET /inventory/dashboard-summary
The dashboard's figures.
GET /inventory/optimize
Purchases and transfers suggested by the optimizer, per SKU and warehouse (session_id, horizon_days).
GET /inventory/setup-gaps
SKUs missing setup (cost, lead time…).
GET /inventory/dead-capital, /margin-erosion, /supplier-cost-inflation, /forecast-money
Tied-up capital, eroding margin, cost inflation and projected revenue, cost and margin.
curl "https://app.stockai.es/api/v1/inventory/status?signal=PEDIR_YA&supplier=Andina" \
  -H "Authorization: Bearer $STOCKAI_KEY"
{
  "period": "daily", "coverage_unit": "day",
  "items": [{
    "sku": "SKU-002", "signal": "PEDIR_YA",
    "current_stock": 42.0, "reorder_point": 5919.28, "recommended_qty": 5878.0,
    "coverage_days": 0.1,
    "lead_time_days": 7, "lead_time_source": "user",
    "unit_cost": 5.2,    "unit_cost_source": "user",
    "moq": 25.0,         "moq_source": "user",
    "supplier": "Granos del Valle",
    "explanation_code": "inventory_explain_reorder",
    "explanation_params": { "signal": "PEDIR_YA", "daily_demand": 757.97, "coverage_days": 0.1,
                            "current_stock": 42.0, "reorder_point": 5919.28,
                            "lead_time_days": 7, "lead_time_source": "user" },
    ...
  }]
}
signalMeaning
PEDIR_YAOrder now: stock does not cover the lead time.
PEDIR_PRONTOOrder soon.
OKCovered.
SOBRESTOCKMore than needed.
SIN_DATOSNo stock on record: unknown, which is not the same as "fine".

The lead_time_source, unit_cost_source and moq_source fields say where each assumption came from: user (you typed it), file (it came in your file), supplier_rule (a supplier rule), learned (learned from your receptions; lead time only) or default (StockAI assumed it). Handle all five. The reason behind each recommendation comes structured in explanation_code + explanation_params: decide on that, not on the explanation text.

Suppliers

Method and routeWhat it does
GET /inventory/suppliers, GET /inventory/suppliers/pageList (whole, or paged with limit, offset, q).
POST /inventory/suppliersCreates a supplier. Only name is required.
PATCH /inventory/suppliers/{id}Changes its details.
DELETE /inventory/suppliers/{id} / POST .../{id}/reactivateDeactivates / reactivates it (it is not deleted).
POST /inventory/suppliers/import/preview, POST /inventory/suppliers/importImport from a file (template at GET /inventory/suppliers/import/template).
GET /inventory/suppliers/scorecard, /lead-time-alerts, /contact-healthPerformance, suppliers taking longer than usual, and missing contact details.
POST /inventory/suppliers/{id}/price-breaks, GET /inventory/price-breaksVolume discounts.
curl -X POST https://app.stockai.es/api/v1/inventory/suppliers \
  -H "Authorization: Bearer $STOCKAI_KEY" -H "Content-Type: application/json" \
  -d '{"name":"Distribuidora Andina","email":"ventas@andina.example",
       "lead_time_days":7,"payment_terms":"30 dias","payment_terms_days":30,
       "erp_code":"PRV-0042"}'

Fields: name, email, phone, whatsapp, lead_time_days, lead_time_std, review_period_days, payment_terms, payment_terms_days, notes and erp_code. The erp_code is the supplier's code in your ERP: it is what the order feed uses to match documents.

Warehouses

  • GET /inventory/warehouses lists warehouses (id, name, is_default…). POST creates one and PATCH /inventory/warehouses/{name} changes it. The number of warehouses has a per-plan ceiling.
  • GET / PUT / DELETE /inventory/warehouses/lanes: transfer lanes between warehouses.

Physical counts

# Open a count for one warehouse
curl -X POST https://app.stockai.es/api/v1/inventory/stock-counts \
  -H "Authorization: Bearer $STOCKAI_KEY" -H "Content-Type: application/json" \
  -d '{"warehouse":"principal","notes":"Monthly count"}'

# Record what was counted, line by line
curl -X PUT https://app.stockai.es/api/v1/inventory/stock-counts/$COUNT_ID/lines \
  -H "Authorization: Bearer $STOCKAI_KEY" -H "Content-Type: application/json" \
  -d '{"sku":"SKU-001","quantity":117,"mode":"set"}'
# mode "add" (default) accumulates, e.g. one scan = one unit; "set" replaces the counted total.
# client_ref (optional): an id per scan, so a retried request is ignored.

# See the differences, then apply them to the stock
curl https://app.stockai.es/api/v1/inventory/stock-counts/$COUNT_ID/preview -H "Authorization: Bearer $STOCKAI_KEY"
curl -X POST https://app.stockai.es/api/v1/inventory/stock-counts/$COUNT_ID/apply -H "Authorization: Bearer $STOCKAI_KEY"

A count is opened (POST), its lines filled (PUT .../lines, DELETE .../lines/{sku}), reviewed (GET .../preview) and applied (POST .../apply) or closed / cancelled (.../close, .../cancel). Applying sets the stock to what was counted.

Transfers between warehouses

curl -X POST https://app.stockai.es/api/v1/inventory/transfers \
  -H "Authorization: Bearer $STOCKAI_KEY" -H "Content-Type: application/json" \
  -d '{"from_warehouse":"principal","to_warehouse":"norte",
       "items":[{"sku":"SKU-001","qty":10}],"notes":"Weekly rebalancing"}'

Then: POST /inventory/transfers/{id}/receive when it arrives, .../close to close it and .../cancel to void it. GET /inventory/transfers lists them.

Other inventory resources

Demand events
/inventory/events and its catalogue: seasons and dates that move demand, with their multipliers.
Bills of materials
/inventory/bom/{parent_sku}: an assembled product's components; GET /inventory/production-requirements explodes demand into components.
Shrinkage
GET / POST /inventory/shrinkage and its reasons.
Money
/inventory/cash-calendar, /inventory/roi, /inventory/roi/monthly.
Report
GET /inventory/report/pdf downloads the inventory report as a PDF.