Concepts
Idempotency
Networks drop answers. An Idempotency-Key lets you retry a POST without creating a second order or confirming a booking twice.
The Idempotency-Key header
Every POST needs an Idempotency-Key header: any string of 1 to 255 characters, usually a UUID. Without it the API answers 400 idempotency_key_required. That covers:
POST /api-v1-orders-createPOST /api-v1-bookings/{id}/confirmPOST /api-v1-bookings/{id}/reject
Make the key once, when you decide to do the thing, and store it with that job. A retry must send the same key; a new decision gets a new key.
curl -X POST https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-bookings/<booking_id>/confirm \
-H "Authorization: Bearer $SANSFLOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3b9d6c1e-0f4a-4f7e-9a51-2c8e5d7b1a90" \
-d '{}'import { randomUUID } from "node:crypto";
// Make the key once per decision and keep it with the job, so a retry reuses it.
const idempotencyKey = randomUUID();
const res = await fetch(`https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-bookings/${bookingId}/confirm`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SANSFLOW_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({}),
});
console.log(res.headers.get("Idempotent-Replayed")); // "true" on a replayimport os, uuid, requests
# Make the key once per decision and keep it with the job, so a retry reuses it.
idempotency_key = str(uuid.uuid4())
res = requests.post(
f"https://ewpuclvmnygoddunkyrb.supabase.co/functions/v1/api-v1-bookings/{booking_id}/confirm",
headers={
"Authorization": f"Bearer {os.environ['SANSFLOW_API_KEY']}",
"Idempotency-Key": idempotency_key,
},
json={},
timeout=10,
)
print(res.headers.get("Idempotent-Replayed")) # "true" on a replayReplays and conflicts
| You send | You get |
|---|---|
| Same key, same request | The original successful response again. Booking endpoints add the header Idempotent-Replayed: true; an order comes back with "replay": true in the body. Nothing runs twice. |
| Same key, different request | 409 idempotency_key_conflict. Use a new key for a new request. |
| New key | A fresh request. |
For the booking endpoints, keys are remembered per API key for 24 hours, and the request is compared by method, path and body. For orders, the key is stored with the order the first request created, and the body is compared.
Natural idempotency
The booking transitions are safe even without the header's help:
- Confirming a booking that is already confirmed, with any key, returns the same reservation with
"replay": true. - Rejecting a booking that is already rejected returns it with
"replay": true. - Two confirms of the same booking, or of different bookings for the same slot, are processed one after the other, so a slot cannot be over-booked through the API.
The order create response also carries replay: false on the first call, true when it was replayed. See Errors for every 409.
Stuck? Quote the request_id from the response and write toSansflow support.Checked against API v1 on .