Home
Preorders

Reports

Preorder analytics — nine reports, each readable as JSON and exportable as a CSV via an async job.

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/reports

Returns 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:

ParamTypeNotes
fromISO 8601 dateStart of window (inclusive)
toISO 8601 dateEnd of window (inclusive)
offer_idUUIDScope to a single offer
offer_ids[]array of UUIDsScope to several offers
currencystringReporting currency

Per-report extras (check /help for each report's exact schema):

  • product_id, variant_id, market_id — on summary, revenue, by_date, and the product/variant breakdowns.
  • granularity (day / week / month) — on time-series reports (revenue, by_date).
  • metrics — on by_date, to restrict which metric columns are computed.
  • include_deposits / include_balances — on revenue (both default true; at least one must be true). Restrict revenue to the deposit or balance portion of each line.
  • page, per_page — on customers.

by_variant requires offer_id (or offer_ids[]) — variant-level data without an offer scope isn't supported.

Reads

MethodPathDescription
GET/preorders/reports/summaryHigh-level overview of preorder activity over a date window.
GET/preorders/reports/revenuePreorder revenue, bucketed by day/week/month.
GET/preorders/reports/by_offerPer-offer breakdown (orders, units, revenue, customers).
GET/preorders/reports/by_variantPer-variant performance for a specific offer.
GET/preorders/reports/by_productPer-product performance, rolled up across variants.
GET/preorders/reports/by_dateTime-series of every preorder metric at the chosen granularity.
GET/preorders/reports/cancellationsPreorder cancellation metrics with reason + per-offer breakdowns.
GET/preorders/reports/balance_collectionOutstanding preorder balances and balance-collection performance.
GET/preorders/reports/customersPaginated list of customers with preorder activity in the window.

Notes per report

  • summary — the totals block includes the payment split: deposits_collected, balances_collected, balances_outstanding, alongside order/unit/revenue counts and a by_offer_top_5 list.
  • revenue — echoes include_deposits / include_balances back in the response so you can tell which slice you're looking at.
  • balance_collection — includes balances_collected and balances_failed collection-performance counters.
  • cancellations — includes refunds_issued. The reason breakdown requires the cancel_reason field on the order record; cancellations from before that field was captured appear under "unknown".
  • by_variantoffer_id is required. Returns one row per attached variant.

CSV exports

Every read has a sibling */export action:

MethodPath
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_id

status 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"