Network calls fail in the middle. Idempotency keys let you retry a POST without worrying about creating the same resource twice.
How it works
Send Idempotency-Key: <opaque-string> on any POST request. Awardee caches the response keyed by (api_key, method, path, idempotency_key) for 24 hours.
- Same key, same body within the window → the cached response is replayed verbatim. The response carries
Idempotent-Replayed: true.
- Same key, different body →
409 idempotency_conflict. This catches client bugs that reuse a key for unrelated requests.
- New key → the request runs normally and the result is cached.
GET, PATCH, PUT, and DELETE are naturally idempotent on the server side, so the header is ignored on those methods. Only POST honors it.
When to use it
- Always, for any
POST you might retry — creating chatbots, articles, conversations messages, submissions, etc. The cost of including the header is one UUID per request.
- Especially for
POSTs triggered by user actions (form submits, button clicks), where a network blip otherwise creates duplicates.
- Especially inside webhook handlers and queue workers, where retries are normal.
Choosing a key
Use a fresh UUIDv4 per logical operation. Generate it on the client and reuse it across retries of the same request.
KEY=$(uuidgen)
curl -X POST https://api.awardee.dev/v1/chatbots \
-H "Authorization: Bearer aw_live_4f8a3c7e2d1b9a5c6f8e3d2c1b4a9f7e" \
-H "Idempotency-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Support bot" }'
Persist the idempotency key alongside the work it represents (in your job queue row, your outbox table, the user’s form-submission record). On retry, look it up and reuse it. Generating a fresh UUID per retry defeats the purpose.
Detecting a replay
A replayed response is byte-identical to the original, including the original status code. The only differences are response headers:
HTTP/1.1 201 Created
Idempotent-Replayed: true
X-Request-Id: req_<new-id>
The X-Request-Id reflects the replay request, not the original. Log both — the cached request_id inside the JSON body (if your original response had one) ties back to the first attempt.
Conflicts
Reusing a key with a different body within 24 hours returns:
{
"error": "idempotency_conflict",
"message": "Idempotency-Key was reused with a different request body.",
"request_id": "req_8fK2x9aLp0qR"
}
This is almost always a client bug. Either generate a new key, or use the same body.
Window expiration
Cached responses live for 24 hours, sliding from the original request’s completion time. After expiration the key is fresh — sending it again with the same body issues a new request and creates a new cache entry.