Idempotency
Network errors are inevitable. To retry a POST without risking a
duplicate record, send an Idempotency-Key header with a unique value
(a UUID v4 is ideal):
If the original request reached the server, the retry returns the cached response from the first call instead of creating a second invoice.
Semantics
- Cache window: 24 hours per key and principal (the API key, or the signed-in user for JWT sessions).
- Same request requirement: A retry must match the original request:
method, path, query, and body. Reusing an existing key with a different request
returns
422 idempotency_key_conflict. - In progress: If a request with the same key is still executing, the retry
returns
409 idempotency_in_progress— wait briefly and retry. - Key length: Keys longer than 255 characters return
400 invalid_idempotency_key. - Replay headers: A replayed response is returned byte-for-byte with
Idempotent-Replayed: trueandX-Original-Request-Id(the initial call’s request ID for log correlation). - Methods: Every
POSTendpoint honors the header. Browser CORS preflights (OPTIONS) allowIdempotency-Key.GET,PATCH, andDELETEignore the header (those methods are naturally idempotent under HTTP semantics).
Response caching and retries
A response is cached once the action itself starts executing, whether the result
is a 2xx success, a domain refusal, or a 5xx server error (since an action
that failed mid-flight may have already committed partial work).
Requests rejected before the action executes — such as authentication failures, rate limits, or model validation errors — are not stored. Once the underlying issue is resolved, the same key can be resent and will execute normally.
After any error, retry with the same key:
- If the retry returns
Idempotent-Replayed: true, the original request ran and this is its cached outcome. Fix the cause and retry with a new key. - If
Idempotent-Replayedis absent, the retry executed afresh.
Status codes follow the IETF Idempotency-Key HTTP header specification.
