Skip to content

Overview

Read-only access to a shop's own transaction data.

Pull-based, read-only access to transactions (bills) recorded by ScanFood POS.

EN — Authenticate with an API key issued per shop or per franchise. A key only ever sees the shops inside its own scope; any shopId outside that scope is rejected. All endpoints are GET, return JSON, and are designed to be polled on a schedule (for example every 15 minutes).

TH — ใช้กุญแจ API ที่ออกให้รายร้านหรือรายแฟรนไชส์ · กุญแจ 1 ใบเห็นเฉพาะสาขาในขอบเขตของตัวเอง ทุกเส้นเป็น GET คืน JSON และออกแบบมาให้ “ดึงเป็นรอบ” (pull)

Authentication

Send the key in the Authorization header: Authorization: Bearer sf_live_<keyId>_<secret>. Keys are shown once when issued — ScanFood stores only a hash and cannot show it again. Treat the key as a password: it belongs on your server, never in a browser or a mobile app (CORS is deliberately disabled on this API, so browser calls will be blocked by the browser).

Paging

/v1/transactions and /v1/members are paged: each returns at most limit rows (default 100, values above 200 are quietly reduced to 200). When the page comes back full, nextCursor holds the cursor for the next page; pass it back as cursor to continue. nextCursor: null means you reached the end. A cursor pointing at a row that no longer exists returns 400 bad_cursor — restart that page without a cursor. A page that happens to be exactly full on the last page returns a nextCursor, and the following request then returns an empty data with nextCursor: null. That is expected, not an error. /v1/shops is not paged: it returns every shop in the key’s scope in one response and has no limit, cursor, or nextCursor.

Two ways to select transactions (choose exactly one per request)

  • from + to — calendar days in the shop’s own timezone (YYYYMMDD, no dashes, maximum 31 days). The matching field in the response is billDate, which is formatted with dashes (YYYY-MM-DD), as is businessDate.
  • updatedSince — only rows changed at or after an ISO-8601 instant. Use this for incremental sync. Rows written before this API existed have no change marker yet and will not appear in this mode; use from/to for a full sync until your backfill is complete.

Calendar day vs business day

businessDate is the day a bill is counted as revenue by the shop (its accounting day ends at the shop’s own cut-off time, which is often after midnight). billDate is the plain calendar day. For a shop that closes its books at 06:00, a bill paid at 02:00 has tomorrow’s billDate but yesterday’s businessDate. Filtering uses billDate; group by businessDate if you need to reconcile against the shop’s own daily totals. businessDate is null on bills that were not closed through the current POS path.

Amounts

All money values are in Thai Baht (THB). net is the amount the customer paid, and it includes tip — tips are collected on behalf of staff and are not shop revenue, so subtract tip when you compute the shop’s sales.

Errors

Every failure returns { "ok": false, "error": "<code>", "message": "…" }. Branch on error, never on message. Besides the usual 4xx codes, an authenticated endpoint can answer 503 auth_unavailable: our key-checking store could not be reached, so the request was refused rather than let through. That is our side, not your key — retry with backoff and do not change or reissue anything.

Authorization: Bearer sf_live_<keyId>_<secret>. Issued per shop or per franchise. Shown once at issue time; revocable at any time. A revoked key returns 401 key_revoked; any other failure returns 401 invalid_key.

Security scheme type: http

Bearer format: sf_live_<keyId>_<secret>