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 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.
Use one of these in place of explicit FirstTradingDay/LastTradingDay parameters. Resolved server-side against the location’s own trading-day cutover time.
Live wallboard numbers. Yesterday’s sales by hour. Channel mix. Product mix. Cash reconciliation. A nightly export to your data warehouse.
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 /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
}
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 /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
]
}
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 /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 }
}
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 /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
}
]
}
What was taken, in what tender, by which till, for a given trading day. Feeds into your accounting system alongside bank settlement.
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
}
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:
// 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));
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.
Auth, environments, your first API call.
Products, modifiers, prices, allergens, deals.
Live orders, history, refunds, webhooks.
Profiles, loyalty progress, marketing segments.
Couriers, payment devices, third-party integrators.
API surface, auth, the platform at a glance.
Test environment, sandbox tenant, sample data, real endpoints. No commitment.