DocsGuides
Idempotency
Make writes safe to retry with the Idempotency-Key header.
Network calls can fail after the server has already processed them. Idempotency lets you retry a write safely: if the original request succeeded, the retry returns the same result instead of performing the action twice.
When it is required
The Idempotency-Key header is required on these writes:
POST /reservationsPATCH /reservations/{id}POST /orders
A write that needs a key but is sent without one is rejected with 422 validation_error. Reads (GET) are naturally idempotent and ignore the header.
How it works
Send a unique key per logical operation, typically a UUID v4:
curl -X POST https://coastly.surf/api/v1/reservations \
-H "Authorization: Bearer $COASTLY_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3f9a1c7e-2b6d-4a11-9c2e-8d7f4b1a0e55" \
-d '{ "property_id": "prop_1a2b3c", "check_in": "2026-07-12", "check_out": "2026-07-15", "guests": 2, "rooms": [{ "room_type_id": "rt_ocean_king", "units": 1, "guests": 2 }] }'
- First request: the action is performed and the response is stored against the key.
- Retry with the same key and the same body: the stored response is replayed byte for byte, including the original status code. The action does not run again.
- Same key with a different body: the request is rejected with
409 idempotency_conflict. A key is bound to the exact request it first saw.
Choosing keys
- Generate a fresh key for each distinct operation you want to perform once. For example, one key per booking attempt.
- Reuse the same key when retrying that same operation after a timeout or network error.
- Do not reuse a key across genuinely different operations; that either replays the wrong stored response or triggers a conflict.
Recommended retry pattern
import { randomUUID } from "node:crypto";
async function createReservation(body) {
const key = randomUUID(); // one key for this booking attempt
for (let attempt = 0; attempt < 4; attempt++) {
const res = await fetch("https://coastly.surf/api/v1/reservations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.COASTLY_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": key, // same key across retries
},
body: JSON.stringify(body),
});
if (res.status === 429 || res.status >= 500) {
await new Promise((r) => setTimeout(r, 2 ** attempt * 250));
continue; // safe: same key replays, never double-books
}
return res.json();
}
throw new Error("reservation failed after retries");
}
Because retries carry the same key, a booking is created at most once even if the client cannot tell whether the first attempt landed.