Analytics
Dashboard plus Sensy and consumption forecasts — not the reports envelope
Analytics
GET /api/v1/analytics/dashboard is mounted (ReportPorts::analytics_repository). It is not a /reports/* body.
Requires tenant read × Analytics. A merchant who can read /reports/sales (report) can still get 403 The authorized record is not allowed to perform this action. here. That is RBAC, not a 501. Live Ocima owner JWT, 2026-09-10: reports 200, dashboard 403.
Window is ?range=, not ?days=. Closed set: 7/7d, 30/30d (and empty), 90/90d, 180/180d, 1y/365/365d. Anything else — including ?range=8 — is 30. No error.
Dashboard
GET /api/v1/analytics/dashboard?range=7d§ion=summary§ion=trends{
"organization_id": "0rmzqwt5hhaf07r",
"range_days": 7,
"fetched_at": "2026-09-10T14:34:07Z",
"data": {}
}errors is omitted when empty. A failed section is a string in errors.<name> and is absent from data.
Nine sections, Go order: summary, trends, apps, alerts, activity, sensy, consumption, products, posts.
?section= | Behaviour |
|---|---|
| omitted | all nine |
| unknown names | dropped silently |
selection empty after drops (?section=nonsense) | all nine, not an empty dashboard |
| selected but empty result | key omitted (omitempty) — not "trends":[] |
summary with zeros | emitted (struct, not a slice) |
summary all-zeros is a measured zero. A missing trends key is either no rows or a section you did not ask for.
Sensy and consumption
| Endpoint | Grant |
|---|---|
GET /api/v1/analytics/sensy | read × Analytics |
GET /api/v1/analytics/consumption | same |
GET /api/v1/analytics/sensy/export | export × Analytics |
GET /api/v1/analytics/sensy/template | read × Analytics — CSV header sku_id,product_name_en,sale_date,qty,unit_price,current_qty,reorder_threshold,buffer |
POST /api/v1/analytics/sensy/upload | import × Analytics |
?horizon= default 14, max 90. Non-positive / unparseable → 14. Same ?range= set as the dashboard.