Appearance
Reports
The Reports API provides access to RORCex's library of pre-built analytical reports. Reports can execute synchronously (returning results inline) or asynchronously (returning a run ID for polling). See the async polling guide for the recommended polling pattern.
Reports run against RORCex's synced aggregate store — not a live lane read — so results reflect data as of the last sync from the RORC back office (typically within ~5 minutes), and they remain available even when a lane is offline. See Data Sources & Freshness.
List Reports
Return the full catalogue of available reports, including each report's parameter schema and execution metadata.
GET /v1/reportsAuthentication: OAuth Token (service_account or user) Permission: rorcex:reports.execute
Response
json
{
"reports": [
{
"id": "operational.yesterday_close_summary",
"pack": "operational",
"name": "Yesterday Close Summary",
"description": "End-of-day close totals and exceptions for all stores, for yesterday's business day.",
"parameter_schema": {
"type": "object",
"properties": {
"stores": { "type": "array", "items": { "type": "string" } }
},
"required": []
},
"execution_mode": "sync",
"cache_ttl_seconds": 300,
"freshness_note": "Data reflects the completed close; re-runs within the TTL window return the cached result."
}
]
}Status: 200 OK
Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Report slug — use this as the report_id path parameter in run endpoints |
pack | string | Logical grouping: operational, sales, product, or ops |
name | string | Human-readable report name |
description | string | One-sentence description of what the report produces |
parameter_schema | object | JSON Schema describing accepted parameters for POST /v1/reports/{report_id}/run |
execution_mode | string | sync (result inline) or async (returns a run ID) |
cache_ttl_seconds | integer | How long a completed result is cached before the next run re-executes |
freshness_note | string | Human-readable note about data currency and cache behaviour |
Registered Reports
| Pack | Report ID | Name |
|---|---|---|
operational | operational.yesterday_close_summary | Yesterday Close Summary |
operational | operational.sales_pace_today | Sales Pace Today |
operational | operational.drift_watchlist | Drift Watchlist |
sales | sales.department_performance_by_store | Department Performance by Store |
sales | sales.gross_margin_by_store | Gross Margin by Store |
sales | sales.markdown_summary | Markdown Summary |
sales | sales.cashier_scorecard_cross_store | Cashier Scorecard Cross-Store |
sales | sales.hourly_pace_heatmap | Hourly Pace Heatmap |
sales | sales.department_traffic_scatter | Department Analysis: Weekly Sales vs. Foot Traffic |
product | product.retail_variance_cross_store | Retail Variance Cross-Store |
product | product.price_change_history | Price Change History |
product | product.slow_movers_cross_store | Slow Movers Cross-Store |
product | product.catalog_gap_detection | Catalog Gap Detection |
ops | ops.refund_void_audit | Refund & Void Audit |
ops | ops.inventory_adjustment_audit | Inventory Adjustment Audit |
ops | ops.outlier_store_flagging | Outlier Store Flagging |
The authoritative list of accepted parameters for each report is its parameter_schema field. Consult the list endpoint for the current schema before constructing a run request.
Errors
See /reference/errors.
Run Report
Execute a report. Supply parameters that satisfy the report's parameter_schema. Synchronous reports return results directly; asynchronous reports return a run ID for polling.
POST /v1/reports/{report_id}/runAuthentication: OAuth Token (service_account or user) Permission: rorcex:reports.execute
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
report_id | string | Yes | Report slug (e.g., sales.department_performance_by_store) |
Request Body
The request body is a JSON object whose fields are validated against the target report's parameter_schema. Retrieve the schema from GET /v1/reports before submitting.
json
{
"stores": ["org_00000k1L2m3N4o5", "org_00000a1B2c3D4e5"],
"from": "2026-06-01",
"to": "2026-06-11"
}Response — Synchronous (execution_mode: "sync")
json
{
"result": {
"data": [
{ "store_org_id": "org_00000k1L2m3N4o5", "DeptCode": "01", "NetSales": 41820.75 }
],
"source": "cache",
"generated_at": "2026-06-11T08:00:00Z",
"params_echo": {
"stores": ["org_00000k1L2m3N4o5", "org_00000a1B2c3D4e5"],
"from": "2026-06-01",
"to": "2026-06-11"
},
"freshness_label": "Cached result from 08:00 UTC",
"warnings": [],
"cache_hit": true
}
}Status: 200 OK
Response Fields — Synchronous
| Field | Type | Description |
|---|---|---|
result.data | array | Report rows, in each report's own schema. HQ-aggregate-backed reports return snake_case rows. |
result.source | string | live (freshly executed) or cache (served from cache) |
result.generated_at | string (ISO 8601) | When this result was generated |
result.params_echo | object | The parameters that produced this result |
result.freshness_label | string | Human-readable freshness description |
result.warnings | array | Non-fatal warnings (e.g., one store returned no data) |
result.cache_hit | boolean | true if served from the result cache, false if freshly executed. |
Response — Asynchronous (execution_mode: "async")
json
{
"run_id": "01J9XS5M8Q9WP3ZR7AC4VHE1XD",
"status": "pending",
"poll_url": "/api/v1/reports/runs/01J9XS5M8Q9WP3ZR7AC4VHE1XD",
"estimated_seconds": 12
}Status: 202 Accepted
Response Fields — Asynchronous
| Field | Type | Description |
|---|---|---|
run_id | string | Public run ID — use with GET /v1/reports/runs/{public_id} |
status | string | Initial status: always "pending" |
poll_url | string | Convenience URL for polling the run status |
estimated_seconds | integer | Estimated time to completion in seconds |
See the async polling guide for the recommended polling pattern.
Errors
| Status | Error | Description |
|---|---|---|
| 404 | report_not_found | No report matches the given report_id |
| 422 | report_param_validation_failed | Body does not satisfy the report's parameter_schema |
| 422 | report_failed | Report executed but produced no result |
See /reference/errors for error envelope shapes.
Get Run Status
Poll the status of an asynchronous report run.
GET /v1/reports/runs/{public_id}Authentication: OAuth Token (service_account or user) Permission: rorcex:reports.execute
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
public_id | string | Yes | The run ID returned by POST /v1/reports/{report_id}/run |
Response
json
{
"run_id": "01J9XS5M8Q9WP3ZR7AC4VHE1XD",
"report_id": "sales.department_performance_by_store",
"status": "completed",
"params_echo": {
"stores": ["org_00000k1L2m3N4o5"],
"from": "2026-06-01",
"to": "2026-06-11"
},
"created_at": "2026-06-11T09:00:00Z",
"started_at": "2026-06-11T09:00:01Z",
"completed_at": "2026-06-11T09:00:13Z",
"result": { "data": [], "source": "live", "generated_at": "2026-06-11T09:00:13Z", "params_echo": {}, "freshness_label": "Live result", "warnings": [] },
"error_class": null,
"error_message": null
}Status: 200 OK
Response Fields
| Field | Type | Description |
|---|---|---|
run_id | string | Public run ID |
report_id | string | Report slug that was executed |
status | string | One of: pending, running, completed, errored |
params_echo | object | Parameters submitted with the run |
created_at | string (ISO 8601) | When the run was enqueued |
started_at | string or null (ISO 8601) | When execution began; null if still pending |
completed_at | string or null (ISO 8601) | When execution finished; null if not yet complete |
result | object or null | Populated when status is completed — same shape as the synchronous result |
error_class | string or null | Error class name when status is errored |
error_message | string or null | Human-readable error description when status is errored |
A run is visible only to callers acting in the organization that requested it (X-Organization-Context). Within that organization, the user or service account that queued it can always read it; anyone else must also send the same store scope the run was computed under (the same set of stores, resolved from X-Organization-Context / X-Organization-Scope). Otherwise this endpoint, Get Run Result and Export Run Result return 403 — run the report yourself instead.
Errors
See /reference/errors.
Get Run Result
Retrieve the completed result for a run directly, without the run metadata envelope.
GET /v1/reports/runs/{public_id}/resultAuthentication: OAuth Token (service_account or user) Permission: rorcex:reports.execute
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
public_id | string | Yes | The run ID |
Response
json
{
"result": {
"data": [
{ "store_org_id": "org_00000k1L2m3N4o5", "DeptCode": "01", "NetSales": 41820.75 }
],
"source": "live",
"generated_at": "2026-06-11T09:00:13Z",
"params_echo": {},
"freshness_label": "Live result",
"warnings": []
}
}Status: 200 OK
Returns 404 when the run does not exist or is not yet complete, and 403 when it belongs to a different organization or a different store scope (see Get Run Status).
Errors
See /reference/errors.
Export Report
Run a report and download its rows as a CSV file instead of JSON. Parameters, store scoping and result caching are exactly those of Run Report — an export can never return rows the same caller could not get from run.
POST /v1/reports/{report_id}/export?format=csvAuthentication: OAuth Token (service_account or user) Permission: rorcex:reports.execute + rorcex:reports.view + rorcex:reports.export
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
report_id | string | Yes | Report slug (e.g., operational.yesterday_close_summary) |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
format | string | No | csv (the default and only supported format) |
bom | boolean | No | 1 (default) starts the file with a UTF-8 byte-order mark so Excel decodes accented text. Pass 0 when a script reads the file as plain UTF-8, or the mark becomes part of the first column name |
Request Body
The report parameters, exactly as for POST /v1/reports/{report_id}/run. format travels in the query string so it never collides with a report parameter.
json
{
"date": "2026-06-10"
}Response — CSV
HTTP/1.1 200 OK
Content-Type: text/csv; charset=UTF-8
Content-Disposition: attachment; filename="operational-yesterday-close-summary-20260611-080000.csv"
Store Org ID,Store Name,Timezone,Business Day Date,Status,Beginning Balance,Total Sales Amount,...
org_00000k1L2m3N4o5,Main Street,America/New_York,2026-06-10,C,100,41820.75,...The file is UTF-8 with CRLF line endings, and starts with a byte-order mark unless you pass bom=0. The filename is the report id plus the result's UTC generated_at.
How rows become columns:
| Value in a result row | In the CSV |
|---|---|
| Scalar | One column. Booleans export as true / false; null as an empty cell |
| Nested object | One column per field, headed Parent - Field |
List of scalars (e.g. missing_from_stores) | One column, values joined with ; |
List of objects (e.g. retail variance's stores) | One CSV line per element, the row's other columns repeated on each. If a row holds more than one such list, they are JSON-encoded instead |
Headers are the row keys in human-readable form (store_org_id → Store Org ID, variance_pct → Variance %). Every store_org_id column is followed by a Store Name column holding the store's name as the Reports page shows it (empty when the store has none, or is not one you can see). Columns are the union of every row's keys, in first-seen order. Text cells that a spreadsheet would read as a formula (starting =, +, -, @) are prefixed with '; numbers are not.
Result metadata (warnings, fidelity, freshness_label) is not part of the file — read it from run when you need it. One exception: when the report assessed store coverage, the response carries an X-Report-Coverage header (full, partial or none). none means no store in scope could report, so an empty file means "could not check", not "nothing found".
Response — Report Queued (202 Accepted)
A report that runs asynchronously for the given parameters cannot be exported in the same request. The response is the async run envelope from Run Report plus an export_url; download that once the run is completed.
json
{
"run_id": "01J9XS5M8Q9WP3ZR7AC4VHE1XD",
"status": "pending",
"poll_url": "/api/v1/reports/runs/01J9XS5M8Q9WP3ZR7AC4VHE1XD",
"estimated_seconds": 30,
"export_url": "/api/v1/reports/runs/01J9XS5M8Q9WP3ZR7AC4VHE1XD/export?format=csv"
}export_url carries bom=0 when you passed it.
Errors
| Status | Error | Description |
|---|---|---|
| 403 | — | Caller lacks one of the three permissions |
| 404 | report_not_found | No report matches the given report_id |
| 422 | (validation) | format or bom has an unsupported value |
| 422 | report_param_validation_failed | Body does not satisfy the report's parameter_schema |
| 422 | report_failed | Report could not run (e.g. none of the requested store_org_ids are in scope) |
Export Run Result
Download a completed asynchronous run's result as a CSV file. Rows, the Store Name column and the X-Report-Coverage header are exactly as for Export Report.
GET /v1/reports/runs/{public_id}/export?format=csvAuthentication: OAuth Token (service_account or user) Permission: rorcex:reports.execute + rorcex:reports.view + rorcex:reports.export
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
public_id | string | Yes | The run ID |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
format | string | No | csv (the default and only supported format) |
bom | boolean | No | 1 (default) starts the file with a UTF-8 byte-order mark so Excel decodes accented text. Pass 0 when a script reads the file as plain UTF-8, or the mark becomes part of the first column name |
Status: 200 OK with a text/csv attachment.
The user or service account that queued the run can always export it. Anyone else in the same organization must also send the store scope the run was computed under (the same set of stores, resolved from X-Organization-Context / X-Organization-Scope), so a colleague with fewer stores cannot download a wider run's rows — run the report yourself instead.
Errors
| Status | Description |
|---|---|
| 403 | Caller lacks one of the three permissions, the run belongs to a different organization, or another caller queued it over a different store scope (run the report yourself) |
| 404 | The run does not exist or is not yet completed |
| 422 | format or bom has an unsupported value |