Supercommerce API Docs
Admin API

Analytics Module — Admin surface

Admin-facing HTTP surface for precomputed platform-wide dashboard snapshots (GMV, orders, commission & payouts, vendor leaderboard, customers, inventory KPIs for today / 7d / 30d / all-time) and a live SSE platform paid-order feed.

Admin-facing HTTP surface for precomputed platform-wide dashboard snapshots (GMV, orders, commission & payouts, vendor leaderboard, customers, inventory KPIs) and a live SSE paid-order feed for the whole platform. The dashboard is computed every 10 minutes by a background worker and served directly from a snapshot table — it is never computed on request. The SSE stream is a long-lived connection that emits a baseline count on connect and increments as new paid orders arrive anywhere on the platform.

This is the platform-wide counterpart to the per-vendor Analytics — Vendor surface. Where the vendor surface scopes every metric to one vendor, the admin surface aggregates across all vendors and adds admin-only metrics (vendor leaderboard, platform commission, payouts, customers).

Source: api-modules/admin-analytics/src/controllers/admin-analytics.controller.ts, api-modules/admin-analytics/src/controllers/admin-live-orders.controller.ts.


Conventions

Authentication & authorization

Both endpoints require a Better-Auth admin bearer session and the analytics:view permission.

Authorization: Bearer <session-token>

Guarded by BetterAuthGuard + PermissionsGuard with @RequirePermissions({ analytics: ["view"] }). The analytics resource is granted to the built-in superAdmin and admin roles; narrower dynamic roles must be granted analytics:view explicitly. A session without the permission is rejected with 403 Forbidden (FORBIDDEN).

There is no tenant scoping — every metric spans the entire platform.

Response envelope (dashboard)

{
  "data": { /* dashboard payload */ },
  "message": "Success",
  "statusCode": 200
}

SSE stream (live/orders)

The SSE endpoint is not wrapped by the standard response envelope. It is a raw text/event-stream connection; each frame is a data: line containing a JSON object.

Error envelope

statusCodeerrorCode examples
400BAD_REQUEST, VALIDATION_ERROR
401UNAUTHORIZED
403FORBIDDEN (missing analytics:view)
500INTERNAL_SERVER_ERROR

Money + dates

All monetary fields are integer subunits (paise / cents / eurocents — single currency per tenant). Date/time fields are ISO 8601 strings (computedAt, at, bucket).


Endpoints

GET /admin/analytics/dashboard

Returns the precomputed platform analytics snapshot for the selected time window.

The snapshot is computed every 10 minutes by a background BullMQ worker (admin-analytics-refresh job). This read never recalculates on request — it always returns the latest stored snapshot. To force a fresh recompute without waiting for the next 10-minute tick, use POST /admin/analytics/refresh (below), then re-read this endpoint. Before the worker has run for the first time, every metric is zeroed and computedAt is null.

Query parameters

ParameterTypeDefaultDescription
window"today" | "7d" | "30d" | "all_time""30d"The analytics time window to serve

Response fields (data object)

FieldTypeDescription
windowstringThe window requested
computedAtstring | nullISO datetime of the last snapshot computation, or null if never computed
grossSalesint (subunits)Platform GMV — total order grand-total in the window (includes shipping + tax)
ordersCountintTotal orders placed in the window
unitsSoldintTotal line-item units sold
aovint (subunits)Average order value (grossSales / ordersCount, 0 when ordersCount = 0)
refundsAmountint (subunits)Total value of refunds processed
deliveredOrdersintSub-orders in delivered status
cancelledOrdersintSub-orders in cancelled status
avgFulfillSecondsintAverage seconds from order placed to fulfilled (across vendors)
avgDeliverSecondsintAverage seconds from fulfilled to delivered (across vendors)
commissionEarnedint (subunits)Platform commission earned (ledger sale entries)
vendorNetEarningsint (subunits)Vendor net earnings accrued (ledger sale + refund)
payoutsPaidint (subunits)Total paid out to vendors (payouts marked paid in the window)
vendorsTotalintTotal registered vendors
vendorsActiveintVendors with at least one sub-order in the window
customersTotalintTotal registered (non-admin, non-anonymous) customers
newCustomersintCustomers created within the window
returningCustomersintDistinct in-window buyers who also have an order before the window start
productsTotalintTotal products platform-wide (excludes soft-deleted)
productsActiveintActive products platform-wide
productsDraftintDraft products platform-wide
productsArchivedintArchived products platform-wide
skusLowStockintSKUs below their low-stock threshold
skusOutOfStockintSKUs with zero available inventory
inventoryUnitsOnHandintTotal on-hand units across all SKUs
pendingActionCountintSub-orders awaiting vendor action (pending status)
salesTrendTrendBucket[]Revenue + order count over time (see below)
statusBreakdownStatusBreakdownSub-order counts by status (see below)
topVendorsByRevenueTopVendor[] (max 5)Top 5 vendors ranked by revenue
topVendorsByOrdersTopVendor[] (max 5)Top 5 vendors ranked by order count
topProductsByRevenueTopProduct[] (max 5)Top 5 SKUs ranked by revenue
topProductsByUnitsTopProduct[] (max 5)Top 5 SKUs ranked by units sold
lowStockListLowStockItem[] (max 20)Up to 20 SKUs closest to or past their low-stock threshold
deltasDeltas | nullPeriod-over-period changes; null for all_time (no previous period)

