Using the Sales API

API OAuth Sales KPI Analytics Android iOS averageTicket terminalId

Purpose of this guide

This guide covers how to retrieve sales for an entire business account, a specific store, or a specific POS terminal using ReceiptRoller's Sales API. It is the entry point for building a "sales dashboard" from a mobile app or server integration.

Prerequisites

  • You have obtained an access token via the OAuth 2.0 authorization code flow
  • Your app's scopes include sales.read
  • You have already obtained the business account and store IDs (Listing business accounts, stores, and POS terminals)
  • Base URL: https://receiptroller.io

Endpoint list

EndpointPurposeMain filters
GET /api/v1/sales/kpisThis month's KPIs (revenue, transactions, average ticket) and month-over-month comparisonorganizationId, storeId?, terminalId?
GET /api/v1/sales/trendMonthly trend (12 months by default, up to 60 months)organizationId, storeId?, months, terminalId?
GET /api/v1/sales/productsSales by product + ABC analysis + gross margin rateorganizationId, storeId, since, until, category, search, sortBy, terminalId?
GET /api/v1/sales/visitorsVisitor analysis (time of day, day of week, conversion, new / repeat)organizationId, storeId, since, until, terminalId?
GET /api/v1/sales/multi-trendOverlay the monthly trends of multiple stores (up to 10 stores)organizationId, storeIds (comma-separated), months
GET /api/v1/sales/adviceAI advice (Intelligence panel cards)organizationId, storeId?, severity?, category?
GET /api/v1/sales/forecastEnd-of-month projectionorganizationId, storeId?
GET /api/v1/sales/churnCustomer churn score (RFM-lite)organizationId, storeId, lookbackDays
GET /api/v1/sales/recommendationsProduct recommendations for individual customersorganizationId, storeId, customerId, topN, lookbackDays

Sales for the whole business account (KPIs)

When you want "this month's numbers" for the selected business account, call kpis without a storeId.

GET /api/v1/sales/kpis?organizationId={organizationId}
Authorization: Bearer {access_token}

Response structure

Every numeric field is returned as a common nested type called KpiValue. There is no need to recompute month-over-month or percentage change on the client side.

{
  "organizationId": "0aa2...e7b1",
  "currentMonth": "2026-06",
  "previousMonth": "2026-05",

  "totalStores":       { "current": 12,       "previous": 12,       "changePercent": 0.0  },
  "totalStaff":        { "current": 87,       "previous": 84,       "changePercent": 3.6  },
  "totalProducts":     { "current": 1450,     "previous": 1410,     "changePercent": 2.8  },
  "totalCustomers":    { "current": 8920,     "previous": 8650,     "changePercent": 3.1  },
  "totalRevenue":      { "current": 3850000,  "previous": 3520000,  "changePercent": 9.4  },
  "totalTransactions": { "current": 1240,     "previous": 1180,     "changePercent": 5.1  },
  "averageTicket":     { "current": 3104.84,  "previous": 2983.05,  "changePercent": 4.1  },
  "avgGoogleRating":   { "current": 4.3,      "previous": 4.2,      "changePercent": 2.4  },
  "totalReservations": { "current": 210,      "previous": 195,      "changePercent": 7.7  }
}

Inside KpiValue

  • current — this month's value
  • previous — last month's value (null when there is no snapshot for last month)
  • changePercent — month-over-month change rate (%). null when previous is null or 0

About averageTicket

  • The server returns totalRevenue / totalTransactions already computed. There is no need to recompute it on the client
  • When the transaction count is 0, it returns current: 0 (it never becomes NaN)
  • The same averageTicket field is returned at the store level and the POS terminal level

Sales for a specific store

Adding storeId to the same endpoint returns KPIs for that store only. The response is in the StoreDashboardKpis shape (with store-oriented field names such as revenue, transactionCount, and averageTicket).

GET /api/v1/sales/kpis?organizationId={organizationId}&storeId={storeId}
Authorization: Bearer {access_token}
{
  "storeId": "store-001",
  "storeName": "渋谷本店",
  "currentMonth": "2026-06",

  "staffCount":       { "current": 12,      "previous": 11,      "changePercent": 9.1  },
  "revenue":          { "current": 850000,  "previous": 780000,  "changePercent": 8.9  },
  "transactionCount": { "current": 340,     "previous": 310,     "changePercent": 9.6  },
  "averageTicket":    { "current": 2500.0,  "previous": 2516.13, "changePercent": -0.6 },
  "reservationCount": { "current": 45,      "previous": 38,      "changePercent": 18.4 },
  "googleRating":     { "current": 4.4,     "previous": 4.3,     "changePercent": 2.3  },
  "productCount":     { "current": 320,     "previous": 318,     "changePercent": 0.6  },
  "customerCount":    { "current": 1820,    "previous": 1750,    "changePercent": 4.0  }
}

Time-series trend

Retrieves the monthly progression over the past N months. Use it for drawing graphs.

GET /api/v1/sales/trend?organizationId={organizationId}&storeId={storeId}&months=12
Authorization: Bearer {access_token}

Notes

  • months is at most 60. Omitting storeId gives the business account total
  • Currently monthly only. If you need a daily or weekly graph, it is planned for an upcoming release (adding ?granularity=day|month is planned, tracked in t-e9a2f468)

Sales by product (with a date range)

Aggregates sales by product over any period, such as "the top 50 sellers last month."

