nora

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

Loading diagram…

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-burnno separate idempotency-key header. The on-chain txSignature in 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-Key binds 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

  • Error handling — duplicate-key replays return the original response, including the original status code
  • Burn signing — where txSignature replaces idempotency-key
  • Wallets — wallet create and submit response schemas

On this page