Skip to content

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 /orders
Idempotency-Key: order-100234
You sendYou get
A new key201 and a new order
The same key, the same body200, the original order, and the header Idempotent-Replayed: true
The same key, a different body409 idempotency_conflict
No key400 validation_error

Keys are scoped to your account. An order created in the sandbox does not collide with a live one.

  • 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.
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.

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.