GET /api/v1/sales/products
  ?organizationId={organizationId}
  &storeId={storeId}
  &since=2026-05-01
  &until=2026-05-31
  &sortBy=revenue
  &category=ドリンク
  &search=ラテ
  &terminalId={terminalId}     # (optional) aggregate for a specific POS terminal only
Authorization: Bearer {access_token}

Notes

  • since / until are dates (start inclusive; the end runs up to 0:00 the day after until)
  • The response includes the ABC group (A: up to 70% of cumulative sales / B: up to 90% / C: the rest), gross profit and gross margin rate, and a total gross-profit KPI
  • sortBy: revenue (default) / quantity / margin
  • Adding terminalId narrows the aggregation to that one terminal (see "Filtering by POS terminal" below)

When you want to compare multiple stores

When you want to overlay multiple stores of a business account onto a single graph, use multi-trend.

GET /api/v1/sales/multi-trend
  ?organizationId={organizationId}
  &storeIds=store-001,store-002,store-003
  &months=12
Authorization: Bearer {access_token}

Up to 10 stores. Beyond 6 stores the lines become hard to read, so it is best to limit it to the stores the user selects.

Visitor analysis (day of week and time of day)

This endpoint is for understanding behavioral patterns such as "strong on weekday mornings 7-9" or "traffic rises on Saturday afternoons."

GET /api/v1/sales/visitors
  ?organizationId={organizationId}
  &storeId={storeId}
  &since=2026-05-01
  &until=2026-05-31
  &terminalId={terminalId}     # (optional) aggregate for a specific POS terminal only
Authorization: Bearer {access_token}

The response includes the distribution by time of day, the distribution by day of week, a time-of-day × day-of-week heatmap, the conversion rate (CRM linkage rate), and the new / repeat breakdown over the past 12 months.

AI advice (Intelligence panel)

Retrieves cards such as sales anomaly detection, forecasts, and improvement suggestions.

GET /api/v1/sales/advice
  ?organizationId={organizationId}
  &storeId={storeId}
  &severity=Action
  &category=Anomaly
Authorization: Bearer {access_token}

Notes

  • severity: Action / Warning / Opportunity / Info
  • category: Trend / AverageTicket / MixShift / Anomaly / Forecast / Comparison
  • Cards are generated in a once-a-day batch (they are not real-time)

Filtering by POS terminal (terminalId)

Filtering at the POS terminal (terminalId) level — such as "sales for one register" or "sales for the terminal that had a specific cashier/staff" — is supported by four endpoints (kpis, trend, products, visitors).

# KPIs (this month's numbers for one terminal + MoM comparison, includes averageTicket)
GET /api/v1/sales/kpis?organizationId={orgId}&storeId={storeId}&terminalId={terminalId}

# Monthly trend (past N months for one terminal)
GET /api/v1/sales/trend?organizationId={orgId}&storeId={storeId}&terminalId={terminalId}&months=12

# Sales by product (period aggregation for one terminal)
GET /api/v1/sales/products?organizationId={orgId}&storeId={storeId}&terminalId={terminalId}&since=...&until=...

# Visitor analysis (time of day and day of week for one terminal)
GET /api/v1/sales/visitors?organizationId={orgId}&storeId={storeId}&terminalId={terminalId}&since=...&until=...

Notes

About terminal-level aggregation

  • Because /kpis and /trend aggregate live at terminal granularity on request, the response contains only Revenue, TransactionCount, and AverageTicket. Fields such as staff count, product count, and Google rating are not terminal-level concepts, so 0 is returned
  • Because /products and /visitors are live aggregations to begin with, all fields are returned correctly at terminal granularity

A typical flow (mobile app)

  1. Get the business accounts with GET /api/v1/me/organizations
  2. Once the user picks one, get the store list with GET /api/v1/me/organizations/{orgId}/stores
  3. On the dashboard screen, request GET /api/v1/sales/kpis and GET /api/v1/sales/trend in parallel
  4. When the user switches stores, re-fetch the same endpoints with a storeId
  5. When a "by POS terminal" tab is opened, get the terminal list with GET /api/v1/me/organizations/{orgId}/stores/{storeId}/pos-terminals, and re-fetch each sales endpoint with a terminalId for the selected terminal
  6. When a "by product" tab is opened, call GET /api/v1/sales/products
  7. When an "advice" tab is opened, call GET /api/v1/sales/advice

Permissions and tokens

  • Required scope: sales.read
  • Apps that support multiple business accounts should specify ?organizationId= on every call using a user-scoped access token
  • Apps fixed to a single business account can omit organizationId if they use a token bound to a business account at authorization time
  • Access token lifetime: 8 hours. It is set long to match a full day's work shift. The same token can be reused even when switching business accounts or stores
  • Refresh token lifetime: 30 days. When it expires, obtain a new access token with POST /api/v1/auth/refresh

Error responses

StatusMeaningWhat to do
401 UnauthorizedAccess token invalid / expiredRe-obtain with the refresh token
403 ForbiddenScope does not include sales.read / user is not a member of the business accountCheck the app's registered scopes and the selected business account
400 Bad RequestMissing required parameter (e.g. specifying storeId/terminalId on products without storeId)Review the query parameters
404 Not FoundterminalId does not belong to the specified storeIdRe-fetch the store/terminal selection UI

Related articles

Published: 2026-06-01 Updated: 2026-07-05
このトピックについて
開発者API
機能の詳細を見る
Tags
API (22) OAuth (15) Android (10) iOS (9) Webhook (6) Troubleshooting (5) api (5) App registration (4) POS Integration (4) Reference (4)
Related articles