Skip to content
Get API key

API v1

API reference

15 endpoints over HTTPS and JSON, plus signed webhooks. Read sales, the guest book, bookings and the daily report; create orders the till confirms; decide on booking requests.

The basics

Base URL
https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1
Auth
Authorization: Bearer sf_live_… or sf_test_…. Authentication
Plan
API access is part of Sansflow Premium. Without it calls answer 402 payment_required.
Format
JSON. Money is integer rupiah. Timestamps are ISO 8601 with offset.
Rate limit
60 requests a minute per test key, 300 per live key. Rate limits
Request id
Every response has an x-request-id header; error bodies carry it as error.request_id.

Your first call

Create a key in the Sansflow app under Settings → API keys (owners only; the key is shown once). Start with an sf_test_ key: it sees only test data and its orders never reach the till.

Keep the key on your server in an environment variable. Every sample on these pages reads it from SANSFLOW_API_KEY.

Getting started walks through it step by step.

List your outlets
curl "https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-locations" \
  -H "Authorization: Bearer $SANSFLOW_API_KEY"

Endpoints

MethodEndpointScope
Transactions
GETList transactions/api-v1-transactionstransactions.read
GETRetrieve a transaction/api-v1-transactions/{transaction_id}transactions.read
Customers
GETList customers/api-v1-customerscustomers.read
GETRetrieve a customer/api-v1-customers/{customer_id}customers.read
Reservations
GETList reservations/api-v1-reservationsreservations.read
Bookings and availability
GETList bookings/api-v1-bookingsreservations.read
GETRetrieve a booking/api-v1-bookings/{booking_id}reservations.read
GETRetrieve availability/api-v1-availabilityreservations.read
POSTConfirm a booking/api-v1-bookings/{booking_id}/confirmreservations.write
POSTReject a booking/api-v1-bookings/{booking_id}/rejectreservations.write
Daily report
GETRetrieve the daily report/api-v1-reports-dailyreports.read
Locations
GETList locations/api-v1-locationsbusiness.read
Menu
GETRetrieve the menu/api-v1-menumenu.read
Orders
POSTCreate an order/api-v1-orders-createorders.create
GETRetrieve an order/api-v1-orders-get/{order_id}orders.read

Scopes

A key carries only the scopes you tick when you create it, and can be pinned to one outlet. Give each integration the least it needs: an accounting sync is transactions.read + reports.read; a booking bot is reservations.read + reservations.write.

ScopeGrantsEndpoints
menu.readThe menu as one outlet sells itRetrieve the menu
orders.createCreating orders the till confirmsCreate an order
orders.readReading back orders the API createdRetrieve an order
transactions.readTill transaction history and detailList transactions, Retrieve a transaction
customers.readThe guest bookList customers, Retrieve a customer
reservations.readReservations, incoming booking requests and slot availabilityList reservations, List bookings, Retrieve a booking, Retrieve availability
reservations.writeConfirming and rejecting booking requestsConfirm a booking, Reject a booking
reports.readThe daily sales summaryRetrieve the daily report
business.readThe outlets the key may readList locations

A key pinned to one outlet cannot read another: another outlet’s location_id is 403, and without a filter every list and the daily report are scoped to the pinned outlet.

Conventions

Lists and pagination

Lists return data, has_more and next_cursor, newest first. Send next_cursor back as ?cursor= until has_more is false. limit defaults to 50; values above 100 are capped at 100. Keyset paging means a page never repeats or skips a row while new rows arrive. Pagination

Retrieve calls

A single object comes back as { "data": { … }, "request_id" }. The menu and the two orders endpoints predate that envelope: their body is the object itself.

Writes

Every POST needs an Idempotency-Key header. Repeat a request with the same key and you get the first answer back; reuse a key with a different body and you get 409. Idempotency

List envelope
{
  "data": [
    "…"
  ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMS0wNVQxMjo0MTowMy4yMTRafGIxYTdjOWUy",
  "request_id": "5d0c7e2a-91f4-4b3e-a8c6-0f2e4b6d8a1c"
}

Errors

Errors use HTTP status codes and a stable error.code. Branch on the code, never on the message. Only answered calls (status below 400, and the menu’s 304) count toward your plan’s API usage. Errors

Error body
{
  "error": {
    "code": "insufficient_scope",
    "message": "Key is missing required scope: customers.read",
    "request_id": "5d0c7e2a-91f4-4b3e-a8c6-0f2e4b6d8a1c"
  }
}
HTTPCodeMeaning
401invalid_keyThe Authorization header is missing, malformed, or the key is revoked or inactive.
402payment_requiredThe business’s plan does not include API access (Sansflow Premium). The key itself is fine.
403insufficient_scopeThe key does not carry the scope this endpoint needs, or it is pinned to a different outlet.
429rate_limitedOver the key’s per-minute limit (60 for sf_test_, 300 for sf_live_). Wait the seconds in Retry-After.
400validation_failedA query parameter did not parse; the message names it. Also returned when from/to span more than 400 days.
400invalid_requestThe id in the path is not a UUID.
400invalid_jsonThe POST body is not valid JSON.
400idempotency_key_requiredA POST without an Idempotency-Key header (1-255 characters).
409idempotency_key_conflictThe Idempotency-Key was already used with a different request.
404not_foundUnknown path under this endpoint.
405method_not_allowedWrong HTTP method for this endpoint.
404transaction_not_foundNo such transaction for this key: another business, deleted, or on the other side of the test/live split.
404customer_not_foundNo such customer in this business’s guest book.
404booking_not_foundNo such booking for this business or, for an outlet-pinned key, for that outlet.
404order_not_foundNo order created through the API with this id for this key (test keys see only test orders, live keys only live ones).
404location_not_foundlocation_id is unknown or inactive for this business.
404table_not_foundtable_id is not an active table at the booking’s outlet.
409capacity_fullThe slot has no free capacity left. The booking stays pending.
409table_takenThe named table already has a reservation overlapping that time.
409proof_requiredThe booking has a pre-order and no transfer proof yet. It stays pending.
409booking_not_confirmableThe booking is rejected or expired; only pending bookings can be confirmed.
409booking_not_rejectableThe booking is confirmed or expired; only pending bookings can be rejected.
500internal_errorSomething failed on our side. Quote the request_id when you report it.
422validation_failedOn a POST: the body failed validation; error.details lists the problems.

Stuck? Quote the request_id from the response and write toSansflow support.Checked against API v1 on .