Reports
The reports resource exposes nine pre-built reports. Every report has:
- A JSON read for in-app dashboards or programmatic use.
- A CSV export that runs asynchronously and returns a signed S3 download URL.
Reads run against read replicas, and most aggregations come from pre-aggregated rollups — large date windows return quickly.
Path prefix: /preorders/reports
Report catalog
GET /preorders/reportsReturns a manifest of every available report — name, description, aliases, and URL — generated from the live registry:
{
"reports": [
{
"name": "revenue",
"description": "Preorder revenue over a date window, bucketed by day/week/month.",
"aliases": ["revenue report", "preorder revenue", "..."],
"url": "/api/v2/external/preorders/reports/revenue"
}
]
}(GET /preorders/reports/help returns the expanded shape with full query schemas.)
Filters
All reads accept a date window plus offer scoping:
| Param | Type | Notes |
|---|---|---|
from | ISO 8601 date | Start of window (inclusive) |
to | ISO 8601 date | End of window (inclusive) |
offer_id | UUID | Scope to a single offer |
offer_ids[] | array of UUIDs | Scope to several offers |
currency | string | Reporting currency |
Per-report extras (check /help for each report's exact schema):
product_id,variant_id,market_id— onsummary,revenue,by_date, and the product/variant breakdowns.granularity(day/week/month) — on time-series reports (revenue,by_date).metrics— onby_date, to restrict which metric columns are computed.include_deposits/include_balances— onrevenue(both defaulttrue; at least one must betrue). Restrict revenue to the deposit or balance portion of each line.page,per_page— oncustomers.
by_variant requires offer_id (or offer_ids[]) — variant-level data without an offer scope isn't supported.
Reads
| Method | Path | Description |
|---|---|---|
GET | /preorders/reports/summary | High-level overview of preorder activity over a date window. |
GET | /preorders/reports/revenue | Preorder revenue, bucketed by day/week/month. |
GET | /preorders/reports/by_offer | Per-offer breakdown (orders, units, revenue, customers). |
GET | /preorders/reports/by_variant | Per-variant performance for a specific offer. |
GET | /preorders/reports/by_product | Per-product performance, rolled up across variants. |
GET | /preorders/reports/by_date | Time-series of every preorder metric at the chosen granularity. |
GET | /preorders/reports/cancellations | Preorder cancellation metrics with reason + per-offer breakdowns. |
GET | /preorders/reports/balance_collection | Outstanding preorder balances and balance-collection performance. |
GET | /preorders/reports/customers | Paginated list of customers with preorder activity in the window. |
Notes per report
summary— thetotalsblock includes the payment split:deposits_collected,balances_collected,balances_outstanding, alongside order/unit/revenue counts and aby_offer_top_5list.revenue— echoesinclude_deposits/include_balancesback in the response so you can tell which slice you're looking at.balance_collection— includesbalances_collectedandbalances_failedcollection-performance counters.cancellations— includesrefunds_issued. The reason breakdown requires thecancel_reasonfield on the order record; cancellations from before that field was captured appear under"unknown".by_variant—offer_idis required. Returns one row per attached variant.
CSV exports
Every read has a sibling */export action:
| Method | Path |
|---|---|
POST | /preorders/reports/summary/export |
POST | /preorders/reports/revenue/export |
POST | /preorders/reports/by_offer/export |
POST | /preorders/reports/by_variant/export |
POST | /preorders/reports/by_product/export |
POST | /preorders/reports/by_date/export |
POST | /preorders/reports/cancellations/export |
POST | /preorders/reports/balance_collection/export |
POST | /preorders/reports/customers/export |
Exports accept the same filter parameters as the corresponding read. They submit a job and return 202 Accepted:
{
"job_id": "exp_a8c3f01b9d2e4471",
"status_url": "/api/v2/external/preorders/reports/exports/exp_a8c3f01b9d2e4471"
}Polling
GET /preorders/reports/exports/:job_idstatus is one of running, ready, failed, or cancelled. When ready, the response includes the download URL:
{
"job_id": "exp_a8c3f01b9d2e4471",
"status": "ready",
"download_url": "https://...s3.amazonaws.com/...",
"file_name": "preorders_revenue_2026-01-01_2026-05-28.csv",
"row_count": 22,
"progress": 100
}The pre-signed S3 URL is valid for 7 days. The job-status record itself expires ~24 hours after completion, so fetch the URL promptly (re-run the export if it's gone).
Example: revenue by week
curl 'https://app.stoqapp.com/api/v2/external/preorders/reports/revenue?from=2026-01-01&to=2026-05-28&granularity=week' \
-H "X-Auth-Token: $STOQ_API_KEY"curl -X POST 'https://app.stoqapp.com/api/v2/external/preorders/reports/revenue/export' \
-H "X-Auth-Token: $STOQ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from":"2026-01-01","to":"2026-05-28","granularity":"week"}'# Poll until status: "ready"
curl 'https://app.stoqapp.com/api/v2/external/preorders/reports/exports/exp_a8c3f01b9d2e4471' \
-H "X-Auth-Token: $STOQ_API_KEY"