Know when to reuse an idempotency key and when to generate a new one; the rules serve different purposes.
Idempotency
Write endpoints require x-idempotency-key (UUID v4). Retries are inevitable, so this is part of the contract, not an optional optimization.
Four Semantics
| Situation | Behavior | HTTP |
|---|---|---|
| Same key + same request body | Return the original result unchanged, including errors | Original status code |
| Same key + different request body | Reject | 409 idempotency_key_reused |
| Original request still processing | Reject; retry later | 409 idempotency_in_progress |
| Key is not a valid UUID | Reject | 400 idempotency_key_invalid |
| Required header absent from a write request | Reject | 400 idempotency_key_required |
| More than 24 hours elapsed | Treat as a new request | Process normally |
Same key + same body returns the first result, even when that result is a failure. Idempotency means the same key returns the same result, not that the same key always succeeds.
504 and Business Failure Require Opposite Handling
Put these two rules in your retry logic rather than leaving them to an on-call decision:
- 504 / network timeout → retry with the same key. We may already have completed the operation; a new key starts a second real business operation.
- Definite business failure (4xx with code) → correct the issue and retry with a new key. The same key returns the original failure unchanged, making corrections appear ineffective.
Quotes and Payouts Follow Opposite Rules
| Action | Idempotency key | Reason |
|---|---|---|
| Payment / payout retry | Reuse the same key | Retrieve the original operation without creating a second payment |
| Quote | Use a new key each time | Reusing a key returns the first result for 24 hours, whereas a quote lasts only 75 seconds |
Reversing these rules causes either a second real payment or an attempt to order with an expired quote.
Two Namespaces on Our Side
Your submitted key exists only in the OpenAPI idempotency table, whose primary key includes the merchant. The same string used by another merchant therefore does not interfere with yours.
Financial journal idempotency keys, for the ledger and dispatch, are generated entirely by our server. None of their characters comes from your input.
The Only Exception: Resubmission After Step-up Authentication
An action requiring step-up authentication first returns 400 step_up_required + hosted_url. After the end user completes verification on our hosted page, resend the same request with the same idempotency key, adding x-step-up: <challenge_id>.
This is the only case where a same-key retry should actually execute once. Every other identical-key, identical-body retry returns only the first result.
⚠ We never accept self-asserted body fields such as step_up_passed: true. A step-up authentication result can only come from a challenge issued by our hosted page.