Skip to content

Idempotency

The draft journal POST requires Idempotency-Key. Generate a random key for each new intended operation. Keep the same key and identical payload when retrying an uncertain result. The header accepts 16–128 characters from [A-Za-z0-9._~-]; missing or invalid keys are rejected with 400.

The namespace is API major + verified tenant + path company + OAuth client + user + operation ID. The proposed core must atomically commit the command, audit, canonical digest and result reference. A rolled-back command leaves no successful receipt.

Attempt Draft response
First successful request 201, resource ID/version, Location, ETag, Idempotency-Replayed: false
Same key and canonical payload Original 201 body, Location and ETag after current authorization; Idempotency-Replayed: true
Same key, different payload 409 IDEMPOTENCY_CONFLICT; do not retry automatically
Same operation still running 409 IDEMPOTENCY_IN_PROGRESS; wait for bounded Retry-After
Replay response expired 409 IDEMPOTENCY_EXPIRED; reconcile, never silently create again

Response replay lasts at least seven days under the proposal. A dedupe tombstone remains after response expiry. Do not switch to a new key to get around a conflict or expiry. Network delivery is not exactly once.

Canonicalization sorts object keys, preserves array order and string content, and uses UTF-8 JSON without insignificant whitespace. It does not normalize Unicode, round decimals or inject defaults. Duplicate JSON keys and unknown fields are rejected.

Revoked access cannot retrieve an old response. Revocation and write authorization must serialize at the core so a losing stale authorization cannot commit. Each HTTP attempt gets a new server request ID, including replay.

For a timeout or uncertain 500, preserve the original key and reconcile the outcome; only a bounded retry of the same payload/key can be considered. Do not blindly replay a new operation. These guarantees still need real provider race, rollback and retention proof.