Almost everything you do inside StockAI can also be done through the API: upload sales, train and read the forecast, read the traffic light, record orders, manage suppliers and warehouses. Connect it to your ERP, your POS or any system of your own.
Every call carries an API key in the Authorization header. Keys are created in the app, under Automation → API Keys, and shown once: StockAI stores only a hash.
Also creates and changes, like an analyst: uploads files, trains, records orders, edits inventory and suppliers.
A read key that tries to write gets 403 with error_code api_key_scope_insufficient, and nothing changes.
What a key can never do
Some actions belong to people, not systems. With an API key they answer 403 api_key_route_not_exposed:
Sign in, refresh tokens or recover passwords.
Create, invite or change users and their permissions.
Create, list or revoke API keys: a key cannot mint another.
Configure the installation or the account's channels.
Export or delete the account.
What belongs to one person: their inbox, their assistant conversations, their preferences.
What only an administrator can do (for example, change the currency or the active period).
Sending an order or an alert by email or WhatsApp with a write key requires the account to have at least one administrator with a verified email (otherwise: 403 api_key_tenant_unverified).
Limits
Authentication
/ min
120 calls per minute per key.
API
The API, the MCP server and the WhatsApp bot come with the Pro, Max and Corporate plans; the Free plan does not include them.
Over the limit you get 429 with a Retry-After header (seconds). The same 429 serves the per-minute limit and the daily cap, and its body has no error_code.
There are no X-RateLimit-* headers: the only signal is the 429 with Retry-After. Count your own calls or wait for the 429.
Metering and purchase
The API comes with the Pro (2,000 calls a day per key), Max (20,000) and Corporate (no daily cap) plans. We count every API-key call that reached an endpoint, by day (UTC) and by key; refused calls (invalid key, no permission or over the limit) do not count. An administrator sees the month's usage in the app, on the API screen.
Almost every error carries a stable error_code and, where it applies, error_params with the values: branch on the code, not on the detail text, which is an English hint and may change. The framework's own answers (missing header, unknown route, wrong method) and the 429 carry only detail.
api_key_invalid
401 the key does not exist, was revoked or has expired.
api_key_route_not_exposed
403 that endpoint cannot be called with an API key.
api_key_scope_insufficient
403 the key is read-only and the endpoint writes.
api_key_tenant_unverified
403 sending anything outside StockAI needs a verified administrator.
validation_error
422 the body or parameters are invalid; detail is a list with one object per field.
PLAN_LIMIT_REACHED
403 a usage ceiling was reached; detail is an object and error_params says which.
server_busy
503 the server is saturated; retry after Retry-After.
(no error_code)
401 the Authorization header is missing or is not “Bearer sk_live_…”: detail is “Not authenticated”.
(no error_code)
429 per-minute limit or daily cap: wait Retry-After seconds.
(no error_code)
404 / 405 unknown route or method not allowed.
The shape of detail
string
Most errors: one English sentence.
list
422 validation_error: [{ type, loc, msg, input }], one per invalid field; loc says where (for example ["body", "name"]).
object
PLAN_LIMIT_REACHED: { code, limit, current, max, tier }, the same content as error_params.
403 Example: a read key that writes
{"detail":"This endpoint writes and the API key is read-only. Use a write key.","error_code":"api_key_scope_insufficient","error_params":{"required_scope":"write","key_scope":"read"}}
Response format
Every JSON response is wrapped: { "success": true, "data": …, "meta": { "timestamp": … } }. Your payload is in data, which is an object or a list depending on the endpoint; paginated lists carry their list in data.items.
Endpoints that return a file (Excel, PDF, CSV) answer with the file itself, with no wrapper.
The MCP endpoint (POST /mcp) speaks JSON-RPC 2.0 and does not use this wrapper.
Types and formats
Dates
Dates are YYYY-MM-DD; instants are ISO 8601 in UTC, for example 2026-10-01T08:00:00+00:00.
Numbers
Quantities and amounts are JSON numbers, not strings. Amounts are in the account's currency (GET /tenant/currency).
Empty values
A field with no value usually arrives as null instead of being omitted.
Identifiers
Opaque prefixed strings (sess_…, ds_…, sup_…): store them as they are.
Signals
The traffic light uses PEDIR_YA, PEDIR_PRONTO, OK and SOBRESTOCK: fixed values that are not translated.
Pagination
There are three forms, and each endpoint declares its own in its parameters:
skip and limit
Sessions, session summaries, data sources and datasets. The response has items, total, skip and limit.
limit and offset
Alert activity (/alerts/activity). The response has items, total, limit and offset.
limit only
Newest first, up to limit: alerts, order history, setup gaps, shrinkage and retraining history.
no pagination
The rest return the whole set and are narrowed with their filters, for example signal or supplier on /inventory/status.
Idempotency
POST /inventory/log-po and POST /inventory/po accept the Idempotency-Key header: one unique value (a UUID) per order you mean to place. Repeating the request with the same key returns the order that was already created (200 with replayed: true) instead of a second one; the same key with different lines answers 409 po_idempotency_key_reused. Every other write endpoint is not idempotent: before retrying a write whose result you did not see, check whether it was applied.
The same key opens an MCP server at POST /mcp with five read-only tools, so an AI assistant can query your traffic light. By design it writes nothing. GET /mcp answers 405: the server does not open a stream to the client.
Endpoint reference
Generated from the service itself: if an endpoint is listed here, it can be called with an API key. Example responses are real calls to a test account.
POSTExplain a SKU's recommendation
AI narratives
Explain a SKU's recommendation
POST/api/v1/ai/narrative/forecast-explanation
Read keyReturns 200 · application/json
Explains a specific SKU's inventory signal and recommendation in plain language.
Request
Headers
AuthorizationBearer sk_live_…required
Your API key. Always required.
Content-Typeapplication/jsonrequired
Body
application/jsonThe body is required.
objectAn object.
skustringrequired
session_idstringrequired
profilestringoptionaldefault "distributor"
languagestringoptionaldefault "es"
Responses
200 On success
No example response was captured for this endpoint. Every JSON answer comes in this envelope; what data holds depends on the endpoint.
Possible errors
Besides the errors of each endpoint, any call made with a key can answer:
401Authorization header missing, or API key invalid, revoked or expired.
403Key may not call this endpoint, is read-only on a write, or a permission check refused it.
429Per-minute limit or daily ceiling reached; honour Retry-After.
Return hyperparameter schemas for all available models.
Session configuration
Dataset profile (alias)
GET/api/v1/sessions/{session_id}/profile
Read keyReturns 200 · application/json
Alias of GET /sessions/{session_id}/inspect.
Session configuration
Upload and attach a dataset
POST/api/v1/sessions/{session_id}/upload
Write keyReturns 201 · application/json
Uploads a file (multipart) and attaches it to the session in one call.
Currency
Read the currency
GET/api/v1/tenant/currency
Read keyReturns 200 · application/json
Readable by every role: it is needed to render any figure on any screen.
Data sources
List data sources
GET/api/v1/data-sources
Read keyReturns 200 · application/json
The tenant's file and SQL data sources. Paginated with skip and limit.
Data sources
Create a file data source
POST/api/v1/data-sources/file
Write keyReturns 200 · application/json
Uploads a CSV/Excel file (multipart) as a new data source.
Data sources
Create a SQL data source
POST/api/v1/data-sources/sql
Write keyReturns 200 · application/json
Registers a database connection as a data source. Fields may come from a pasted `connection_string` (URL, JDBC, ADO.NET or libpq form); typed fields win. `ssl_mode` is disable, prefer, require, verify-ca or verify-full; `ssl_ca` is the server's CA certificate (PEM), stored encrypted like the password. Private and link-local hosts are refused unless the installation allows them (`data_source_host_not_allowed`).
Data sources
Parse a connection string
POST/api/v1/data-sources/sql/parse
Write keyReturns 200 · application/json
Reads a connection string into engine, host, port, database, user and TLS mode. The password is never returned, only `has_password`.
Data sources
Delete a data source
DEL/api/v1/data-sources/{source_id}
Write keyReturns 200 · application/json
Removes the data source.
Data sources
Get a data source
GET/api/v1/data-sources/{source_id}
Read keyReturns 200 · application/json
One data source's metadata.
Data sources
Rename a data source
PATCH/api/v1/data-sources/{source_id}
Write keyReturns 200 · application/json
Changes the data source's name and description.
Data sources
Analyze a data source
GET/api/v1/data-sources/{source_id}/analyze
Read keyReturns 200 · application/json
Demand statistics over the source: totals, seasonality and per-SKU summaries, optionally within a date range.
Executes ONE read statement (SELECT or WITH ... SELECT) on the customer's database, inside a read-only transaction that is always rolled back, and returns up to `limit` rows starting at `offset`, with `has_more`. Needs a `write`-scope key: it runs caller-written SQL on the customer's database. Anything else is refused with `sql_multiple_statements`, `sql_not_a_select`, `sql_forbidden_keyword`, `sql_forbidden_function` or `sql_unsupported_syntax`.
Data sources
Export a SQL query as Excel or CSV
POST/api/v1/data-sources/{source_id}/export-query
Write keyReturns 200 · application/json
Runs the query (same read-only rules as execute-query) and returns the FULL result as an .xlsx (default) or .csv file, refusing rather than truncating past the row ceiling. Needs a `write`-scope key.
Data sources
Replace a data source's file
POST/api/v1/data-sources/{source_id}/file
Write keyReturns 200 · application/json
Uploads a new file (multipart) IN PLACE: the source keeps its id and column mapping, so the next training run needs no reconfiguration. The nightly-export endpoint.
Data sources
Materialize a SQL query as a dataset
POST/api/v1/data-sources/{source_id}/materialize
Write keyReturns 200 · application/json
Runs the query and stores the result as a dataset that sessions can train on.
Data sources
Preview a data source
GET/api/v1/data-sources/{source_id}/preview
Read keyReturns 200 · application/json
The first rows of the source (and of a given sheet, for Excel).
Data sources
Save a SQL source's query
PATCH/api/v1/data-sources/{source_id}/query
Write keyReturns 200 · application/json
Stores the query the SQL source materializes.
Data sources
Save an edited table as a new source
POST/api/v1/data-sources/{source_id}/save-as-new
Write keyReturns 200 · application/json
Creates a new data source from edited columns and rows.
Data sources
List a SQL source's tables
GET/api/v1/data-sources/{source_id}/schema
Write keyReturns 200 · application/json
Tables and views the connection can read, with row estimates and a safely quoted preview statement for each. Cached for a few minutes; `refresh=true` re-reads.
Column names, types and nullability of one table of a SQL source.
Data sources
Update a SQL source's connection
PATCH/api/v1/data-sources/{source_id}/sql-config
Write keyReturns 200 · application/json
Changes any connection field of a SQL source. Omitted fields keep their stored value, the password included, except when the engine, host or port changes: then the password must be sent again (`data_source_password_required`).
Runs a staged test (dns, tcp, tls, auth, privileges, select, tables) and reports each stage as ok, warning, failed or skipped with a code. Warns when the login can write, with the least-privilege GRANT statements.
Datasets
List datasets
GET/api/v1/datasets
Read keyReturns 200 · application/json
Uploaded datasets. Paginated with skip and limit.
Datasets
Upload a dataset
POST/api/v1/datasets
Write keyReturns 201 · application/json
Uploads a sales-history file (multipart) and profiles it.
Datasets
Get a dataset
GET/api/v1/datasets/{dataset_id}
Read keyReturns 200 · application/json
One dataset's metadata and profile.
Documents
List documents
GET/api/v1/documents
Read keyReturns 200 · application/json
Documents uploaded for the analyst to read.
Documents
Upload a document
POST/api/v1/documents
Write keyReturns 200 · application/json
Upload a document (PDF, DOCX, TXT up to 50 MB). File is saved to storage and indexed asynchronously. Returns doc metadata with status=PENDING immediately.
Documents
Delete a document
DEL/api/v1/documents/{doc_id}
Write keyReturns 200 · application/json
Removes the document and its index.
Documents
Get a document
GET/api/v1/documents/{doc_id}
Read keyReturns 200 · application/json
One document's metadata.
Documents
Download a document
GET/api/v1/documents/{doc_id}/content
Read keyReturns 200 · application/json
Serve the raw file for in-browser viewing or download.
Documents
Document indexing status
GET/api/v1/documents/{doc_id}/status
Read keyReturns 200 · application/json
Whether the document has been processed and indexed.
Usage limits
Read plan and limits
GET/api/v1/entitlements
Read keyReturns 200 · application/json
This tenant's tier, its ceilings, and how close it is to them.
Forecasts
Configuration schema
GET/api/v1/config/schema
Read keyReturns 200 · application/json
The JSON schema of every configuration block, independent of a session.
Forecasts
Available forecasting models
GET/api/v1/models/available
Read keyReturns 200 · application/json
The forecasting models this deployment can train.
Forecasts
Forecast accuracy
GET/api/v1/sessions/{session_id}/accuracy
Read keyReturns 200 · application/json
Accuracy of the session's forecasts against baselines, per SKU and overall.
The latest automatic reading of how this forecast is doing against the sales uploaded after it was made, next to its accuracy at training time.
Forecasts
Models available to a session
GET/api/v1/sessions/{session_id}/available-models
Read keyReturns 200 · application/json
The models that can be trained on this session's data.
Forecasts
Start Backtest
POST/api/v1/sessions/{session_id}/backtest
Write keyReturns 202 · application/json
Train a back-test of a completed session: the same configuration on a copy of its dataset with the last `holdout_periods` periods held out. Its forecast then covers those periods and can be graded against the full dataset with `forecast-vs-actual`. The source session and dataset are not modified; the run counts against the plan's saved-forecast ceiling and is refused (nothing deleted) when that is full.
Forecasts
Session configuration schema
GET/api/v1/sessions/{session_id}/config-schema
Read keyReturns 200 · application/json
The configuration schema with this session's current values.
History and forecast (with quantiles) for one SKU, optionally for a given model.
Forecasts
Get Forecast Total
GET/api/v1/sessions/{session_id}/forecast-total
Read keyReturns 200 · application/json
The whole catalogue as one series: every SKU's champion forecast and history summed per date. Feeds the session-comparison chart's "all SKUs" option, where two sessions are only comparable as totals.
What this session predicted vs. what the tenant's LATER sales uploads say actually happened: per-SKU and pooled WAPE / MAPE / bias, the predicted-vs- actual series, and a verdict code the frontend renders in words.
Forecasts
Session inventory recommendations
GET/api/v1/sessions/{session_id}/inventory
Read keyReturns 200 · application/json
Per-SKU inventory recommendations computed from this session's forecast.
Forecasts
Training metrics
GET/api/v1/sessions/{session_id}/metrics
Read keyReturns 200 · application/json
Validation metrics per model and SKU.
Forecasts
List forecast overrides
GET/api/v1/sessions/{session_id}/overrides
Read keyReturns 200 · application/json
Manual adjustments made to the session's forecast.
Forecasts
Save forecast overrides
PATCH/api/v1/sessions/{session_id}/overrides
Write keyReturns 200 · application/json
Upserts manual forecast values per SKU and date, with a reason. The body is a list.
Forecasts
Predict on demand
POST/api/v1/sessions/{session_id}/predict
Read keyReturns 200 · application/json
Runs the trained model for one SKU with the given inputs and horizon.
Forecasts
Data quality
GET/api/v1/sessions/{session_id}/quality
Read keyReturns 200 · application/json
Data-quality findings for the session's dataset.
Forecasts
Reconcile actual values
POST/api/v1/sessions/{session_id}/reconcile
Read keyReturns 200 · application/json
Upload a CSV of actual values to compare against the stored forecast.
Forecasts
Session report (alias)
GET/api/v1/sessions/{session_id}/report
Read keyReturns 200 · application/json
The session's results summarized as one report document.
Forecasts
Training results
GET/api/v1/sessions/{session_id}/results
Read keyReturns 200 · application/json
Forecasts and per-model results of a completed session, at SKU or aggregate level.
Forecasts
Model routing
GET/api/v1/sessions/{session_id}/routing
Read keyReturns 200 · application/json
Which model was routed to each SKU and why.
Forecasts
Model routing plan (alias)
GET/api/v1/sessions/{session_id}/routing-plan
Read keyReturns 200 · application/json
The routing decisions as a plan, per SKU segment.
Forecasts
Read a SKU's feature importances
GET/api/v1/sessions/{session_id}/shap/{sku}
Read keyReturns 200 · application/json
Return SHAP feature importances for the best model for the given SKU.
Everything known about one SKU in the session: series, model, drivers and recommendation.
Forecasts
List run warnings
GET/api/v1/sessions/{session_id}/warnings
Read keyReturns 200 · application/json
Data problems the validation layers found while training this session.
Data freshness
Read data freshness
GET/api/v1/data-freshness
Read keyReturns 200 · application/json
Age of the sales history and of the stock table, plus whether the semáforo may still claim a colour.
Inventory, purchasing and suppliers
Send the daily alert now
POST/api/v1/inventory/alerts/send-now
Write keyReturns 202 · application/json
Fire the daily inventory alert immediately for this tenant — email to the admins, WhatsApp to the ones who opted in. Lets the user verify their channels without waiting for the 8:00 UTC scheduler run.
Inventory, purchasing and suppliers
List where a component is used
GET/api/v1/inventory/bom/{child_sku}/used-in
Read keyReturns 200 · application/json
Returns all finished goods that use this component.
Inventory, purchasing and suppliers
Read a bill of materials
GET/api/v1/inventory/bom/{parent_sku}
Read keyReturns 200 · application/json
Returns BOM (Bill of Materials) for a finished good.
Inventory, purchasing and suppliers
Remove a bill-of-materials line
DEL/api/v1/inventory/bom/{parent_sku}/{child_sku}
Write keyReturns 204 · application/json
Removes a component from a parent SKU's bill of materials.
Inventory, purchasing and suppliers
Set a bill-of-materials line
PUT/api/v1/inventory/bom/{parent_sku}/{child_sku}
Write keyReturns 200 · application/json
Creates or updates how much of a component one unit of the parent SKU uses.
Inventory, purchasing and suppliers
Import stock from a file
POST/api/v1/inventory/bulk
Write keyReturns 200 · application/json
Import stock from a CSV or Excel file.
Inventory, purchasing and suppliers
Preview a stock import
POST/api/v1/inventory/bulk/preview
Write keyReturns 200 · application/json
Dry run of POST /bulk: what we detected in the file and what we would write, without touching a single row. This is what makes the mapping wizard possible — the user corrects our column guesses BEFORE importing, the same way the sales upload works.
Inventory, purchasing and suppliers
Read the cash calendar
GET/api/v1/inventory/cash-calendar
Read keyReturns 200 · application/json
Invoices falling due from POs already sent, dated by each supplier's credit terms. Read-only, so viewers may call it.
Inventory, purchasing and suppliers
Fit a purchase into a budget
POST/api/v1/inventory/cash-calendar/fit
Read keyReturns 200 · application/json
"Does the recommended purchase fit in the cash I have?"
Inventory, purchasing and suppliers
Read the dashboard summary
GET/api/v1/inventory/dashboard-summary
Read keyReturns 200 · application/json
Lightweight endpoint for the dashboard widget. Returns only the summary counts without the full item list.
Inventory, purchasing and suppliers
List dead capital
GET/api/v1/inventory/dead-capital
Read keyReturns 200 · application/json
Every SKU on hand whose stock level has not fallen in at least `window_days`, ranked worst first by money, with the tenant's total at the top. Needs no session — it reads real stock-level history, not a forecast.
Inventory, purchasing and suppliers
List demand events
GET/api/v1/inventory/events
Read keyReturns 200 · application/json
Promotions and seasonal events that multiply expected demand.
Inventory, purchasing and suppliers
Create a demand event
POST/api/v1/inventory/events
Write keyReturns 201 · application/json
Adds an event with a date range and a demand multiplier.
Inventory, purchasing and suppliers
Read the event catalog
GET/api/v1/inventory/events/catalog
Read keyReturns 200 · application/json
Which commercial events StockAI knows for a country, and whether this tenant has them seeded / switched on. Read-only.
Inventory, purchasing and suppliers
Seed the event catalog
POST/api/v1/inventory/events/catalog/seed
Write keyReturns 200 · application/json
Preload the LatAm commercial calendar into this tenant's events.
Drop the override: the product falls back to the event multiplier.
Inventory, purchasing and suppliers
Project revenue, cost and margin
GET/api/v1/inventory/forecast-money
Read keyReturns 200 · application/json
Projected revenue, cost and gross margin over the active session's forecast horizon, per SKU and in total, ranked so the top contributors are visible — "your next N days: X in sales, Y in margin, and these products carry it."
Inventory, purchasing and suppliers
Record a purchase order
POST/api/v1/inventory/log-po
Write keyReturns 201 · application/json
Records that an order was placed, with its lines. Reception tracking and supplier lead-time learning read it. A body without `items` records every actionable SKU of the session.
Inventory, purchasing and suppliers
List margin erosion
GET/api/v1/inventory/margin-erosion
Read keyReturns 200 · application/json
SKUs whose margin eroded because their cost rose while the product's sale_price is the only price StockAI has ever stored. `price_history_available: false` in the response is load-bearing: the "before" margin is today's price against a past cost, not a historical fact — see `cost_alerts.get_margin_erosion`'s docstring.
Inventory, purchasing and suppliers
Read the morning briefing
GET/api/v1/inventory/morning-briefing
Read keyReturns 200 · application/json
Daily operations briefing: risks, recommendations, demand changes, KPIs. Designed to be the first thing a manager opens every morning. Reflects the tenant's ACTIVE planning period — coverage and the signal in that unit — so /hoy agrees with /inventory (a weekly session must be read as weekly).
Inventory, purchasing and suppliers
Optimize purchases and transfers
GET/api/v1/inventory/optimize
Read keyReturns 200 · application/json
Runs the MILP purchasing/transfers optimizer for this session and returns suggested purchase quantities per SKU x warehouse, plus recommended inter-warehouse transfers, collapsed to one total per line over the full horizon.
Inventory, purchasing and suppliers
Create a manual purchase order
POST/api/v1/inventory/po
Write keyReturns 201 · application/json
A purchase order the buyer writes from scratch — supplier chosen explicitly, lines typed in, no forecast session behind it. Persisted with source='manual' so adoption metrics stay clean. `Idempotency-Key`: same contract as /log-po.
Inventory, purchasing and suppliers
List recent purchase orders
GET/api/v1/inventory/po-history
Read keyReturns 200 · application/json
Returns recent PO generation events for the history panel.
Inventory, purchasing and suppliers
Po History Page
GET/api/v1/inventory/po-history/page
Read keyReturns 200 · application/json
The PO history, filtered and paged on the server. `total` counts the filtered set; `awaiting_reception` counts every open order of the tenant.
Inventory, purchasing and suppliers
Import purchase orders from a file
POST/api/v1/inventory/po/import
Write keyReturns 200 · application/json
Create purchase orders from a CSV / Excel file, one order per (order reference, supplier, destination warehouse).
Inventory, purchasing and suppliers
Preview a purchase-order import
POST/api/v1/inventory/po/import/preview
Write keyReturns 200 · application/json
Dry run of POST /po/import: the orders the file would create, and every row whose supplier / product / warehouse did not resolve. Writes nothing.
Inventory, purchasing and suppliers
Download the purchase-order import template
GET/api/v1/inventory/po/import/template
Read keyReturns 200 · application/json
Purchase-order import template: header row plus example lines (two orders, the first with two lines).
Inventory, purchasing and suppliers
List overdue purchase orders
GET/api/v1/inventory/po/overdue
Read keyReturns 200 · application/json
POs still pending/partial whose expected arrival — order date plus the supplier's already-learned lead time — has passed with no reception recorded. Powers the /hoy 'did it arrive?' nudge.
Inventory, purchasing and suppliers
List a purchase order's lines
GET/api/v1/inventory/po/{po_log_id}/items
Read keyReturns 200 · application/json
Lines of a PO with ordered vs received quantities (reception form).
Inventory, purchasing and suppliers
Receive a purchase order
POST/api/v1/inventory/po/{po_log_id}/receive
Write keyReturns 200 · application/json
Record that a PO arrived (fully, partially, or not at all). Side effects: current_stock increases by the received units, and StockAI logs the supplier's REAL lead time (order date → reception date).
Inventory, purchasing and suppliers
Send a purchase order to suppliers
POST/api/v1/inventory/po/{po_log_id}/send
Write keyReturns 200 · application/json
Sends a PO's PDF to each of its suppliers by email and WhatsApp, grouping the PO's lines by supplier name (a PO can span more than one supplier). Lines with no supplier name, or whose supplier has no saved contact info, are skipped and reported back — never a 500.
Inventory, purchasing and suppliers
List price breaks
GET/api/v1/inventory/price-breaks
Read keyReturns 200 · application/json
Supplier quantity scales, optionally filtered by supplier and/or SKU.
Inventory, purchasing and suppliers
Evaluate price breaks for a cart
POST/api/v1/inventory/price-breaks/evaluate
Read keyReturns 200 · application/json
Given the cart the buyer currently has on screen, which lines are one step away from a better unit price AND would still be a good idea to step up to.
The valid product types, as the English keys the frontend translates.
Inventory, purchasing and suppliers
Explode demand into components
GET/api/v1/inventory/production-requirements
Read keyReturns 200 · application/json
MRP Level 1 explosion: given forecast demand + BOM, returns required quantities of each component and raw material, flagging shortages and purchase requirements.
Inventory, purchasing and suppliers
Download the inventory PDF
GET/api/v1/inventory/report/pdf
Read keyReturns 200 · application/json
Generates and streams a one-page executive PDF inventory summary.
Inventory, purchasing and suppliers
Read accumulated ROI
GET/api/v1/inventory/roi
Read keyReturns 200 · application/json
Returns accumulated ROI metrics across all time.
Inventory, purchasing and suppliers
Read one month's ROI report
GET/api/v1/inventory/roi/month-report
Read keyReturns 200 · application/json
Recap of a single calendar month (feature 3.2). Defaults to the month that just closed — the same period the monthly recap email covers.
Inventory, purchasing and suppliers
List monthly ROI
GET/api/v1/inventory/roi/monthly
Read keyReturns 200 · application/json
Last N months: orders, stockout risks handled, adoption, capital freed from overstock.
Inventory, purchasing and suppliers
List SKUs missing setup
GET/api/v1/inventory/setup-gaps
Read keyReturns 200 · application/json
Unconfigured SKUs ordered by the money they move, with the running cumulative share of projected spend.
Inventory, purchasing and suppliers
List shrinkage
GET/api/v1/inventory/shrinkage
Read keyReturns 200 · application/json
Recent history of recorded shrinkage (input to the future monthly summary).
Inventory, purchasing and suppliers
Record shrinkage
POST/api/v1/inventory/shrinkage
Write keyReturns 201 · application/json
Record a stock-out that is NOT a sale — breakage, expiry, self-consumption or a gift/sample. Decrements the SKU's theoretical stock through the same path PO reception uses (so the signal stays accurate) and accumulates the cost (quantity x unit cost) for a future monthly shrinkage summary.
Inventory, purchasing and suppliers
List shrinkage reasons
GET/api/v1/inventory/shrinkage/reasons
Read keyReturns 200 · application/json
Returns the valid shrinkage reason codes (labels are handled client-side via i18n).
Inventory, purchasing and suppliers
Read the inventory traffic light
GET/api/v1/inventory/status
Read keyReturns 200 · application/json
Returns per-SKU inventory status in the tenant's ACTIVE planning period:
- coverage (in the active period's units — see `coverage_unit`)
- traffic-light signal (PEDIR_YA / PEDIR_PRONTO / OK / SOBRESTOCK / SIN_DATOS)
- recommended order quantity
- inventory value
Inventory, purchasing and suppliers
Export the purchase order as CSV
GET/api/v1/inventory/status/export-po
Read keyReturns 200 · application/json
Export purchase order as CSV, filtered to actionable SKUs.
Inventory, purchasing and suppliers
List stock
GET/api/v1/inventory/stock
Read keyReturns 200 · application/json
Every SKU's stock record: on hand, minimum, lead time, cost, supplier and catalogue fields.
Inventory, purchasing and suppliers
List Counts
GET/api/v1/inventory/stock-counts
Read keyReturns 200 · application/json
List stock counts, newest first, with how many products each holds.
Inventory, purchasing and suppliers
Create Count
POST/api/v1/inventory/stock-counts
Write keyReturns 201 · application/json
Open a count session for one warehouse, optionally scoped to a category or supplier.
Inventory, purchasing and suppliers
Get Count
GET/api/v1/inventory/stock-counts/{count_id}
Read keyReturns 200 · application/json
One count with every line (counted quantity next to the system quantity at the first scan).
Write the differences into stock, exactly once, and record each adjustment with reason 'physical_count'. Omit skus to apply every line; a list applies only those. All or nothing.
Sets the supplier's cost, MOQ, lead time and primary flag for the SKU.
Inventory, purchasing and suppliers
List supplier cost inflation
GET/api/v1/inventory/supplier-cost-inflation
Read keyReturns 200 · application/json
Suppliers who raised a SKU's cost at least once in the window, worst first, with the products each one hit hardest. Built only from POs that were actually received — a quoted or rejected order proves nothing was paid.
Inventory, purchasing and suppliers
List suppliers
GET/api/v1/inventory/suppliers
Read keyReturns 200 · application/json
The tenant's suppliers with contact details, lead times and payment terms.
Inventory, purchasing and suppliers
Create a supplier
POST/api/v1/inventory/suppliers
Write keyReturns 201 · application/json
Adds a supplier with contact details, lead time and payment terms.
Inventory, purchasing and suppliers
Check supplier contact details
GET/api/v1/inventory/suppliers/contact-health
Read keyReturns 200 · application/json
Suppliers that POST /po/{id}/send would silently skip — no email and no WhatsApp on file, or a supplier name on PO lines with no record at all (feature 2.5).
Inventory, purchasing and suppliers
Import suppliers from a file
POST/api/v1/inventory/suppliers/import
Write keyReturns 200 · application/json
Create suppliers from a CSV / Excel file.
Inventory, purchasing and suppliers
Preview a suppliers import
POST/api/v1/inventory/suppliers/import/preview
Write keyReturns 200 · application/json
Dry run of POST /suppliers/import: what was detected and what would be written. Touches no row.
Inventory, purchasing and suppliers
Download the suppliers import template
GET/api/v1/inventory/suppliers/import/template
Read keyReturns 200 · application/json
Suppliers import template: the header row plus two example rows.
Inventory, purchasing and suppliers
List supplier lead-time alerts
GET/api/v1/inventory/suppliers/lead-time-alerts
Read keyReturns 200 · application/json
Suppliers whose recent lead time is significantly slower than their own history — "Acme is taking 12 days, not 7" (feature 3.3).
Inventory, purchasing and suppliers
List Suppliers Page
GET/api/v1/inventory/suppliers/page
Read keyReturns 200 · application/json
Active suppliers, searched and paged on the server (the plain list stays whole for the dropdowns that need every supplier).
Inventory, purchasing and suppliers
Read the supplier scorecard
GET/api/v1/inventory/suppliers/scorecard
Read keyReturns 200 · application/json
Per-supplier performance: real lead time range, on-time rate, fill rate.
Inventory, purchasing and suppliers
Deactivate a supplier
DEL/api/v1/inventory/suppliers/{supplier_id}
Write keyReturns 204 · application/json
Deactivates the supplier (see the reactivate endpoint).
Inventory, purchasing and suppliers
Update a supplier
PATCH/api/v1/inventory/suppliers/{supplier_id}
Write keyReturns 200 · application/json
Changes a supplier's contact details, lead time, review period or payment terms.
Records the quantities that arrived at the destination warehouse.
Inventory, purchasing and suppliers
List warehouses
GET/api/v1/inventory/warehouses
Read keyReturns 200 · application/json
The tenant's warehouses.
Inventory, purchasing and suppliers
Create a warehouse
POST/api/v1/inventory/warehouses
Write keyReturns 201 · application/json
Adds a warehouse, optionally as the default.
Inventory, purchasing and suppliers
Delete a transfer lane
DEL/api/v1/inventory/warehouses/lanes
Write keyReturns 204 · application/json
Names travel as query params, not path segments: a warehouse name may contain a slash and would break path matching once encoded.
Inventory, purchasing and suppliers
List transfer lanes
GET/api/v1/inventory/warehouses/lanes
Read keyReturns 200 · application/json
Configured lanes only. A pair with no row falls back to the documented default (lead_time_days=1, cost_per_unit=0, fixed_cost=0) everywhere it is consumed — see backend/inventory/transfer_lane_service.py.
Inventory, purchasing and suppliers
Set a transfer lane
PUT/api/v1/inventory/warehouses/lanes
Write keyReturns 200 · application/json
Creates or updates the lead time and costs of moving stock between two warehouses.
Inventory, purchasing and suppliers
Update a warehouse
PATCH/api/v1/inventory/warehouses/{name}
Write keyReturns 200 · application/json
Set or clear the manual demand share for one warehouse (feature 5.4).
Every SKU that carried an ordering signal (PEDIR_YA/PEDIR_PRONTO) in the window: whether a purchase order followed, and — only where it did not and stock was later observed at or below zero — the estimated unserved units and their value. See `recommendation_reports.cost_of_ignoring` for the conservatism rules (no PO + no observed stockout => no loss claimed; no sale price => units reported, value null).
The SKU's latest recorded recommendation against the previous recorded one, decomposed into avg daily demand, lead time, stock on hand and safety stock — each tagged with whether it came from a new training session (a model opinion) or the tenant's own operational data. Returns `available: false` with a `reason` when there is not yet a previous row to compare against.
Order reversals
Undo a purchase-order reception
POST/api/v1/inventory/po/{po_log_id}/unreceive
Write keyReturns 200 · application/json
Undo a reception: take the received units back out of stock, reset the PO line's `received_qty` and the order's `reception_status`, and remove the lead-time observation(s) that reception taught the supplier scorecard. Refuses (409, with the offending SKU/warehouse named) rather than go negative if some of the received units are no longer in stock. See `reception_service.unreceive_po` for the full reasoning.
Order reversals
Undo marking a purchase order as sent
POST/api/v1/inventory/po/{po_log_id}/unsend
Write keyReturns 200 · application/json
Undo `mark_po_sent`: clear `sent_at` so the order stops counting as incoming stock and drops off the cash-payables calendar. Does NOT recall the email/WhatsApp message a prior `/send` may have delivered — that left the system and nothing can pull it back. Refuses once a reception exists against this PO (undo that first). See `reception_service.unsend_po`.
inventory-sync
Draft Doctor
POST/api/v1/inventory-sync/doctor
Write keyReturns 200 · application/json
The connection doctor for an unsaved configuration.
inventory-sync
Dry Run Unsaved
POST/api/v1/inventory-sync/dry-run
Write keyReturns 200 · application/json
Preview a configuration that is not saved yet. Writes nothing.
inventory-sync
List Presets
GET/api/v1/inventory-sync/presets
Write keyReturns 200 · application/json
ERP recipe presets (SAP, Oracle EBS, Dynamics, Business Central, JD Edwards, Infor M3, Odoo, Magento, WooCommerce, generic SCD2 / UNION ledgers): parameters, stock model, the gotchas each one guards against, and whether it was verified against a live system. Also the systems read another way (push, or a warehouse through an existing engine).
The profile body a preset produces for one engine and its parameters (client, company, organisations...). Nothing is saved: the body goes through the wizard's dry run like any other.
inventory-sync
Create Profile
POST/api/v1/inventory-sync/profiles
Write keyReturns 201 · application/json
Create a profile (off until previewed and enabled). Send `Idempotency-Key` to make a retried POST create it once.
inventory-sync
Apply Document
POST/api/v1/inventory-sync/profiles/apply
Write keyReturns 200 · application/json
Apply a profile document (JSON or YAML): created, or updated by its `slug` — idempotent, so CI can re-apply the file on every deploy and the second time answers `action: unchanged`. Errors name the exact path (`mapping.current_stock.cast: 'int' is not one of ...`). `dry_run=true` validates and previews without saving.
inventory-sync
Validate Document
POST/api/v1/inventory-sync/profiles/validate
Write keyReturns 200 · application/json
Validate a profile document without touching anything: every problem with its path.
inventory-sync
Delete Profile
DEL/api/v1/inventory-sync/profiles/{profile_id}
Write keyReturns 200 · application/json
Delete a profile with its run log. The stock it synced stays as it is.
inventory-sync
Update Profile
PATCH/api/v1/inventory-sync/profiles/{profile_id}
Write keyReturns 200 · application/json
Change a profile. Changing what it writes turns it off until previewed again (`disabled_by_edit: true`).
The connection doctor for a saved profile: source, connection (dns, tcp, tls, auth, privileges, select, tables), object, columns, sample read, row estimate, index on the incremental column (with the CREATE INDEX to run), supported change capture. `first_failure` names the stage and code; `diagnostic` is a secret-free bundle to paste to a DBA.
'Sync now'. Answers at once with the run row (status `running`); poll `GET .../runs/{run_id}` for the outcome. `override_brake` runs once past the safety brake (audited). With `Idempotency-Key`, a retry does not start a second run.
inventory-sync
Recipes
GET/api/v1/inventory-sync/recipes
Write keyReturns 200 · application/json
Copy-paste material: least-privilege read-only GRANTs per engine (or for one data source), StockAI's egress IPs, example queries for common ERP schemas, a `stockai_inventory` view template and push forwarders.
inventory-sync
Refresh
POST/api/v1/inventory-sync/refresh
Write keyReturns 200 · application/json
'Refresh now': make the displayed stock current. Coalesces (a refresh that finds a run in flight joins it), honours a per-profile cooldown (`status: cooldown`, `retry_after_s`), refreshes only `skus` when given, and waits at most `wait_seconds` before answering `status: running` with the `data_as_of` of the last success — the stock shown is never blanked. Paused or blocked profiles answer with their reason instead of running.
The CREATE VIEW stockai_inventory statement (or MongoDB view pipeline) for this source and mapping: hand it to the DBA, then map the view at confidence 1.0. StockAI never runs it.
inventory-sync
Transform Test
POST/api/v1/inventory-sync/transform-test
Write keyReturns 200 · application/json
Try a field's transforms on one sample value: the output, or the rejection code a run would report.
inventory-sync-status
Get Fields
GET/api/v1/inventory-sync/fields
Read keyReturns 200 · application/json
The canonical fields, transforms, policies and limits the editor offers.
inventory-sync-status
Get Freshness
GET/api/v1/inventory-sync/freshness
Read keyReturns 200 · application/json
How current the stock is: `state` is `fresh`, `stale` (a profile has not succeeded within its expected window), `blocked` (a profile is paused or blocked and needs someone) or `none` (no sync). `data_as_of` is the oldest last success among live profiles: the stock is at least that fresh. Each profile lists its own state and the reason code when not fresh.
inventory-sync-status
Tenant Health
GET/api/v1/inventory-sync/health
Read keyReturns 200 · application/json
Every profile's health in one call; `status` is the worst of them.
inventory-sync-status
Overview
GET/api/v1/inventory-sync/overview
Read keyReturns 200 · application/json
The sync screen's one answer: is the stock current, and what is waiting for a person. `state` is none / waiting / fresh / attention / blocked; `attention` lists what only a person can settle (a stock file held for a decision, a sync the safety brake paused, a source that refuses us, a sync that is late, rows a run refused), worst first, each with the ids the action needs; `sources` is every database, file-drop and push source with its freshness and last run. Unlike `/health` it never answers 503: it is for reading, not for an uptime checker.
inventory-sync-status
Get Ownership
GET/api/v1/inventory-sync/ownership
Read keyReturns 200 · application/json
Which stock fields of which (sku, warehouse) a live sync will overwrite at its next run, and which sync: the editors warn before a person saves a value the source will replace (the source wins; the replacement is counted and audit-logged). `items[].fields` are the fields the owning profile writes; `profiles[]` carries its name and `next_sync_at` (null for a push sync or an on-demand one: nobody can tell when it will write).
inventory-sync-status
List Profiles
GET/api/v1/inventory-sync/profiles
Read keyReturns 200 · application/json
Every inventory sync profile with its status: enabled, last success and its age, `stale` (no success within twice the interval), `braked` (the safety brake stopped the last run), the last error code and the fields the source owns.
inventory-sync-status
Get Profile
GET/api/v1/inventory-sync/profiles/{profile_id}
Read keyReturns 200 · application/json
One inventory sync profile with the same status fields as the list.
Machine-readable health for monitoring: `status` ok / stale / degraded / paused / blocked, `http_status` 200 or 503, the reason code with its category and whether it retries on its own, the last-success age and the next run. `strict=true` answers with that HTTP status itself (a 503 for an uptime checker), the same body either way.
The run log of a profile, newest first: rows read, created, updated, unchanged and rejected, the outcome (success / failed / braked), the error code and the watermark each run started from and reached.
The run's rejected rows as CSV (row, sku, code, params): bounded to the sample the run kept.
inventory-sync-status
Profile Schema
GET/api/v1/inventory-sync/schema/profile.json
Read keyReturns 200 · application/json
The JSON Schema (draft 2020-12) of a profile document, `stockai.inventory-sync/v1`. The importer validates against the same spec.
MCP (AI clients)
Call the MCP server (JSON-RPC)
POST/api/v1/mcp
Read keyReturns 200 · application/json
Model Context Protocol endpoint, for an AI client the customer runs.
Planning
Planning context
GET/api/v1/planning
Read keyReturns 200 · application/json
The active planning period and `active_session_id` — the session the app's own screens show.
purchase-orders-erp
Export Feed
GET/api/v1/purchase-orders/export
Read keyReturns 200 · application/json
Approved orders changed after `since`, oldest change first, with a cursor to resume from. A cancelled order comes back with `state = "cancelled"`. An order still waiting for approval is not here until it is approved.
purchase-orders-erp
Export File
GET/api/v1/purchase-orders/export/file
Read keyReturns 200 · application/json
The chosen orders as the file the ERP imports. One document per supplier.
purchase-orders-erp
Export Formats
GET/api/v1/purchase-orders/export/formats
Read keyReturns 200 · application/json
The ERP file layouts, and which of them have been run against a real system.
purchase-orders-erp
Unacknowledged
GET/api/v1/purchase-orders/unacknowledged
Read keyReturns 200 · application/json
Open approved orders whose ERP document number nobody has written back.
Run a saved scenario and return the BASE vs SCENARIO comparison.
Scheduled retraining
List retraining schedules
GET/api/v1/schedules
Read keyReturns 200 · application/json
Every schedule this tenant has, with the session's name.
Scheduled retraining
List scheduler history
GET/api/v1/schedules/history
Read keyReturns 200 · application/json
What the scheduler has actually done, newest first.
Scheduled retraining
Delete a retraining schedule
DEL/api/v1/sessions/{session_id}/schedule
Write keyReturns 200 · application/json
Stops the session's scheduled retraining.
Scheduled retraining
Get a retraining schedule
GET/api/v1/sessions/{session_id}/schedule
Read keyReturns 200 · application/json
The session's cron schedule for automatic retraining.
Scheduled retraining
Save a retraining schedule
POST/api/v1/sessions/{session_id}/schedule
Write keyReturns 200 · application/json
Sets a cron expression for automatic retraining, and whether it is enabled.
Sessions
List sessions
GET/api/v1/sessions
Read keyReturns 200 · application/json
Forecast sessions with their state. Paginated with skip and limit.
Sessions
Create a session
POST/api/v1/sessions
Write keyReturns 201 · application/json
Creates a forecast session in DRAFT.
Sessions
List session summaries
GET/api/v1/sessions/summary
Read keyReturns 200 · application/json
The sessions library: every session of the tenant, enriched (dataset name, horizon, SKU count, granularity, headline accuracy, models), searchable, filterable, sortable and paginated. `total` counts the rows matching the filters, so a pager can be drawn.
Sessions
Delete a session
DEL/api/v1/sessions/{session_id}
Write keyReturns 204 · application/json
Deletes the session and its results.
Sessions
Get a session
GET/api/v1/sessions/{session_id}
Read keyReturns 200 · application/json
One session's metadata and state.
Sessions
Update a session
PATCH/api/v1/sessions/{session_id}
Write keyReturns 200 · application/json
Changes a session's name, description or tags.
Sessions
Get Manifest
GET/api/v1/sessions/{session_id}/manifest
Read keyReturns 200 · application/json
How this forecast was produced: who or what started the run, the dataset's content hash and size, the full configuration, engine and library versions, per-model outcomes, stage timings and a hash of the forecast. Written once when the run ends and never changed. 404 `manifest_not_available` for a session that has not finished a run since manifests were introduced.
Sessions
Restore Session
POST/api/v1/sessions/{session_id}/restore
Write keyReturns 200 · application/json
Bring an archived session back into the working list. Counts against the plan's saved-forecast ceiling like a new one: at the ceiling it is refused with the same message, and the session stays archived and intact.
Sessions
Get Run Durations
GET/api/v1/training/run-durations
Read keyReturns 200 · application/json
Median and p95 training duration over the tenant's last `limit` runs, by catalogue-size bucket and by granularity, read from the lineage manifests (no new storage). For choosing a retrain cadence from evidence.
Time zone
Read the time zone
GET/api/v1/tenant/timezone
Read keyReturns 200 · application/json
Readable by every role: any screen showing a scheduled time needs it.
Training
List active jobs
GET/api/v1/jobs/active
Read keyReturns 200 · application/json
Training runs of this tenant that are queued or running right now.
Training
Cancel a job
DEL/api/v1/jobs/{job_id}
Write keyReturns 200 · application/json
Cancels a queued or running training job.
Training
Get a job
GET/api/v1/jobs/{job_id}
Read keyReturns 200 · application/json
A training job's state and progress.
Training
Job logs
GET/api/v1/jobs/{job_id}/logs
Read keyReturns 200 · application/json
The last `tail` log lines of a training job.
Training
List a session's jobs
GET/api/v1/sessions/{session_id}/jobs
Read keyReturns 200 · application/json
Training jobs run for the session.
Training
Start Reforecast
POST/api/v1/sessions/{session_id}/reforecast
Write keyReturns 202 · application/json
Create a NEW session that forecasts from this session's stored models over newer sales, without retraining. The source session is never modified. Counts against the plan's saved-forecast ceiling; runs on the job queue.
Whether a re-forecast with newer sales is on offer for this session: stored models present, newer data than the forecast used, and the refit age of the models. `reason` is a stable code when it is not.
Training
Start training
POST/api/v1/sessions/{session_id}/train
Write keyReturns 202 · application/json
Queues a training run for a configured session. Poll /sessions/{session_id}/train/status.
Training
Read training status
GET/api/v1/sessions/{session_id}/train/status
Read keyReturns 200 · application/json
Returns the latest job status for this session. Alias used by the Frontend — maps to GET /jobs/{job_id} for the most recent job.
Webhooks
List webhooks
GET/api/v1/webhooks
Read keyReturns 200 · application/json
Outgoing webhooks and the events they subscribe to.
Webhooks
Create a webhook
POST/api/v1/webhooks
Write keyReturns 200 · application/json
Subscribes an https URL to business events (purchase orders, stockouts, commitments, jobs). The signing secret is returned once.
Webhooks
List webhook events
GET/api/v1/webhooks/events
Read keyReturns 200 · application/json
The events a webhook can subscribe to and the data keys each carries.
Webhooks
Delete a webhook
DEL/api/v1/webhooks/{webhook_id}
Write keyReturns 200 · application/json
Removes the webhook and its delivery log.
Webhooks
List webhook deliveries
GET/api/v1/webhooks/{webhook_id}/deliveries
Read keyReturns 200 · application/json
The delivery log: status, attempts, last status code, last error and next retry.
Webhooks
Re-enable a webhook
POST/api/v1/webhooks/{webhook_id}/enable
Write keyReturns 200 · application/json
Switches a webhook back on after it was disabled for repeated failures.
Webhooks
Rotate a webhook secret
POST/api/v1/webhooks/{webhook_id}/rotate-secret
Write keyReturns 200 · application/json
Issues a new signing secret, returned once. The old one stops verifying at once.
Webhooks
Send a test event
POST/api/v1/webhooks/{webhook_id}/test
Write keyReturns 200 · application/json
Queues a signed webhook.test delivery to the webhook.
Request
curl-X POST "$STOCKAI/ai/narrative/forecast-explanation"\-H"Authorization: Bearer $STOCKAI_KEY"\-H"Content-Type: application/json"\-d'{"sku":"SKU-001","session_id":"<session_id>","profile":"distributor","language":"es"}'