Sansflow Developers
Build on the counter.
One REST API, signed webhooks and an MCP server for the shops, cafés and salons that run Sansflow POS. Read sales, send orders to the till, confirm bookings, or let an AI assistant answer questions about the business.
API v1 is live · test and live keys
Build with Sansflow
Each API is one resource with one scope, so a key only gets what its job needs. Money is whole rupiah and every time carries its offset.
Send an order from your shop, kiosk or bot to the till. Staff accept it and take payment at the counter.
TransactionsEvery sale from the till, with totals, payments and the bill it came from. For accounting and BI syncs.
CustomersThe guest book: members, tiers, points and lifetime spend. For your CRM or loyalty tools.
MenuThe menu an outlet sells right now: categories, products, variants and modifiers, with ETag caching.
ReservationsReservations by the time guests arrive, with party size, table and status from booked to seated.
BookingsBooking requests from the public booking page. See open slots, then confirm or reject a request.
WebhooksSigned POSTs when an order, booking or reservation changes, so you never have to poll.
MCPConnect Claude or ChatGPT to a business and ask about sales, tables, staff and stock in plain words.
Quick start
Your first call in a minute
- Create a test key In Sansflow, open Developers and create an
sf_test_key with thebusiness.readscope. It is shown once. - Call the API Send it as a Bearer token. The answer lists the outlets the key may read.
- Go live Swap in an
sf_live_key with the scopes your integration needs.
curl https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-locations \
-H "Authorization: Bearer $SANSFLOW_API_KEY"const res = await fetch("https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-locations", {
headers: { Authorization: `Bearer ${process.env.SANSFLOW_API_KEY}` },
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code} (request ${body.error.request_id})`);
console.log(body.data); // the outlets this key may readimport os, requests
res = requests.get(
"https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-locations",
headers={"Authorization": f"Bearer {os.environ['SANSFLOW_API_KEY']}"},
timeout=10,
)
body = res.json()
if not res.ok:
raise RuntimeError(f"{body['error']['code']} (request {body['error']['request_id']})")
print(body["data"]) # the outlets this key may read{
"data": [
{
"id": "6f1c2a4e-…",
"name": "Main",
"type": "storefront",
"is_default": true,
"is_active": true,
"address": null,
"phone": null,
"receipt_name": null,
"operating_hours": null,
"tax": { "enabled": false, "rate": null, "label": null }
}
],
"request_id": "…"
}Get your API key
The API, webhooks and MCP are part of Sansflow Premium. The business owner creates keys in the Sansflow app and hands them to the developer.
- 01
Sansflow Premium
The business is on Sansflow Premium. Without it the API answers
402 payment_required. - 02
Open Developers
In pos.sansflow.com, open Developers. Owners and anyone allowed to manage the API see it.
- 03
Create a test key
Name it, pick its scopes and, if you like, one outlet. Copy it: only its hash is kept.
- 04
Switch to live
Build against
sf_test_, then create ansf_live_key for real data.
Built for the work around the code
The Developers page in Sansflow is where keys, AI tokens and webhooks live, next to a log of every request.
Test keys
sf_test_ keys price orders exactly like live ones but never reach the till, the kitchen or reports.
Request log
Every call is logged with its status, time taken and error code. Quote the request_id when you ask for help.
Signed webhooks
HMAC-SHA256 signatures, five retries over about 8½ hours, and a send-test button for your endpoint.
WebhooksMCP server
Personal tokens for Claude and ChatGPT: read-only by default, scoped to outlets, revocable.
MCPWhat changed
- One order status everywhereAn order now carries the same status word in the create response, in GET orders and in every order.* webhook: pending_confirmation, accepted, preparing, ready, completed, cancelled or test.
- Order webhooks match GET ordersThe data of every order.* webhook is the order exactly as GET /api-v1-orders-get returns it, including totals.gross, table_ref and your external_ref.
- Pre-order notes on bookingsBookings carry preorder.order_note: the guest's note for the whole pre-order, one line of at most 300 characters, or null.
Get help
Write to us with the request_id from the response and we can find the call.