API reference
Customers API
The business’s guest book, newest first. Use it for CRM and marketing syncs.
List customers
/api-v1-customersReturns guests newest first by created_at, optionally filtered by a search term.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
search | string | Optional | Matches name, phone, phone digits, email or member number, case-insensitively.At most 120 characters. |
cursor | string | Optional | The next_cursor from the previous page. Opaque; send it back unchanged. |
limit | integer | Optional | Rows per page.Default 50. At least 1; values above 100 are capped at 100. |
Response fields (200)
| Name | Type | Description |
|---|---|---|
data[].member_number | string | null | The loyalty member number. |
data[].member_since | date | null | When they joined the programme. |
data[].tier | string | null | The loyalty tier’s name. |
data[].points_balance | integer | Points available. |
Errors
Every error answers { "error": { "code", "message", "request_id" } }. Branch on code; see Errors for retrying.
| HTTP | Code | When |
|---|---|---|
| 400 | validation_failed | A query parameter did not parse; the message names it. Also returned when from/to span more than 400 days. |
| 401 | invalid_key | The Authorization header is missing, malformed, or the key is revoked or inactive. |
| 402 | payment_required | The business’s plan does not include API access (Sansflow Premium). The key itself is fine. |
| 403 | insufficient_scope | The key does not carry the scope this endpoint needs, or it is pinned to a different outlet. |
| 429 | rate_limited | Over the key’s per-minute limit (60 for sf_test_, 300 for sf_live_). Wait the seconds in Retry-After. |
| 500 | internal_error | Something failed on our side. Quote the request_id when you report it. |
curl -G "https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-customers" \
-H "Authorization: Bearer $SANSFLOW_API_KEY" \
--data-urlencode "search=0812"const url = new URL("https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-customers");
url.search = new URLSearchParams({
"search": "0812"
}).toString();
const res = await fetch(
url,
{
headers: {
Authorization: `Bearer ${process.env.SANSFLOW_API_KEY}`,
},
},
);
const json = await res.json();
if (!res.ok) throw new Error(`${json.error.code} (${json.error.request_id})`);
console.log(json);import os
import requests
res = requests.get(
"https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-customers",
headers={
"Authorization": f"Bearer {os.environ['SANSFLOW_API_KEY']}",
},
params={
"search": "0812",
},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']} ({data['error']['request_id']})")
print(data)GET /api-v1-customers
{
"data": [
{
"id": "c4e8a2f1-6b9d-4e3a-8f72-1d5c9b0a3e24",
"name": "Rina",
"phone": "081200000000",
"email": null,
"address": null,
"member_number": "M-0042",
"member_since": "2026-03-14",
"tier": "Gold",
"points_balance": 120,
"created_at": "2026-03-14T09:02:11+00:00",
"updated_at": "2026-10-30T13:45:00+00:00"
}
],
"has_more": false,
"next_cursor": null,
"request_id": "5d0c7e2a-91f4-4b3e-a8c6-0f2e4b6d8a1c"
}Example values. Run replays this response here; nothing is sent.
Retrieve a customer
/api-v1-customers/{customer_id}One guest with lifetime stats: order count, net spend, last order and reservation visits.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
customer_id | uuid | Required | The customer’s id. |
Response fields (200)
| Name | Type | Description |
|---|---|---|
notes | string | null | Staff notes on the guest. |
stats.orders | integer | Orders linked to the guest. |
stats.lifetime_net | integer | Net spend in rupiah. |
stats.last_order_at | timestamp | null | Their latest order. |
stats.reservation_visits | integer | Reservations on record. |
Errors
Every error answers { "error": { "code", "message", "request_id" } }. Branch on code; see Errors for retrying.
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_request | The id in the path is not a UUID. |
| 401 | invalid_key | The Authorization header is missing, malformed, or the key is revoked or inactive. |
| 402 | payment_required | The business’s plan does not include API access (Sansflow Premium). The key itself is fine. |
| 403 | insufficient_scope | The key does not carry the scope this endpoint needs, or it is pinned to a different outlet. |
| 429 | rate_limited | Over the key’s per-minute limit (60 for sf_test_, 300 for sf_live_). Wait the seconds in Retry-After. |
| 404 | customer_not_found | No such customer in this business’s guest book. |
| 500 | internal_error | Something failed on our side. Quote the request_id when you report it. |
curl "https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-customers/c4e8a2f1-6b9d-4e3a-8f72-1d5c9b0a3e24" \
-H "Authorization: Bearer $SANSFLOW_API_KEY"const res = await fetch(
"https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-customers/c4e8a2f1-6b9d-4e3a-8f72-1d5c9b0a3e24",
{
headers: {
Authorization: `Bearer ${process.env.SANSFLOW_API_KEY}`,
},
},
);
const json = await res.json();
if (!res.ok) throw new Error(`${json.error.code} (${json.error.request_id})`);
console.log(json);import os
import requests
res = requests.get(
"https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-customers/c4e8a2f1-6b9d-4e3a-8f72-1d5c9b0a3e24",
headers={
"Authorization": f"Bearer {os.environ['SANSFLOW_API_KEY']}",
},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']} ({data['error']['request_id']})")
print(data)GET /api-v1-customers/{customer_id}
{
"data": {
"id": "c4e8a2f1-6b9d-4e3a-8f72-1d5c9b0a3e24",
"name": "Rina",
"phone": "081200000000",
"email": null,
"address": null,
"notes": null,
"member_number": "M-0042",
"member_since": "2026-03-14",
"tier": "Gold",
"points_balance": 120,
"stats": {
"orders": 14,
"lifetime_net": 2450000,
"last_order_at": "2026-10-30T13:44:51+00:00",
"reservation_visits": 3
},
"created_at": "2026-03-14T09:02:11+00:00",
"updated_at": "2026-10-30T13:45:00+00:00"
},
"request_id": "5d0c7e2a-91f4-4b3e-a8c6-0f2e4b6d8a1c"
}Example values. Run replays this response here; nothing is sent.
Base URL, keys and conventions: API reference overview.
Stuck? Quote the request_id from the response and write toSansflow support.Checked against API v1 on .