Idempotency
Retry intent creation and wallet registration safely. Intent creation accepts an idempotency-key header. Wallet create and submit require it. Burn approval uses txSignature instead.
Network failures, timeouts, and user double-clicks are facts of life.
Nora uses the idempotency-key header to make selected write operations
safe to retry. Generate one UUID for a logical operation. Reuse that UUID
and the same request body for every retry of that operation.
How it works
Nora caches the response body against the (idempotency-key, endpoint)
pair within a dedup window. A second call with the same key does not
re-execute the operation — it replays the stored response.
Which operations carry a key
The /v2 API uses the idempotency-key header on these endpoints:
POST /v2/intents/onramp— mint a fresh UUID per user intent; reuse the same UUID when retrying the same click.POST /v2/intents/offramp— same rule as onramp.POST /v2/wallets— the header is required. Reuse the same UUID and body when retrying one wallet registration.POST /v2/wallets/:id/submit— the header is required. Reuse the same UUID and signed transaction when retrying one submission.
The header is format-validated as a UUID.
POST /v2/intents/:id/approve-burn— no separateidempotency-keyheader. The on-chaintxSignaturein the body IS the deduplication key. Resubmitting the same signature is a no-op; a different signature against the same intent is rejected.
GET endpoints are already idempotent and don't need the header.
Rule of thumb: one key per logical write
- Generate a fresh UUID before the first request.
- Persist the UUID with the request body.
- Reuse both values after a timeout, connection reset, or application restart.
- Generate a new UUID when the body changes or the user starts a new operation.
Burn-approval nuance
POST /v2/intents/:id/approve-burn — the txSignature you submit
IS the dedup key. The backend rejects a second call with a different
txSignature against an intent that already has one. A retry means
resubmitting the same signed bytes, not re-signing. If the blockhash
has expired, you rebuild from a fresh payload (which produces a new
txSignature) and that new signature is what the backend keys on.
Wallet-submission nuance
If POST /v2/wallets/:id/submit times out, retry the same signed transaction
with the same idempotency-key. Do not sign a second transaction while the
first preparation remains valid.
If the preparation expires, call POST /v2/wallets/:id/prepare. Sign the
new transaction and submit it with a new idempotency-key. Reusing the old
key with the new signed bytes returns 409 with details.code of
IDEMPOTENCY_KEY_MISMATCH.
Wallet create and submit responses include replayed. It is true when the
server returns the stored result for an idempotent retry.
Gotchas
- Keys are scoped per endpoint. The same UUID sent to two different paths is two separate cache entries — you don't need to globally coordinate keys.
- Keys are scoped per API key (and therefore per instance). Because
X-API-Keybinds to an (org, instance) tuple, the same UUID reused against sandbox and production is two separate operations on two separate servers' cache entries. - Don't generate the key at module load. If your process is long-
lived, a module-level
randomUUID()would make every user intent share one key — the opposite of what you want. Generate inside the request handler, per user action. - TTL is finite. The cache entry expires after a server-side window. A retry that arrives that late will execute as a fresh operation.
See also
- Authentication — how
X-API-Keyscopes your idempotency-key cache
- Error handling — duplicate-key replays return the original response, including the original status code
- Burn signing — where
txSignaturereplacesidempotency-key - Wallets — wallet create and submit response schemas