TrendBucket

Bucket granularity depends on the window:

  • today → hourly buckets
  • 7d, 30d → daily buckets
  • all_time → monthly buckets
{
  bucket: string;   // ISO datetime (start of the bucket)
  revenue: number;  // integer subunits
  orders: number;
}

StatusBreakdown

{
  pending: number;
  fulfilled: number;
  delivered: number;
  cancelled: number;
  returned: number;
}

TopVendor

{
  vendorId: string;
  name: string;
  revenue: number;  // integer subunits
  orders: number;
}

TopProduct

{
  variantId: string;
  productId: string | null;
  name: string;
  sku: string;
  units: number;
  revenue: number;  // integer subunits
}

LowStockItem

{
  variantId: string;
  sku: string;
  name: string;
  onHand: number;
  threshold: number | null;
}

Deltas

Holds period-over-period comparison for four headline KPIs. Each delta has an absolute value (in the same unit as the metric) and a pct (percentage change, or null if the previous period had a zero baseline so percentage is undefined). The deltas field itself is null for the all_time window because there is no defined previous period.

{
  grossSales:       { absolute: number; pct: number | null };
  ordersCount:      { absolute: number; pct: number | null };
  unitsSold:        { absolute: number; pct: number | null };
  commissionEarned: { absolute: number; pct: number | null };
}

Example response

// GET /admin/analytics/dashboard?window=7d
{
  "data": {
    "window": "7d",
    "computedAt": "2026-06-08T09:40:00.000Z",
    "grossSales": 84200000,
    "ordersCount": 612,
    "unitsSold": 1043,
    "aov": 137581,
    "refundsAmount": 1250000,
    "deliveredOrders": 470,
    "cancelledOrders": 33,
    "avgFulfillSeconds": 16200,
    "avgDeliverSeconds": 169200,
    "commissionEarned": 12630000,
    "vendorNetEarnings": 71570000,
    "payoutsPaid": 58000000,
    "vendorsTotal": 38,
    "vendorsActive": 24,
    "customersTotal": 5821,
    "newCustomers": 142,
    "returningCustomers": 318,
    "productsTotal": 1987,
    "productsActive": 1840,
    "productsDraft": 96,
    "productsArchived": 51,
    "skusLowStock": 73,
    "skusOutOfStock": 22,
    "inventoryUnitsOnHand": 41280,
    "pendingActionCount": 109,
    "salesTrend": [
      { "bucket": "2026-06-02T00:00:00.000Z", "revenue": 11200000, "orders": 82 },
      { "bucket": "2026-06-03T00:00:00.000Z", "revenue": 12450000, "orders": 90 },
      { "bucket": "2026-06-04T00:00:00.000Z", "revenue": 13900000, "orders": 101 },
      { "bucket": "2026-06-05T00:00:00.000Z", "revenue": 12800000, "orders": 95 },
      { "bucket": "2026-06-06T00:00:00.000Z", "revenue": 13100000, "orders": 96 },
      { "bucket": "2026-06-07T00:00:00.000Z", "revenue": 11550000, "orders": 86 },
      { "bucket": "2026-06-08T00:00:00.000Z", "revenue": 9200000,  "orders": 62 }
    ],
    "statusBreakdown": {
      "pending": 109,
      "fulfilled": 0,
      "delivered": 470,
      "cancelled": 33,
      "returned": 0
    },
    "topVendorsByRevenue": [
      { "vendorId": "ven_01", "name": "GlowLabs", "revenue": 18400000, "orders": 132 },
      { "vendorId": "ven_02", "name": "DermaCo",  "revenue": 14100000, "orders": 98 },
      { "vendorId": "ven_03", "name": "PureSkin", "revenue": 11900000, "orders": 87 },
      { "vendorId": "ven_04", "name": "AuraBeauty", "revenue": 9700000, "orders": 71 },
      { "vendorId": "ven_05", "name": "NudeEssentials", "revenue": 8200000, "orders": 64 }
    ],
    "topVendorsByOrders": [
      { "vendorId": "ven_01", "name": "GlowLabs", "revenue": 18400000, "orders": 132 },
      { "vendorId": "ven_02", "name": "DermaCo",  "revenue": 14100000, "orders": 98 },
      { "vendorId": "ven_03", "name": "PureSkin", "revenue": 11900000, "orders": 87 },
      { "vendorId": "ven_04", "name": "AuraBeauty", "revenue": 9700000, "orders": 71 },
      { "vendorId": "ven_05", "name": "NudeEssentials", "revenue": 8200000, "orders": 64 }
    ],
    "topProductsByRevenue": [
      { "variantId": "var_01", "productId": "prod_01", "name": "Vitamin C Serum 30ml", "sku": "VCS-30", "units": 220, "revenue": 6600000 }
    ],
    "topProductsByUnits": [
      { "variantId": "var_03", "productId": "prod_01", "name": "Vitamin C Serum 15ml", "sku": "VCS-15", "units": 410, "revenue": 2870000 }
    ],
    "lowStockList": [
      { "variantId": "var_07", "sku": "MZ-CLN-100", "name": "Micellar Cleanser 100ml", "onHand": 3, "threshold": 10 },
      { "variantId": "var_08", "sku": "RET-EYE",    "name": "Retinol Eye Serum",      "onHand": 0, "threshold": 5 }
    ],
    "deltas": {
      "grossSales":       { "absolute": 5400000, "pct": 6.85 },
      "ordersCount":      { "absolute": 41,      "pct": 7.18 },
      "unitsSold":        { "absolute": 63,      "pct": 6.43 },
      "commissionEarned": { "absolute": 810000,  "pct": 6.85 }
    }
  },
  "message": "Success",
  "statusCode": 200
}

