A request that timed out may still have succeeded. To retry a POST without
creating a second page, post or message, send an Idempotency-Key header: any
string of 1 to 255 printable characters, one per logical operation (a UUID is
the easy choice).
Idempotency-Key: 5b8f6e2c-1d4a-4f7b-9c3e-8a2d6f0b4e17
- The same key and the same request within 24 hours gets the first answer
again, word for word, with
Idempotent-Replay: true. Nothing is done twice. - The same key with a different request is
409 IDEMPOTENCY_KEY_CONFLICT. - The same key while the first request is still running is
409 IDEMPOTENCY_KEY_IN_FLIGHT. Wait a moment and retry. - Only a success is kept. After a
4xx, fix the request and retry with the same key; the corrected request runs.
Two requests, one page
A create that can be retried — answers 201
Any string of 1 to 255 printable characters. A UUID is the easy choice, one per logical operation.
curl -X POST https://api.boltclaw.ai/v1/pages \
-H "Authorization: Bearer bc_live_EXAMPLExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5b8f6e2c-1d4a-4f7b-9c3e-8a2d6f0b4e17" \
-d '{
"slug": "acme-staging",
"title": "Acme, staging"
}'
const res = await fetch("https://api.boltclaw.ai/v1/pages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BOLTCLAW_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
"slug": "acme-staging",
"title": "Acme, staging"
}),
});
// 201
const data = await res.json();
The same request again, with the same key — answers 201
The first answer, word for word, with Idempotent-Replay: true. No second page was created, and the slug is not reported as taken.
curl -X POST https://api.boltclaw.ai/v1/pages \
-H "Authorization: Bearer bc_live_EXAMPLExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5b8f6e2c-1d4a-4f7b-9c3e-8a2d6f0b4e17" \
-d '{
"slug": "acme-staging",
"title": "Acme, staging"
}'
const res = await fetch("https://api.boltclaw.ai/v1/pages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BOLTCLAW_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
"slug": "acme-staging",
"title": "Acme, staging"
}),
});
// 201
const data = await res.json();
Which operations take it
Every POST accepts the header. Four refuse the request without it:
POST /v1/keys, POST /v1/keys/{id}/rotate, POST /v1/webhooks and
POST /v1/webhooks/{id}/rotate-secret. All four mint a credential, and a credential is the one thing where a retried
network timeout cannot be told apart from a request that never landed: without
a key the only safe move is to issue a second one, and then nobody knows which
of the two their system is holding.
Keys are scoped to your workspace and to the operation, so reusing one
elsewhere is harmless. A replay of POST /v1/media answers "upload": null:
the upload URL is a credential too, it is never kept, so ask for a new upload
if you still need one.
Request ids
Every response, success or error, also carries X-Request-Id, the same
value as an error's request_id. Send your own (a UUID) and it is used
instead, so your logs and ours share one id.