Complete API Reference¶
Base URL: http://localhost:8002
All routes prefixed with /api/
Interactive docs: http://localhost:8002/docs
Figure: FastAPI router topology — all routers are mounted under the single /api prefix.
mindmap
root((FastAPI /api))
auth
login
microsoft
google
users
series
data
forecasts
distributions
analytics
accuracy
aggregate-demand
pipeline
run
jobs
workflows
supply
orders
inventory
exceptions
meio
scenarios
results
allocation
overview
Authentication¶
Figure: request lifecycle — JWT verification, tenant DB resolution from account_id, pipeline_id guard on PIPE tables, then handler logic across PostgreSQL master and ClickHouse PIPE tables.
sequenceDiagram
participant C as "Client"
participant M as "FastAPI middleware"
participant H as "Route handler"
C->>M: "GET /api/... (Authorization: Bearer)"
M->>M: "Verify JWT (sub, role, account_id)"
M->>M: "Resolve tenant DB from account_id"
M->>H: "request + tenant context"
H->>H: "pipeline_id guard<br/>(required for PIPE_* endpoints)"
H->>H: "Business logic (PG master + CH PIPE_*)"
H-->>M: "Response JSON"
M-->>C: "200 / 4xx / 5xx"
All endpoints (except /api/auth/login, /api/auth/microsoft, /api/auth/google) require a JWT bearer token.
Header format:
The token is obtained from any of the login endpoints and stored in localStorage under the key mirabelle_token.
Token format: HS256 JWT containing sub (user ID), email, role, account_id, account_name, can_run_process, can_create_override.
Error responses:
HTTP status codes:
- 401 — missing or invalid token
- 403 — authenticated but insufficient role/permission
- 404 — resource not found
- 400 — invalid request body or parameters
- 500 — internal server error
- 503 — database not available
Pagination pattern: Endpoints that return lists support skip (offset) and limit query parameters where applicable. Exception-log and process-log endpoints use page / page_size.
Auth — /api/auth/*¶
These routes are defined in files/api/auth.py and mounted at /api/auth/.
GET /api/auth/probe¶
Check which tenant accounts are available for an email address. Used for the superAdmin two-phase login flow.
Query params:
- email (string, required)
Response:
POST /api/auth/login¶
Local email/password login.
Body:
{
"email": "user@example.com",
"password": "secret",
"account_id": "uuid" // optional — superAdmin only
}
Success response:
{
"access_token": "<jwt>",
"user": {
"id": 1,
"email": "user@example.com",
"name": "Alice",
"role": "admin",
"account_id": "uuid",
"account_name": "Acme Corp"
}
}
SuperAdmin multi-tenant response (when account_id not supplied):
POST /api/auth/microsoft¶
Microsoft OAuth login. Pass the MSAL access token from the browser SDK.
Body:
Response: Same as local login.
POST /api/auth/google¶
Google OAuth login. Pass the Google ID credential from the Google Sign-In button.
Body:
Response: Same as local login.
POST /api/auth/switch-account¶
Switch the current session to a different tenant. Requires superAdmin or multi-account access.
Body:
Response: New token and user object.
GET /api/auth/me¶
Return the current user profile from the JWT.
Response:
{
"id": 1, "email": "...", "name": "...", "role": "admin",
"account_id": "uuid", "account_name": "Acme Corp",
"can_run_process": true, "can_create_override": true,
"allowed_segments": [], "allowed_segments_edit": []
}
POST /api/auth/users¶
Create a new user (admin only).
Body:
{
"email": "new@example.com",
"name": "Bob",
"password": "secret123",
"role": "user",
"can_run_process": false,
"can_create_override": false
}
GET /api/auth/users¶
List all users in the current tenant (admin only).
PUT /api/auth/users/{user_id}¶
Update a user (admin only). Same body shape as POST.
POST /api/auth/change-password¶
Change the current user's password.
Body:
POST /api/auth/logout¶
Invalidate the current session (best-effort; JWT is stateless so client-side cleanup is also needed).
Config and System — /api/config, /api/reload¶
GET /api/config¶
Return the assembled runtime configuration. Sensitive fields (password, secret, etc.) are redacted.
Response:
POST /api/config/update¶
Update one or more config keys and persist to the DB parameters table.
Body (single key):
Body (batch):
{
"updates": [
{ "path": "forecasting.horizon", "value": 26 },
{ "path": "characterization.intermittency.adi_threshold", "value": 1.5 }
]
}
POST /api/reload¶
Reload all in-memory data cache from PostgreSQL. Call after a pipeline run completes to refresh the API without restarting.
Response:
GET /api/log-level¶
Return the current API log level.
PUT /api/log-level¶
Set the log level. Body: { "level": "DEBUG" }. Accepted values: DEBUG, INFO, WARNING, ERROR.
Series and Demand — /api/series/*, /api/items, /api/sites¶
GET /api/series¶
Paginated list of time series with characterization, best-method, classification, and cost data.
Query params:
- skip (int, default 0) — offset for pagination
- limit (int, default 200, max 50000)
- scenario_id (int, optional)
- pipeline_id (int, optional)
- segment_id (int, optional) — filter to segment members
Response: Array of series objects:
[{
"unique_id": "12_301",
"item_id": 12,
"site_id": 301,
"item_name": "Wheel Assembly",
"site_name": "Paris DC",
"item_xuid": "WH-FA",
"site_xuid": "FR-PAR",
"item_image_url": null,
"item_attributes": { "category": "Wheels", "weight_g": 850 },
"site_attributes": { "country": "FR", "site_type": "central_wh" },
"n_observations": 156,
"date_range_start": "2021-01-04",
"date_range_end": "2024-01-01",
"mean": 45.2,
"is_intermittent": false,
"has_seasonality": true,
"has_trend": false,
"adi": 1.0,
"zero_ratio": 0.0,
"cov": 0.45,
"complexity_level": "medium",
"recommended_methods": ["AutoETS", "MSTL"],
"best_method": "AutoETS",
"best_score": 0.82,
"runner_up_method": "MSTL",
"classifications": { "Default (demand)": "A", "XYZ (order hits)": "X" },
"avg_unit_cost": 125.50,
"unit_price": 199.99
}]
avg_unit_cost and unit_price
These fields power the View Mode (Quantity / Cost / Price / Margin) dropdown in the Dashboard toolbar. When avg_unit_cost is 0, all monetary-mode panels display zero.
GET /api/series/type-counts¶
Counts of series by demand type for the Dashboard type-count cards.
Query params:
- pipeline_id (int, optional)
- segment_id (int, optional)
Response:
GET /api/abc/configurations¶
List all ABC classification configurations.
Response: Array of configuration objects:
[{
"id": 1,
"name": "Default (demand)",
"class_labels": ["A", "B", "C", "D"],
"is_active": true,
"metric": "revenue",
"thresholds": [0.2, 0.5, 0.8]
}]
GET /api/series/{unique_id}/data¶
Get the actual demand time series data for a specific series.
Path: unique_id — the series identifier (e.g. 12_301)
Response: Array of {ds: "2024-01-01", y: 42.0, corrected_y: 42.0} objects.
GET /api/series/{unique_id}/distributions¶
Get the fitted distribution parameters for a series.
Response:
{
"distribution_type": "lognormal",
"mean": 45.2,
"std": 12.3,
"params": { "s": 0.27, "scale": 44.1 },
"ks_statistic": 0.04,
"ks_pvalue": 0.82,
"service_level_quantiles": { "0.9": 62.1, "0.95": 71.4, "0.99": 89.3 }
}
GET /api/series/{unique_id}/outliers¶
Get detected outliers for a series.
GET /api/series/{unique_id}/best-method¶
Get the selected best method for a series.
PUT /api/series/{unique_id}/locked-method¶
Lock a series to a specific forecasting method. Requires can_create_override.
Body: { "method": "AutoETS" }
DELETE /api/series/{unique_id}/locked-method¶
Remove a method lock.
GET /api/series/{unique_id}/parameters¶
Get the effective parameter configuration for this series (resolves the three-level hierarchy).
GET /api/series/{unique_id}/method-explanation¶
Get a natural-language explanation of why a particular method was selected for this series (uses Claude AI).
GET /api/series/{unique_id}/indirect¶
Get the indirect demand breakdown (bill-of-materials demand roll-up).
GET /api/series/{unique_id}/io-network¶
Get the inventory optimisation network graph for this series (nodes = sites, edges = routes).
PATCH /api/items/{item_id}/image¶
Upload or update the item image URL (admin only).
Body: { "image_url": "https://..." }
GET /api/items¶
List all items (master data).
GET /api/sites¶
List all sites (master data).
Forecasts and Metrics — /api/forecasts/*, /api/metrics/*¶
GET /api/forecasts/{unique_id}¶
Get forecast values for all methods for a series.
Query params:
- scenario_id (int)
- pipeline_id (int)
Response:
{
"unique_id": "12_301",
"forecasts": {
"AutoETS": [{ "ds": "2024-01-08", "yhat": 48.3, "yhat_lower": 32.1, "yhat_upper": 64.5 }],
"MSTL": [...]
}
}
GET /api/metrics/{unique_id}¶
Get backtesting metrics for all methods for a series.
Response:
{
"unique_id": "12_301",
"metrics": {
"AutoETS": { "mae": 8.2, "rmse": 11.4, "mase": 0.78, "bias": 0.03, "coverage_90": 0.91 },
"MSTL": { ... }
}
}
GET /api/forecasts/{unique_id}/origins¶
List all backtest origin dates available for a series.
GET /api/forecasts/{unique_id}/origins/{origin_date}¶
Get the forecast made from a specific historical origin date (for forecast evolution analysis).
GET /api/series/{unique_id}/forecast-evolution¶
Get the full forecast evolution chart data — how forecasts have changed over time for a series.
GET /api/series/{unique_id}/forecast-convergence¶
Get forecast convergence data — how the forecast for a specific future date converged as new data arrived.
GET /api/series/{unique_id}/backtest-forecasts¶
Get all backtest forecast values across all windows and methods.
GET /api/series/{unique_id}/vega-spec¶
Get a Vega-Lite spec for rendering the series chart in the UI.
POST /api/sparklines¶
Batch sparkline data for the dashboard series table.
Body: { "unique_ids": ["12_301", "12_302", ...] }
Response: { "12_301": [[date, value], ...], ... } — minimal arrays for inline SVG rendering.
Analytics — /api/analytics/*, /api/best-methods¶
GET /api/analytics¶
Portfolio-level accuracy analytics across all series.
Query params: scenario_id, pipeline_id, segment_id
GET /api/analytics/accuracy-precision¶
Accuracy vs. precision scatter data for method comparison.
Query params:
- pipeline_id (int, optional)
Response:
{
"points": [
{ "unique_id": "12_301", "accuracy": 0.85, "precision": 0.72, "method": "AutoETS" }
],
"summary": {
"avg_accuracy": 0.78,
"avg_precision": 0.65,
"by_method": { "AutoETS": { "avg_accuracy": 0.82, "avg_precision": 0.70 } }
}
}
GET /api/analytics/aggregate-demand¶
Historical + forecast + causal + maintenance aggregated demand for the Dashboard demand chart.
Query params:
- pipeline_id (int, optional)
- series_scenario_id (int, optional)
- maintenance_scenario_id (int, optional)
- causal_scenario_id (int, optional)
Response:
{
"historical": [{ "date": "2024-01-07", "value": 1511.0 }],
"forecast": [{ "date": "2026-04-12", "value": 3207.5 }],
"forecast_causal": [{ "date": "2026-04-12", "value": 150.0 }],
"forecast_maint": [{ "date": "2026-04-12", "value": 80.0 }]
}
POST /api/analytics/aggregate-demand¶
Same as GET but accepts unique_ids in body for large filtered sets.
Body:
GET /api/best-methods¶
Summary of best method distribution across all series.
Response:
{
"method_counts": { "AutoETS": 222, "AutoTheta": 174, "AutoARIMA": 107, "MSTL": 38 },
"total_series": 590
}
POST /api/methods/check-compatibility¶
Check which forecasting methods are compatible with a series's characteristics.
Hyperparameters — /api/hyperparams/*¶
GET /api/hyperparams/{unique_id}¶
Get per-SKU hyperparameter overrides for a series.
PUT /api/hyperparams/{unique_id}¶
Set per-SKU hyperparameter overrides. Requires can_create_override.
Body: { "method": "AutoARIMA", "overrides": { "season_length": 26 } }
DELETE /api/hyperparams/{unique_id}¶
Remove all overrides for a series.
Adjustments (Demand Corrections) — /api/adjustments/*¶
GET /api/adjustments/{unique_id}¶
Get all demand corrections for a series.
POST /api/adjustments/{unique_id}¶
Create or update a demand correction for a specific date.
Body:
DELETE /api/adjustments/{unique_id}¶
Delete all corrections for a series.
POST /api/series/{unique_id}/copy-forecast¶
Copy a method's forecast values into the demand corrections for a future period (used to "accept" a forecast).
NPI — /api/npi/*¶
GET /api/npi/list¶
List all New Part Introduction items.
Query params: skip, limit, search
DELETE /api/npi/{unique_id}¶
Delete an NPI item.
Segments — /api/segments/*¶
GET /api/segments¶
List all segments.
GET /api/segments/by-uid/{unique_id}¶
Get all segments that contain a specific series.
POST /api/segments¶
Create a new segment with filter rules.
Body:
{
"name": "High Value Items",
"description": "Items with annual value > 10000",
"rules": [
{ "field": "annual_value", "operator": ">", "value": 10000 }
]
}
GET /api/segments/fields¶
List available fields for building segment rules (with data types and example values).
POST /api/segments/parse-natural-language¶
Parse a natural-language segment description into structured rules (uses Claude AI).
Body: { "description": "All items with high seasonality and low stock" }
PUT /api/segments/{segment_id}¶
Update a segment's name, description, or rules.
DELETE /api/segments/{segment_id}¶
Delete a segment.
GET /api/segments/{segment_id}/members¶
List the series that belong to a segment.
GET /api/segments/{segment_id}/details¶
Get segment statistics (member count, coverage, etc.).
POST /api/segments/{segment_id}/preview¶
Preview which series would match a set of rules without saving.
POST /api/segments/{segment_id}/assign¶
Assign all matching series to the segment (runs the filter and writes to segment_membership).
ABC Classification — /api/abc/*¶
GET /api/abc/configurations¶
List all ABC classification configurations.
POST /api/abc/configurations¶
Create a new ABC configuration.
Body:
GET /api/abc/configurations/{config_id}¶
Get one configuration.
PUT /api/abc/configurations/{config_id}¶
Update a configuration.
DELETE /api/abc/configurations/{config_id}¶
Delete a configuration.
POST /api/abc/run/{config_id}¶
Run ABC classification for a configuration.
POST /api/abc/run-all¶
Run all ABC configurations.
GET /api/abc/results/{config_id}¶
Get classification results for a configuration.
GET /api/abc/results/by-series/{unique_id}¶
Get ABC class for a specific series across all configurations.
GET /api/abc/summary/{config_id}¶
Aggregate summary (count and value per class).
GET /api/abc/price-available¶
Check whether price data is available for value-based classification.
Parameters — /api/parameters/*¶
See Parameter System for full documentation.
| Method | Path | Description |
|---|---|---|
| GET | /api/parameters |
List all parameter sets (?parameter_type= to filter) |
| GET | /api/parameters/{id} |
Get one parameter set |
| POST | /api/parameters |
Create a parameter set |
| PUT | /api/parameters/{id} |
Update a parameter set |
| DELETE | /api/parameters/{id} |
Delete a parameter set |
| PUT | /api/parameters/reorder |
Reorder within a type |
| POST | /api/parameters/resolve |
Trigger assignment resolution |
| GET | /api/parameters/{id}/segments |
Linked segments |
| PUT | /api/parameters/{id}/segments |
Update segment links |
Pipeline and Process Control — /api/pipeline/*, /api/process-log/*¶
GET /api/pipeline/steps¶
Return the available pipeline steps as [{ "id": "etl", "label": "ETL", "description": "..." }].
POST /api/pipeline/run/{step}¶
Launch a pipeline step as a background subprocess.
Path: step — one of etl, outlier, characterization, forecast, backtest, backtest-forecast, best-method, distribution, meio, supply
Body:
{
"scenario_id": 1,
"pipeline_id": 42,
"segment_id": null,
"force": false,
"best_method_only": false,
"force_full_run": false,
"run_id": null,
"n_workers": null
}
Requires: can_run_process permission or admin role.
Response:
POST /api/pipeline/run-all¶
Launch the complete pipeline (all steps in sequence).
Body:
Requires: can_run_process permission or admin role.
POST /api/pipeline/run-forecast¶
Launch a forecast for specific series, optionally with all methods or a specific set.
Body:
{
"series": ["SKU-A", "SKU-B"],
"all_methods": false,
"pipeline_id": 42,
"methods": ["autoarima", "autoets"]
}
Requires: can_run_process permission or admin role.
GET /api/pipeline/jobs¶
List all jobs for the current account.
Response: Array of { job_id, step, status, started_at, finished_at, error }.
GET /api/pipeline/jobs/{job_id}¶
Get job status and tail of log output.
POST /api/pipeline/jobs/{job_id}/kill¶
Kill a running job.
POST /api/pipeline/jobs/kill-all¶
Kill ALL running/pending pipeline jobs for the current account and clear them. Used by the frontend "Kill & Restart" button.
Requires: can_run_process permission or admin role.
POST /api/pipeline/jobs/reset¶
Reset stale/zombie jobs to failed status.
GET /api/pipeline/jobs/{job_id}/stream¶
Server-Sent Events stream of job log lines. Connect with EventSource in the browser.
GET /api/process-log¶
Paginated process log with filtering.
Query params: limit, offset, step, status, run_id, date_from, date_to, pipeline_id, scenario_id, sort_by, sort_dir
GET /api/process-log/runs¶
List distinct pipeline runs.
GET /api/process-log/step-names¶
List all unique step names present in the log.
GET /api/process-log/{run_id}/steps¶
Get all steps for a specific run.
GET /api/process-log/step/{step_id}/tail¶
Get the log tail for a specific step execution.
Process Workflows — /api/process-workflows/*¶
Workflows define ordered sequences of pipeline steps that can be run as a single unit. They are the backend for the "Workflows" tab in the Process Runner UI.
GET /api/process-workflows¶
List all process workflows for the current tenant, ordered by sort_order.
Response:
[
{
"workflow_id": 1,
"name": "E2E Full Pipeline",
"steps": ["etl", "outlier", "characterization", "forecast", "backtest", "best-method", "distribution", "meio"],
"segment_ids": [],
"scenario_pipeline_id": null,
"step_workers": {},
"sort_order": 0,
"last_run_at": "2026-04-22T14:30:00Z"
}
]
POST /api/process-workflows¶
Create a new process workflow.
Body:
{
"name": "E2E Full Pipeline",
"steps": ["etl", "outlier", "characterization", "forecast", "backtest", "best-method", "distribution", "meio"],
"segment_ids": [],
"scenario_pipeline_id": null,
"step_workers": {}
}
Requires: can_run_process permission or admin role.
PUT /api/process-workflows/{workflow_id}¶
Update an existing workflow (name, steps, segment IDs, etc.).
Body: same fields as create.
Requires: can_run_process permission or admin role.
DELETE /api/process-workflows/{workflow_id}¶
Delete a process workflow.
Requires: can_run_process permission or admin role.
PUT /api/process-workflows/reorder¶
Reorder workflows by providing an ordered list of IDs.
Body:
Requires: can_run_process permission or admin role.
Import ETL — /api/import/*¶
These endpoints manage the import.* staging schema — the landing zone for ERP and external data before the ETL pipeline transforms it into scenario.* and master.*. Import tables are stateless; truncating or dropping them is safe (re-create via files/DDL/import_schema.sql).
GET /api/import/tables¶
List all import staging tables with current row counts and metadata.
Response:
[
{ "table_name": "import_demand_actuals", "row_count": 125000, "last_loaded": "2026-04-20T08:00:00Z" }
]
DELETE /api/import/tables/{table_name}¶
Truncate one import staging table. No-op if the table does not exist.
Requires: admin role.
POST /api/import/run¶
Trigger a full or partial import ETL run. The ETL reads from import.* tables, validates and transforms the data, and writes it into scenario.*.
Body:
{
"tables": ["import_demand_actuals", "import_item"],
"modes": { "import_demand_actuals": "upsert" }
}
Both fields are optional — omitting tables runs all import tables. modes overrides the default mode per table ("upsert" or "replace").
Requires: can_run_process permission or admin role.
Response:
GET /api/import/runs¶
List recent import runs (newest first) with pagination.
Query params: limit (1–100, default 20), offset (default 0).
GET /api/import/runs/{run_id}¶
Get one import run including its per-table results (rows inserted, updated, errors).
GET /api/import/runs/{run_id}/log¶
Get the pipe_process_log tail for an import run.
Query params: limit (1–200, default 50).
Scenarios (Forecast) — /api/forecast/scenarios/*¶
| Method | Path | Description |
|---|---|---|
| GET | /api/forecast/scenarios |
List |
| POST | /api/forecast/scenarios |
Create |
| GET | /api/forecast/scenarios/{id} |
Get |
| PUT | /api/forecast/scenarios/{id} |
Update |
| DELETE | /api/forecast/scenarios/{id} |
Delete |
| POST | /api/forecast/scenarios/{id}/clone |
Clone |
| GET | /api/forecast/scenarios/{id}/results/summary |
Accuracy summary |
Forecast Netting — /api/forecast/netting/*¶
| Method | Path | Description |
|---|---|---|
| GET | /api/forecast/netting/summary |
Aggregate netting summary (series_count, total_forecast, total_consumed, total_netted, total_firm_demand, total_supply_plan, consumption_rate, netting_direction) |
| GET | /api/forecast/netting/series |
Series-level netting |
| GET | /api/forecast/netting/{unique_id} |
Netting for one series |
| POST | /api/forecast/netting/refresh |
Recompute netting |
Query params for summary: pipeline_id (int), segment_id (int, optional)
Scenario Pipelines — /api/scenario-pipeline/*¶
See Pipeline Configuration for full documentation.
| Method | Path | Description |
|---|---|---|
| GET | /api/scenario-pipeline |
List all pipelines |
| POST | /api/scenario-pipeline |
Create |
| GET | /api/scenario-pipeline/{id} |
Get |
| PUT | /api/scenario-pipeline/{id} |
Update |
| DELETE | /api/scenario-pipeline/{id} |
Delete |
| POST | /api/scenario-pipeline/{id}/copy-from/{src_id} |
Copy from source |
| POST | /api/scenario-pipeline/{id}/segments |
Add segment |
| DELETE | /api/scenario-pipeline/{id}/segments/{seg_id} |
Remove segment |
| GET | /api/scenario-pipeline/{id}/baseline/{uid} |
Get baseline for series |
Supply Planning — /api/supply/*¶
Pipeline and scenario required
Every supply endpoint requires both pipeline_id and scenario_id as query parameters. Use useSupplyParams() on the frontend. Exceptions: item-site and sales-returns CRUD — these are immutable master data.
Figure: pipeline_id guard decision flow — None and 0 are rejected; only master-data endpoints skip the PIPE_ filter.*
flowchart TD
A["Endpoint receives request"] --> B{"pipeline_id provided?"}
B -->|"None"| C["raise HTTPException 400<br/>pipeline_id is required"]
B -->|"0"| D["reject, 0 is orphan sentinel"]
B -->|"> 0"| E{"reads / writes PIPE_*?"}
E -->|"yes"| F["filter WHERE pipeline_id = X"]
E -->|"no, master data"| G["no pipeline filter needed"]
F --> H["Return result"]
G --> H
POST /api/supply/run¶
Launch a supply planning run for a scenario.
Body: { "scenario_id": 1, "pipeline_id": 42 }
GET /api/supply/runs¶
List supply planning runs.
Query params: pipeline_id, scenario_id
GET /api/supply/runs/{run_id}¶
Get details of a specific run.
DELETE /api/supply/runs/{run_id}¶
Delete a run.
GET /api/supply/orders/breakdown¶
Aggregated order breakdown by item, site, and period.
Query params: pipeline_id, scenario_id, run_id
GET /api/supply/orders¶
List replenishment orders.
Query params: pipeline_id, scenario_id, run_id, item_id, site_id, status
PUT /api/supply/orders/{order_id}¶
Update an order (e.g., change quantity, dates).
GET /api/supply/orders/{order_id}/pegging¶
Get demand pegging for a specific order (which demand lines drove this order).
GET /api/supply/pegging¶
Bulk pegging data.
Query params: pipeline_id, scenario_id, run_id
GET /api/supply/pegging/{order_id}¶
Pegging for a specific order.
GET /api/supply/inventory¶
Current inventory positions.
Query params: pipeline_id, scenario_id, run_id
GET /api/supply/exceptions¶
Supply exceptions (stockouts, excess, etc.).
Query params: pipeline_id, scenario_id, run_id
GET /api/supply/dashboard¶
Aggregated supply dashboard KPIs.
Query params: pipeline_id, scenario_id, run_id
GET /api/supply/capacity¶
Capacity utilisation by site and period.
GET /api/supply/scenarios¶
List supply scenarios.
POST /api/supply/scenarios¶
Create a supply scenario.
GET /api/supply/scenarios/{id}¶
Get one scenario.
PUT /api/supply/scenarios/{id}¶
Update a scenario.
PUT /api/supply/scenarios/{id}/param-overrides¶
Update parameter overrides for a scenario.
DELETE /api/supply/scenarios/{id}¶
Delete a scenario.
GET /api/supply/scenarios/{id}/segments¶
Get segment scopes for a scenario.
PUT /api/supply/scenarios/{id}/segments¶
Update segment scopes.
GET /api/supply/item-site¶
List item-site records (master data — no pipeline required).
PUT /api/supply/item-site/{item_id}/{site_id}¶
Update item-site parameters (cost, lead time, etc.). Master data.
GET /api/supply/sales-returns¶
List sales return records. Master data.
POST /api/supply/sales-returns¶
Create a sales return record.
PUT /api/supply/sales-returns/{return_id}¶
Update a return record.
DELETE /api/supply/sales-returns/{return_id}¶
Delete a return record.
POST /api/supply/sync-alerts¶
Sync supply alerts from the alert engine.
POST /api/supply/firm-orders/import¶
Bulk upload firm supply orders (JSON list). Each row obeys the same validation as POST /api/supply/firm-orders. Uses ON CONFLICT (external_id, source_system) upsert.
Body:
[
{ "external_id": "PO-001", "source_system": "ERP", "item_id": 1, "site_id": 1, "order_type": "BUY", "quantity": 100, "due_date": "2026-06-01" }
]
Response:
GET /api/supply/consumption-preview¶
Preview consumption forecast for the supply planning horizon.
Routes — /api/routes, /api/route-types¶
GET /api/route-types¶
List route types (REPLENISHMENT, RETURN, REPAIR, etc.).
GET /api/routes¶
List all supply network routes.
POST /api/routes¶
Create a route.
Body: { "item_id": 12, "from_site_id": 100, "to_site_id": 305, "route_type": "REPLENISHMENT", "lead_time": 7, "priority": 1 }
PUT /api/routes/{route_id}¶
Update a route.
DELETE /api/routes/{route_id}¶
Delete a route.
MEIO — /api/meio/*¶
See MEIO Configuration for full documentation.
| Method | Path | Description |
|---|---|---|
| GET | /api/meio/scenarios |
List scenarios |
| POST | /api/meio/scenarios |
Create |
| PUT | /api/meio/scenarios/{id} |
Update |
| DELETE | /api/meio/scenarios/{id} |
Delete |
| GET | /api/meio/scenarios/{id}/segment-params |
Segment overrides |
| PUT | /api/meio/scenarios/{id}/segment-params |
Upsert overrides |
| DELETE | /api/meio/scenarios/{id}/segment-params/{seg} |
Remove override |
| DELETE | /api/meio/scenarios/{id}/segment-params |
Clear all |
| GET | /api/meio/scenarios/{id}/parameter-sets |
Parameter sets |
| POST | /api/meio/scenarios/{id}/parameter-sets |
Add parameter set |
| PUT | /api/meio/parameter-sets/{pset_id} |
Update |
| DELETE | /api/meio/parameter-sets/{pset_id} |
Delete |
| PUT | /api/meio/parameter-sets/{pset_id}/segments |
Link segments |
| PUT | /api/meio/scenarios/{id}/parameter-sets/reorder |
Reorder |
| POST | /api/meio/scenarios/{id}/run |
Run optimiser |
| GET | /api/meio/scenarios/{id}/results |
SKU + group results |
| GET | /api/series/{unique_id}/mc-distribution |
MEIO V3 MC distribution (demand × supply-LT → compound → SS) |
| GET | /api/meio/fr-heatmap |
Fill-rate heatmap |
| GET | /api/meio/diagnostic |
Diagnostic data |
K-Curve (EOQ) — /api/kcurve/*¶
The K-Curve module implements EOQ (Economic Order Quantity) and Wagner-Whitin dynamic lot sizing.
| Method | Path | Description |
|---|---|---|
| GET | /api/kcurve/items |
List items eligible for EOQ |
| GET | /api/kcurve/curve |
The cost curve (holding vs. ordering cost tradeoff). Query params: constrained: bool (default false) — when true, also returns constrained_curve |
| POST | /api/kcurve/solve |
Solve EOQ for a set of items. Body: constrained: bool (default false) — when true, returns constrained_curve, alternatives, per-item freq_options. Optional scenario_id + persist (persist defaults on when scenario_id is present): when set, results are atomically jsonb-merged into scen_inventory_scenario.results for the active pipeline — the surface downstream MEIO/supply read for EOQ — making an explicit curve click authoritative without a separate EOQ process-step run. master.item.eoq write-back is intentionally not done here (stays exclusive to the EOQ process step, kcurve.runner.run_eoq_for_pipeline) so a dev/what-if click cannot clobber production master data. scenario_id is validated against config_scenario_pipeline (ownership/IDOR guard). |
| GET | /api/kcurve/eoq |
EOQ results for all items |
| GET | /api/kcurve/eoq/item |
EOQ for one item. Query params: respect_frequency_constraints: bool (default true) — when true, snaps EOQ to allowed frequency and returns freq_options |
| GET | /api/kcurve/wagner-whitin |
Wagner-Whitin dynamic lot-sizing. Returns per-item eoq (annualised average order qty), eoq_override, orders_per_year, n_orders, avg_lot_size, schedule[], plus portfolio totals. Applies the same interact_io_overrides (scenario_id=0) as Classic EOQ/K-Curve. When a pipeline's linked inventory scenario is tag_code='wagner_whitin', the eoq pipeline step runs run_wagner_whitin_for_pipeline, persists results.items[] (consumed by MEIO/supply), and writes back item.eoq. |
| GET | /api/kcurve/pipeline/{pipeline_id} |
K-curve results for a pipeline |
| GET | /api/kcurve/scenarios |
List K-curve scenarios |
| POST | /api/kcurve/scenarios |
Create a scenario |
| DELETE | /api/kcurve/scenarios/{id} |
Delete a scenario |
Order-frequency constraint response fields¶
When constrained=true or respect_frequency_constraints=true:
| Field | Location | Description |
|---|---|---|
constrained_curve |
kcurve/curve, kcurve/solve |
Stepped feasible frontier [{k, inv_val, orders}] |
alternatives |
kcurve/solve |
Neighbouring feasible k points [{k, inv_val, orders_per_year}] |
freq_options |
per-item in kcurve/solve, kcurve/eoq/item |
Allowed frequencies [{freq, freq_label, orders_per_year, eoq, cycle_stock_value, total_annual_cost, k_equivalent, is_selected}] |
freq_constraint |
per-item | {param_name, allowed_orders_per_year, selected_orders_per_year, constrained} |
freq_constrained |
kcurve/eoq/item top-level |
true when EOQ was overridden by frequency snapping |
freq_param_name |
kcurve/eoq/item top-level |
Name of the order_frequency parameter that applied |
Causal Forecasting — /api/causal/*¶
Causal forecasting models demand driven by asset deployments and failure rates (MRO / aerospace use case).
| Method | Path | Description |
|---|---|---|
| GET | /api/causal/asset-types |
List asset types |
| POST | /api/causal/asset-types |
Create asset type |
| GET | /api/causal/assets |
List assets |
| POST | /api/causal/assets |
Create asset |
| DELETE | /api/causal/assets/{id} |
Delete |
| PUT | /api/causal/assets/{id} |
Update |
| GET | /api/causal/asset-deployments |
List deployments |
| POST | /api/causal/asset-deployments |
Create deployment |
| DELETE | /api/causal/asset-deployments/{id} |
Delete |
| PUT | /api/causal/asset-deployments/{id} |
Update |
| GET | /api/causal/coverage |
Asset coverage |
| POST | /api/causal/coverage |
Create coverage |
| DELETE | /api/causal/coverage/{id} |
Delete |
| GET | /api/causal/bom |
Bill of materials |
| POST | /api/causal/bom |
Create BOM row |
| DELETE | /api/causal/bom/{id} |
Delete |
| PUT | /api/causal/bom/{id} |
Update |
| GET | /api/causal/bom/explosion |
BOM explosion |
| POST | /api/causal/bom/import |
Import BOM from CSV |
| GET | /api/causal/fleet-plan |
Fleet plan |
| POST | /api/causal/fleet-plan |
Create fleet plan |
| PUT | /api/causal/fleet-plan/{id} |
Update |
| DELETE | /api/causal/fleet-plan/{id} |
Delete |
| POST | /api/causal/fleet-plan/import |
Import fleet plan |
| GET | /api/causal/customers |
List customers |
| GET | /api/causal/usage-types |
List usage types |
| GET | /api/causal/asset-usage |
List asset usage |
| POST | /api/causal/asset-usage |
Create usage |
| PUT | /api/causal/asset-usage/{id} |
Update |
| DELETE | /api/causal/asset-usage/{id} |
Delete |
| GET | /api/causal/usage-timeseries |
Usage as time series |
| GET | /api/causal/failure-rate |
List failure rates |
| POST | /api/causal/failure-rate |
Create |
| DELETE | /api/causal/failure-rate/{id} |
Delete |
| PUT | /api/causal/failure-rate/{id} |
Update |
| POST | /api/causal/failure-rate/fit |
Fit failure rates from actuals |
| GET | /api/causal/scenarios |
Causal scenarios |
| POST | /api/causal/scenarios |
Create |
| PUT | /api/causal/scenarios/{id} |
Update |
| DELETE | /api/causal/scenarios/{id} |
Delete |
| POST | /api/causal/scenarios/run |
Run causal forecast |
| GET | /api/causal/results |
Results |
| GET | /api/causal/results/summary |
Summary |
| GET | /api/causal/results/breakdown |
Breakdown by asset/item |
| GET | /api/causal/results/compare |
Compare scenarios |
| GET | /api/causal/results/compare/deployments |
Deployment comparison |
| GET | /api/causal/results/compare/usage |
Usage comparison |
| POST | /api/causal/uncertainty |
Uncertainty quantification |
| GET | /api/causal/templates/{name} |
Download import template |
Maintenance — /api/maintenance/*¶
Scheduled maintenance event management and maintenance-driven demand.
| Method | Path | Description |
|---|---|---|
| GET | /api/maintenance/events-weekly |
Weekly maintenance event view |
| GET | /api/maintenance/items-for-week |
Items needed for a specific week |
| GET | /api/maintenance/scenarios |
Maintenance scenarios |
| GET | /api/maintenance/demand/summary |
Maintenance demand summary |
| GET | /api/maintenance/demand/series |
Demand series from maintenance |
| GET | /api/maintenance/events-summary |
Event aggregation |
| GET | /api/maintenance/events |
List all events |
| GET | /api/maintenance/scenario/{id}/item-breakdown |
Item breakdown per scenario |
| GET | /api/maintenance/scenario/{id}/schedule |
Schedule |
| PUT | /api/maintenance/scenario/{id}/schedule |
Update schedule |
| GET | /api/maintenance/events/{event_id}/occurrences |
Event occurrences |
| PUT | /api/maintenance/events/{event_id}/bom |
Update event BOM |
| PUT | /api/maintenance/events/{event_id}/scenario/{id}/dates |
Update event dates |
Alerts — /api/alerts/*¶
The alert engine evaluates 32+ business rules against each series and flags anomalies.
POST /api/alerts/query¶
Query alerts using natural language or structured filters.
Body:
{
"query": "items with low service level in the Paris region",
"segment_id": null,
"pipeline_id": 42
}
GET /api/alerts¶
List alerts with optional filters.
Query params: pipeline_id, segment_id, status, metric, page, page_size
POST /api/alerts/batch¶
Batch process (approve/reject) multiple alerts.
POST /api/alerts/{alert_id}/approve¶
Approve an alert (marks it handled with no action needed).
POST /api/alerts/{alert_id}/reject¶
Reject an alert (requires corrective action).
GET /api/alerts/ontology¶
List the alert rule ontology (all 32+ rules with thresholds).
PUT /api/alerts/ontology/{metric_name}¶
Update an alert rule threshold.
DELETE /api/alerts/ontology/{metric_name}¶
Remove an alert rule.
POST /api/alerts/ontology/seed¶
Seed the ontology with the default 32 rules (idempotent).
POST /api/alerts/synonyms¶
Add a synonym mapping for natural-language alert queries.
Body: { "term": "excess inventory", "canonical_metric": "high_stock" }
Exceptions — /api/exceptions/*, /api/exception-log/*¶
GET /api/exceptions¶
Get computed planner exceptions for a series (live compute from business rules).
Query params: pipeline_id, scenario_id, unique_id
GET /api/exceptions/{unique_id}¶
Get exceptions for a specific series.
POST /api/exceptions/{unique_id}/{exc_type}/snooze¶
Snooze an exception type for a series until a specified date.
Body: { "until_date": "2026-06-01" }
DELETE /api/exceptions/{unique_id}/{exc_type}/snooze¶
Remove a snooze.
GET /api/exception-log¶
Paginated exception log (persisted exceptions).
Query params: pipeline_id (required), status (open, resolved, snoozed), types (comma-separated: forecast,io,supply), segment_id, page, page_size
Response:
{
"total": 1234,
"page": 1,
"page_size": 50,
"items": [{
"id": 1,
"pipeline_id": 42,
"item_id": 12,
"site_id": 301,
"exception_types": ["forecast"],
"issues": [{ "type": "forecast", "severity": "high", "description": "..." }],
"status": "open",
"item_name": "Wheel Assembly",
"item_code": "WA-001",
"site_name": "Paris DC",
"unique_id": "12_301"
}]
}
GET /api/exception-log/preload¶
Bulk prefetch inline data for exception overlays (actuals, forecast, MEIO, supply negatives for multiple items in one request).
Query params: pipeline_id (required), item_ids (comma-separated integers)
PATCH /api/exception-log/{exception_id}/status¶
Update exception status.
Body: { "status": "resolved" }
POST /api/exception-log/generate¶
Generate (or refresh) exceptions for a pipeline.
Body: { "pipeline_id": 42 }
GET /api/exception-log/{exception_id}/forecast-data¶
Get forecast data inlined for an exception (used in the detail overlay).
GET /api/exception-log/{exception_id}/io-data¶
Get inventory optimisation data for an exception.
GET /api/exception-log/{exception_id}/supply-data¶
Get supply plan data for an exception.
IO Overrides — /api/io-overrides¶
POST /api/io-overrides¶
Upsert inventory optimisation parameter overrides for a specific item/site/pipeline/scenario combination.
Body:
{
"pipeline_id": 42,
"scenario_id": 1,
"item_id": 12,
"site_id": 301,
"sl_override": 0.99,
"eoq_override": 50.0,
"fr_override": 0.97
}
Orders — /api/orders/*¶
PATCH /api/orders/{order_id}/approve¶
Approve a supply order with a specific quantity and availability date.
Body:
Calendars — /api/calendars/*¶
Working day and holiday calendars used by supply planning.
| Method | Path | Description |
|---|---|---|
| GET | /api/calendars |
List calendars |
| GET | /api/calendars/{id} |
Get calendar |
| POST | /api/calendars |
Create |
| PUT | /api/calendars/{id} |
Update |
| PUT | /api/calendars/{id}/entries |
Update calendar entries (holidays, etc.) |
| DELETE | /api/calendars/{id} |
Delete |
Audit Log — /api/audit-log¶
GET /api/audit-log¶
Paginated, filterable audit trail of all significant state changes (configuration,
security, forecasting, pipeline, model events). All writes go through the
centralized helper in files/utils/audit.py.
Query params:
| param | type | description |
|---|---|---|
entity_type |
string | Filter by entity (e.g. user, pipeline, auth, override) |
entity_id |
int | Filter by entity PK |
action |
string | Filter by verb (e.g. create, delete, failed_login, promote_prod) |
changed_by |
string | Partial match on actor email/name |
outcome |
string | success / failure / denied (auth + run lifecycle) |
date_from |
ISO date | created_at >= lower bound |
date_to |
ISO date | created_at < upper bound (exclusive of next day) |
sort_by |
string | id / entity_type / entity_id / action / changed_by / created_at |
sort_dir |
string | asc / desc |
limit |
int | Page size (default 50) |
offset |
int | Pagination offset |
Response: { items: [...], total, limit, offset } where each item carries
id, entity_type, entity_id, action, old_value, new_value, changed_by, ip,
user_agent, request_id, outcome, created_at.
See the sys_audit_log event catalog in
database.md for the full (entity_type, action)
matrix.
Indirect Demand — /api/indirect/*¶
GET /api/series/{unique_id}/indirect¶
Get indirect demand (BOM-driven demand roll-up) for a series.
POST /api/indirect/compute¶
Recompute indirect demand for the full BOM network.
Overview — /api/overview/*¶
GET /api/overview/map¶
Supply network map data (nodes = sites, edges = routes, with KPI values attached).
GET /api/overview/kpis¶
Portfolio-level KPI summary (total inventory value, weighted fill rate, coverage, etc.).
Features Catalogue — /api/features/catalogue¶
GET /api/features/catalogue¶
Return the feature flag catalogue — which optional features are enabled for this tenant.
Admin — /api/admin/*¶
SuperAdmin only. Defined in files/api/admin.py.
| Method | Path | Description |
|---|---|---|
| GET | /api/admin/accounts |
List all tenant accounts |
| POST | /api/admin/accounts |
Provision a new tenant |
| GET | /api/admin/accounts/{job_id}/provision-status |
Check provisioning job status |
| DELETE | /api/admin/accounts/{account_id} |
Delete a tenant |
| POST | /api/admin/accounts/{account_id}/clone |
Clone a tenant's schema |
| GET | /api/admin/superadmins |
List superadmins |
| POST | /api/admin/superadmins |
Create superadmin |
| GET | /api/admin/users |
List users across all tenants |
| GET | /api/admin/users/assignments |
User-account assignments |
| PUT | /api/admin/users/{email}/accounts |
Update a user's account assignments |
| POST | /api/admin/cache/refresh |
Refresh the account cache |
| POST | /api/admin/run-migrations |
Run pending schema migrations |
| POST | /api/admin/populate-received-dates |
Backfill received dates on orders |
Allocation Overview — /api/allocation/overview/*¶
All allocation overview endpoints include avg_unit_cost in their response (a global average from master.item + master.item_location). This is used by the frontend View Mode (Quantity / Cost / Price / Margin) to convert qty-based values to monetary values.
| Method | Path | Description |
|---|---|---|
| GET | /api/allocation/overview/kpis |
Aggregate KPIs (fill rate, backlog, violations) |
| GET | /api/allocation/overview/graph |
Supply–demand DAG (top-N nodes + edges) |
| GET | /api/allocation/overview/backlog-heatmap |
Top-40 item/site backlog cells |
| GET | /api/allocation/overview/waterfall |
Starting stock → allocations → shortages → remaining |
| GET | /api/allocation/overview/fair-share |
Actual vs. target share per fairness group |
| GET | /api/allocation/overview/routing-violations |
List of soft routing violations |
| GET | /api/allocation/overview/aging |
Unfulfilled demand counts by age bucket |
| GET | /api/allocation/overview/reservations |
Top-10 supplies by reservation penalty |
Common query params: pipeline_id (int, default 1), scenario_id (int, default 1), segment_id (int, optional).
avg_unit_cost computation:
SELECT AVG(COALESCE(isit.unit_cost, isit.cost_price, i.unit_cost, i.cost, 0))
FROM master.item i
LEFT JOIN master.item_location isit ON isit.item_id = i.id
WHERE COALESCE(isit.unit_cost, isit.cost_price, i.unit_cost, i.cost, 0) > 0
Zero avg_unit_cost
When avg_unit_cost is 0 (no cost data in master.item / master.item_location), all allocation panels in Cost / Price / Margin mode will render as zero. This is the most common cause of "empty allocation charts in non-quantity mode."