POST /admin/analytics/refresh

Forces an immediate recompute of every snapshot window instead of waiting for the scheduled 10-minute tick.

Requires @RequirePermissions({ analytics: ["refresh"] }) — granted to the built-in superAdmin and admin roles alongside analytics:view.

The recompute runs in the background on the worker, not in this request. The handler enqueues a one-off admin-analytics-refresh job (fixed jobId admin-analytics-refresh-manual, so rapid repeat calls dedup to a single in-flight run) onto the same admin-analytics BullMQ queue the cron uses, then returns immediately. Poll GET /admin/analytics/dashboard afterwards — once the worker finishes, every window's computedAt advances.

No request body.

Response 200{ "data": { "enqueued": true }, "message": "Success", "statusCode": 200 }.

StatusCodeWhen
403FORBIDDENSession lacks analytics:refresh

GET /admin/analytics/live/orders (Server-Sent Events)

Opens a long-lived SSE connection (text/event-stream) that streams real-time paid-order activity across the whole platform.

This endpoint is not wrapped in the standard response envelope — the response body is a raw event stream.

Behaviour

EventWhen emittedPayload
Immediately on (re)connectbaseline snapshot of today's platform-wide paid-order countsnapshot frame
On each newly-paid order anywhere on the platformincremented live countorder.paid frame
Every 30 secondskeep-aliveheartbeat frame

Self-healing counter: on every connect (including EventSource auto-reconnects after a dropped connection), the controller re-baselines the count from a fresh DB query (COUNT(order WHERE payment_status='paid' AND paid_at >= start-of-day)). A missed event corrects itself within one reconnect cycle — the count never permanently drifts.

Frame shapes

All frames arrive as data: <JSON>\n\n lines.

snapshot — emitted once immediately on connect:

{
  "type": "snapshot",
  "paidOrdersToday": 184,
  "at": "2026-06-08T09:41:05.123Z"
}

order.paid — emitted for each newly-paid order on the platform:

{
  "type": "order.paid",
  "paidOrdersToday": 185,
  "orderNumber": "ORD-2026-00892",
  "grandTotal": 249900,
  "at": "2026-06-08T09:53:28.447Z"
}

grandTotal is the order's total in integer subunits.

heartbeat — emitted every 30 seconds:

{
  "type": "heartbeat",
  "at": "2026-06-08T10:00:05.001Z"
}

Example: curl

curl -N \
  -H "Authorization: Bearer <session-token>" \
  -H "Accept: text/event-stream" \
  https://api.example.com/admin/analytics/live/orders

On this page