Supercommerce API Docs
Vendor API

Reports Module — Vendor

Vendor-scoped, registry-driven operational reports (orders sub-orders) with date-ranged paginated views, KPI/trend, and async CSV/XLSX exports — all locked to the authenticated vendor.

The vendor-facing mirror of the admin reporting surface: date-ranged, row-level operational reports scoped to the authenticated vendor, with an on-screen paginated view and async CSV/XLSX file exports. Today it exposes one report — Orders (one row per sub-order belonging to the vendor).

Source: api-modules/reports/src/controllers/vendor-report.controller.ts + vendor-report-export.controller.ts.

Optional plugin. Removing ReportsModule.forRoot({ … }) from app.module.ts disables every report + export route (admin and vendor), stops the export worker and its retention cron, and leaves the report_export table inert.


How it works

Vendor reports use the same registry-driven machinery as the admin surface (a ReportDefinition provides key, title, columns, filters, filtersSchema, run(), plus optional kpis/summary() and trendSeries/trend()), registered via ReportsModule.forRoot({ vendorReports: [...] }). The only difference: every run is locked to the caller's vendor — the report's queries filter on req.vendor.id, so a vendor only ever sees its own sub-orders and its own money columns (its allocated subtotal / discount / shipping / tax / total), never another supplier's figures or whole-order totals.

  • View is synchronous + paginated, filtered to a [from, to) window. The orders report also exposes summary KPI cards (GET .../summary) and a trend chart (GET .../trend).
  • Export is asynchronous: POST .../export enqueues a BullMQ job; a worker runs the report (scoped to the vendor) in batches, writes a CSV/XLSX file to object storage, and flips the report_export row to ready. The vendor polls the exports list and downloads the file. Files auto-expire after 7 days (daily retention sweep); the row remains as expired.

A date range is required for exports and capped at 366 days. Export files are capped at 200,000 rows — beyond that the file is marked truncated (never silently cut).

Built-in reports

KeyRow granularityDate fieldFilters
ordersone sub-order (the vendor's slice of an order)order placed datefulfillmentStatus

fulfillmentStatus is one of pending | fulfilled | delivered | cancelled | returned. Columns: orderNumber, placedAt, customerName, fulfillmentStatus, paymentStatus, and the vendor's subtotal, discountAllocated, shippingCost, taxAmount, total (all money is integer subunits). KPIs: orders, grossRevenue, avgOrderValue, discountTotal, shippingTotal. Trend series: orders, revenue.

Conventions

Authentication

All endpoints require a Better-Auth vendor session. There is no permission/RBAC gating — scoping is implicit to the active vendor (resolveActiveVendorId(session)). An export id belonging to another vendor (or to a store-wide admin export) resolves to 404, never 403.

Response envelope

Successful responses are wrapped by ResponseInterceptor into { data, message, statusCode, metadata? }. List/run endpoints use the canonical metadata: { total, limit, offset, hasMore }. The download endpoint streams the raw file (Content-Disposition: attachment) and bypasses the envelope.


Endpoints

GET /vendor/reports

List registered vendor reports with their metadata.

Response data is an array of:

{
  "key": "orders",
  "title": "Orders",
  "description": "Your sub-orders in the range with your totals, fulfillment, and payment status.",
  "dateField": "Order placed date",
  "columns": [{ "key": "orderNumber", "label": "Order #", "type": "string" }, /* … */],
  "filters": [{ "key": "fulfillmentStatus", "label": "Fulfillment status", "type": "enum", "options": [/* … */] }],
  "kpis": [{ "key": "orders", "label": "Orders", "type": "number" }, /* … */],
  "trendSeries": [{ "key": "orders", "label": "Orders", "type": "number" }, /* … */]
}

Column type is one of string | number | money | date | datetime | boolean (money is integer subunits).

GET /vendor/reports/:key/meta

Metadata for a single report (same shape as one element above).

GET /vendor/reports/:key/summary

Summary KPI values for the range. Same query params as the run endpoint (from, to, filters). Returns:

{ "kpis": [{ "key": "orders", "label": "Orders", "type": "number", "value": 312 }, /* … */] }

value is null when not computable; empty kpis when the report has no summary.

GET /vendor/reports/:key/trend

Time-bucketed series for the range (bucketed by day, or by month for ranges over ~62 days). Returns:

{
  "granularity": "day",
  "series": [{ "key": "orders", "label": "Orders", "type": "number" }, /* … */],
  "points": [{ "bucket": "<ISO>", "orders": 12, "revenue": 458900 }, /* … */]
}

GET /vendor/reports/:key

Run a report (paginated, on-screen).

Query params:

ParamTypeNotes
fromISO datetimeinclusive; omitted → last 30 days
toISO datetimeexclusive; omitted → now
filtersJSON stringreport-specific, validated against filtersSchema
limitint (1–500)default 100
offsetintdefault 0

Response: paginated array of column-keyed row objects (metadata carries totals).

POST /vendor/reports/:key/export

Queue an async export. Body:

{ "format": "csv" | "xlsx", "from": "<ISO>", "to": "<ISO>", "filters": { /* optional */ } }

from/to are required. Returns the created report_export row (status: "pending").

GET /vendor/report-exports

List the vendor's export history (newest first). Query: reportKey?, status?, limit (default 50), offset. Each row:

{
  "id": "…",
  "reportKey": "orders",
  "format": "csv",
  "status": "pending | processing | ready | failed | expired",
  "params": { "from": "<ISO>", "to": "<ISO>", "filters": {} },
  "rowCount": 312,
  "truncated": false,
  "fileName": "orders-20260101-20260201.csv",
  "fileSize": 21884,
  "error": null,
  "createdAt": "<ISO>",
  "completedAt": "<ISO>",
  "expiresAt": "<ISO>",
  "downloadUrl": "/vendor/report-exports/…/download"  // only when ready
}

GET /vendor/report-exports/:id

Status of a single export — poll this (or the list) until status: "ready". 404 if the export belongs to another vendor.

GET /vendor/report-exports/:id/download

Stream a ready export file as an attachment. 404 if not ready / expired / unknown / not owned by the caller.

On this page