Developers · Reports & data

Every number on the dashboard — as an endpoint.

Sales by hour. Sales by channel. Sales by product. Live intraday. Cash reconciliation. Exception reports. The same data that powers the Portal dashboards, available as JSON, on trading-day ranges, ready to stream into your BI stack.

Trading days, not calendar days. What your store counts as “today” is what the API counts as today.

Metrics, reports and the trading-day.

Metrics are aggregates — revenue, order count, AOV, service times — grouped by day, hour, channel, location or product. Reports are the operational outputs — sales summaries, cash breakdowns, exception lists, product mix. Both accept a trading-day range: either two explicit dates or a named range like Last7Days.

Live & intraday — Queries API

  • GET /organizations/{orgId}/metrics/live
  • GET /organizations/{orgId}/metrics/intraday
  • GET /organizations/{orgId}/orders/live

Sales metrics — Queries API

  • GET /organizations/{orgId}/metrics/sales
  • GET /organizations/{orgId}/metrics/sales-by-hour
  • GET /organizations/{orgId}/metrics/sales-by-channel
  • GET /organizations/{orgId}/metrics/sales-by-product
  • GET /organizations/{orgId}/metrics/sales-by-location
  • GET /organizations/{orgId}/metrics/service-times

Operational reports — Queries API

  • GET /organizations/{orgId}/reports/product-mix
  • GET /organizations/{orgId}/reports/cash-reconciliation
  • GET /organizations/{orgId}/reports/exceptions
  • GET /organizations/{orgId}/reports/refunds
  • GET /organizations/{orgId}/reports/couriers
  • GET /organizations/{orgId}/reports/customer-feedback

NamedDateRange values

Use one of these in place of explicit FirstTradingDay/LastTradingDay parameters. Resolved server-side against the location’s own trading-day cutover time.

Today Yesterday ThisWeek LastWeek Last7Days Last28Days Last30Days ThisMonth LastMonth ThisYear LastYear
Data freshness. Live and intraday endpoints read the real-time event stream — latency is seconds. Sales metrics and operational reports read from the aggregated projections — a handful of minutes behind live. Trading-day totals settle after the location’s cutover. If you’re reconciling against bank settlement, wait at least 24 hours after cutover before calling.

Six things you’ll build on day one.

Live wallboard numbers. Yesterday’s sales by hour. Channel mix. Product mix. Cash reconciliation. A nightly export to your data warehouse.

1. Live metrics for a wallboard

Real-time revenue, order count and AOV for a single location — updates every second. Drive an in-store dashboard or an above-store group view.

GET
Request
GET /organizations/{orgId}/metrics/live?LocationId=e8a2…
Authorization: Bearer <token>
Response — 200 OK
{
  "locationId": "e8a2...",
  "asOf": "2026-04-19T12:47:03Z",
  "tradingDay": "2026-04-19",
  "revenue": { "amount": 1842.50, "currency": "GBP" },
  "orderCount": 74,
  "averageOrderValue": { "amount": 24.90, "currency": "GBP" },
  "ordersInFlight": 9,
  "averageMakeTimeSeconds": 412,
  "averageTimeToDoorSeconds": 1680
}

2. Sales by hour for yesterday

Classic intraday chart data. Returns one row per hour for the trading day with revenue, order count and AOV. Swap Yesterday for Today or Last7Days as needed.

GET
Request
GET /organizations/{orgId}/metrics/sales-by-hour
    ?LocationIds=e8a2...
    &TradingDays.NamedDateRange=Yesterday
Authorization: Bearer <token>
Response — 200 OK
{
  "range": { "from": "2026-04-18", "to": "2026-04-18" },
  "rows": [
    { "hour": 11, "orders": 6,  "revenue": 148.20, "aov": 24.70 },
    { "hour": 12, "orders": 32, "revenue": 796.40, "aov": 24.89 },
    { "hour": 13, "orders": 41, "revenue": 1020.15, "aov": 24.88 },
    // ... 24 rows
  ]
}

3. Sales by channel across a whole group

How much is Web doing vs in-store vs Uber Eats vs Deliveroo, across every location in a group? Pass a LocationGroups filter instead of explicit locations.

GET
Request
GET /organizations/{orgId}/metrics/sales-by-channel
    ?LocationGroups=North%20Region
    &TradingDays.NamedDateRange=Last7Days
