The header
Every write requires Idempotency-Key — a stable value of your choosing (8–128 characters) per logical request. Without it the write is refused with invalid_request before anything happens. Choose keys that name the intent (order-8841-invoice), not the attempt.
A write, keyed
From the published contract
curl -X POST "https://api.rivet.network/v1/documents" \
-H "Authorization: Bearer rk_live_…" \
-H "Idempotency-Key: order-8841-invoice" \
-H "Content-Type: application/json" \
-d '{"connection_id":"6a5eed00000000000000c001","document_type":"invoice","line_items":[{"description":"Anvil, 40 lb","quantity":2,"unit_price":95,"tax_rate":0.08}],"external_refs":{"issuer_ref":"ACME-1042"}}'Replays
Retrying with the same key and the same body returns the original response — byte for byte, same status — with one extra header so you can tell:
The retry's answer
From the published contract
HTTP/1.1 201 Created
Idempotent-Replay: true
{ …the byte-identical original response… }- Keys are held for 24 hours; after that the same key runs fresh.
- The body comparison is structural: the same JSON with keys in a different order is the same request — a retry serialized by a different client library never reads as a conflict.
- Only successes replay. A refusal (a 4xx) releases the key, so you fix the request and retry with the same key.
Conflicts
The same key with a different body is refused with 409 idempotency_conflict — the original response stays replayable under the original body. While the original request is still running, a concurrent retry answers 409 conflict: wait a moment and retry.
The rule of thumb: one Idempotency-Key per thing you mean to happen once. New intent, new key. See Errors for both codes in the envelope.