Idempotency
Keel documents idempotency separately for the canonical permit flow and the execution routes.
At a glance
| Route family | Where idempotency goes |
|---|---|
POST /v1/permits | JSON body idempotency_key |
POST /v1/permits/{permit_id}/usage | JSON body usage_idempotency_key |
POST /v1/executions | Idempotency-Key header |
POST /v1/execute | Idempotency-Key header |
POST /v1/proxy/* | Idempotency-Key header |
Permit idempotency
Route:
POST /v1/permits
Canonical field:
idempotency_keyin the JSON body
Behavior:
- scope is per project
- same key plus the same semantic payload replays the prior permit response
- same key plus a different semantic payload returns
409 conflict - if the client omits
idempotency_key, Keel generates a server-side idempotency key for that permit - replay is still route-dependent: stale
allowpermits with inactive reservations can expire or revalidate instead of replaying the originalallowbody
Permit usage closeout idempotency
Route:
POST /v1/permits/{permit_id}/usage
Canonical field:
usage_idempotency_keyin the JSON body
Behavior:
- use the same key when retrying the same permit closeout
- same key plus the same closeout values replays the existing closeout response
- same key plus different closeout values returns
409 conflict - this is a narrower permit-first closeout contract, not proxy-strength execution replay binding
Execution idempotency
Routes:
POST /v1/executionsPOST /v1/executePOST /v1/proxy/openaiPOST /v1/proxy/anthropicPOST /v1/proxy/googlePOST /v1/proxy/xaiPOST /v1/proxy/meta
Canonical field:
Idempotency-KeyHTTP header
Behavior:
- reuse the same header value when retrying the same execution request
- if the header is omitted, the request still runs, but retries are treated as new executions
- proxy routes document the strongest replay contract: same key plus the same payload replays the cached response, and same key plus a different payload returns
409 conflict - execution routes use the same header-based retry pattern, but you should still treat the header as part of the public route contract rather than assume identical semantics across every route family
/v1/executionsand/v1/executedo not currently document the same proxy-strength request-hash replay binding as the proxy routes
Why the fields differ
Permit creation uses a body field. Execution routes use a header. Use the documented field for each route family.
Related pages
Last updated on Edit this page on GitHub