Authorization: Bearer <token>
Response — 200 OK
{
  "rows": [
    { "channel": "Web",        "orders": 1842, "revenue": 42180.50, "share": 0.38 },
    { "channel": "App",        "orders": 612,  "revenue": 16820.75, "share": 0.15 },
    { "channel": "UberEats",   "orders": 918,  "revenue": 22405.00, "share": 0.20 },
    { "channel": "Deliveroo",  "orders": 744,  "revenue": 18602.20, "share": 0.17 },
    { "channel": "JustEat",    "orders": 410,  "revenue": 10105.80, "share": 0.09 },
    { "channel": "InStore",    "orders": 58,   "revenue": 1230.40,  "share": 0.01 }
  ],
  "totals": { "orders": 4584, "revenue": 111344.65 }
}

4. Product mix for a single location

Which items, modifiers and deals actually sold. Use this to feed forecasting, to rank top-movers by site, or to flag SKUs with unusually low attachment rates.

GET
Request
GET /organizations/{orgId}/reports/product-mix
    ?LocationId=e8a2...
    &StartDate=2026-04-12
    &EndDate=2026-04-18
Authorization: Bearer <token>
Response — 200 OK
{
  "products": [
    {
      "productId": "b21a...",
      "name": "Chicken Tikka Masala",
      "category": "Mains",
      "quantity": 184,
      "revenue": 1840.00,
      "averagePrice": 10.00,
      "attachmentRate": 0.42
    },
    {
      "productId": "9c77...",
      "name": "Peshwari Naan",
      "category": "Sides",
      "quantity": 97,
      "revenue": 339.50,
      "averagePrice": 3.50,
      "attachmentRate": 0.22
    }
  ]
}

5. Cash reconciliation for a trading day

What was taken, in what tender, by which till, for a given trading day. Feeds into your accounting system alongside bank settlement.

GET
Request
GET /organizations/{orgId}/reports/cash-reconciliation
    ?LocationId=e8a2...
    &StartDate=2026-04-18
    &EndDate=2026-04-18
Authorization: Bearer <token>
Response — 200 OK
{
  "tradingDay": "2026-04-18",
  "tenders": [
    { "type": "Cash",         "count": 42,  "amount": 580.20 },
    { "type": "Card",         "count": 218, "amount": 5120.45 },
    { "type": "StripeWeb",    "count": 96,  "amount": 2410.80 },
    { "type": "AggregatorPaid", "count": 74,  "amount": 1852.30 }
  ],
  "grossSales": 9963.75,
  "refunds": 42.00,
  "netSales": 9921.75
}

6. Nightly export to your data warehouse

The common pattern — a scheduled job pulls yesterday’s trading day across every location, flattens to rows, writes to your warehouse or BI tool (BigQuery, Snowflake, Power BI, Fabric). Pseudo-code sketch:

PATTERN
Nightly cron — 02:00 local
// 1. Get OAuth2 token (client_credentials, scope=andromeda-api)
const token = await getToken();

// 2. Pull sales-by-location for Yesterday
const sales = await fetch(
  `${QUERIES_API}/organizations/${orgId}/metrics/sales-by-location` +
  `?TradingDays.NamedDateRange=Yesterday`,
  { headers: { Authorization: `Bearer ${token}` } }
).then(r => r.json());

// 3. Pull product-mix per location in parallel
const productMix = await Promise.all(
  sales.rows.map(loc =>
    fetch(
      `${QUERIES_API}/organizations/${orgId}/reports/product-mix` +
      `?LocationId=${loc.locationId}&StartDate=${yesterday}&EndDate=${yesterday}`,
      { headers: { Authorization: `Bearer ${token}` } }
    ).then(r => r.json())
  )
);

// 4. Flatten and append to warehouse table
await warehouse.append("andromeda_daily_sales", flatten(sales, productMix));

One API, three data stores — and you don’t need to care.

Behind the scenes, some reports read the Portal projection store (near-real-time, event-sourced), some read the AMS data warehouse (daily-settled), and some read the legacy dashboard store. The API surface hides the difference. If your data needs to reconcile to bank settlement, prefer the cash-reconciliation and sales-by-* endpoints and wait 24 hours after trading-day cutover.

Explore the rest of the developer platform.

Build something on top of Andromeda.

Test environment, sandbox tenant, sample data, real endpoints. No commitment.