Idempotency & retries
Networks fail. A request can time out after GoOrder created the order, and if you simply send it again you could create a second order and charge the customer twice.
POST /orders therefore requires an Idempotency-Key header.
POST /ordersIdempotency-Key: order-100234How it works
Section titled “How it works”| You send | You get |
|---|---|
| A new key | 201 and a new order |
| The same key, the same body | 200, the original order, and the header Idempotent-Replayed: true |
| The same key, a different body | 409 idempotency_conflict |
| No key | 400 validation_error |
Keys are scoped to your account. An order created in the sandbox does not collide with a live one.
Choosing a key
Section titled “Choosing a key”- Use something that is unique to the order attempt and that you can regenerate after a crash: your own order id is ideal (
order-100234). - 8–64 characters. A random UUID per checkout attempt also works — but then store it before you send the request, so a retry reuses it.
- Do not reuse a key for a different order. Do not generate a new key on each retry — that defeats the point.
What to do after a timeout
Section titled “What to do after a timeout”async function createOrderSafely(payload, key, attempts = 4) { for (let i = 0; i < attempts; i++) { try { const res = await fetch(`${BASE}/orders`, { method: 'POST', headers: { Authorization: `Bearer ${KEY}`, 'Idempotency-Key': key, 'Content-Type': 'application/json' }, body: JSON.stringify(payload), signal: AbortSignal.timeout(15_000), }); if (res.status < 500 && res.status !== 429) return res; // success or a real answer const wait = Number(res.headers.get('Retry-After')) * 1000 || 500 * 2 ** i; await new Promise((r) => setTimeout(r, wait)); } catch { await new Promise((r) => setTimeout(r, 500 * 2 ** i)); // timeout / network error } } throw new Error('Could not confirm the order — check GET /orders?reference=… before retrying');}Retry on network errors, 429 and 5xx with the same key and exponential back-off. Do not retry 4xx business errors (store_closed, out_of_zone, …) — they will not change.
Other endpoints
Section titled “Other endpoints”GET requests are always safe to repeat. Repeating POST /orders/{id}/cancel changes nothing — the second call returns 409 order_not_cancellable because the order is already cancelled. POST /orders/{id}/payment returns a fresh payment link each time.