Error handling
The HTTP status codes, error envelopes, and retry semantics you'll encounter when integrating with the Nora API.
Nora uses conventional HTTP status codes and returns JSON error bodies on every non-2xx response. Branch on status first, then on the appropriate error envelope for actionable details.
Status codes
| Status | Meaning | What to do |
|---|---|---|
200 / 201 | Success | Parse body. |
400 | Bad request — validation failure, malformed body | Fix the payload. On intents endpoints, validationErrors names each failing field. |
401 | Missing or invalid credentials | Refresh your X-API-Key configuration. Do not retry. |
403 | Authenticated but not authorized (e.g., your org's permissions don't cover this operation in this instance) | Do not retry; the key's org/instance is fixed and the condition is resource-side. |
404 | Resource does not exist | Often means wrong env — a sk_test_ key can't see sk_live_ data and vice versa. |
409 | Conflict — state precondition failed (e.g., duplicate cpf on parties, or an idempotency-key reused with a different body on intents) | Read the latest resource state and decide whether to proceed. |
422 | Semantic validation — payload parsed but business rules rejected it | Inspect the error envelope for a machine-readable code / error. |
429 | Rate limited | Respect Retry-After. See Rate limits. |
5xx | Server error | Safe to retry with backoff. Keep the same idempotency-key on intent creates so the retry is deduped. |
Response envelopes
The /v2 API has two envelope shapes, split by resource family.
Parties family (/v2/parties/*)
{
"error": "Human-readable message",
"code": "optional_machine_code"
}erroris the required field. Human-readable; safe to log; prefer your own UX copy for end users.codeis optional — when present, it's a machine-readable hint for branching.
Intents family (/v2/intents/*)
{
"code": "machine_readable",
"message": "Human-readable message",
"validationErrors": [
{ "field": "amountCents", "message": "must be > 0" }
],
"details": { }
}codeandmessageare always present on non-2xx responses.validationErrorsis a structured array of{ field, message }items on400responses driven by structural validation.detailsis optional and endpoint-specific.
Do not assume a unified error shape across /v2. Parse by
endpoint family: if your HTTP client reached a /v2/intents/* URL,
expect code + message; if it reached /v2/parties/*, expect
error (and optionally code).
Branching on the error body
On parties endpoints, inspect response.error for display and
optionally response.code to differentiate failure modes. On intents
endpoints, inspect response.code for branching and
response.validationErrors for per-field messages.
Machine-readable
codevalues are not enumerated in the public OpenAPI spec. Common codes you may observe include validation failures (validation_failedor similar) and idempotency conflicts (idempotency_key_conflictor similar), but the full taxonomy is not part of the public contract. Branch on HTTP status first; usecode/errorfor finer-grained UX only when it surfaces a specific failure you want to special-case.
Retry semantics
Not every error is retry-safe:
- Safe to retry with the same idempotency key (intents creates
only):
5xx, transport failures, connection resets. The server replays the original response. - Safe to retry with a new idempotency key: time-sensitive rebuilds
— burn signing, where a stale-blockhash failure means you fetch a
fresh payload, re-sign, and the new
txSignatureitself becomes the dedup key. - Do not retry, fix first:
400,401,403,422. - Read first, then decide:
404,409. These often mean the state you're acting on has moved — fetching the latest resource tells you what to do next.
Gotchas
401is not a "try again" signal. The credential itself is the problem; no retry will fix it. Check theX-API-Keyvalue and whether it matches the environment you're calling.422vs400.400means the request was malformed (bad JSON, missing required field).422means the request parsed correctly but the business logic rejected it. On intents endpoints, branch oncode; on parties endpoints, oncodeif present orerrortext.- Error bodies are NOT Nora's typed happy-path shape. If you only care about the typed response for 2xx, you still need a fallback path for errors — the envelope is separate and varies by family.
- Don't consume the response body twice. If your HTTP client auto- parses non-ok responses, don't re-read the stream.
See also
- Authentication — fixing
401 - Parties — the parties-family envelope in context
- Idempotency — how retries stay safe
- Rate limits — handling
429
Idempotency
Make intent-creation requests safe to retry. POST /v2/intents/onramp and /v2/intents/offramp accept an idempotency-key header — mint one UUID per user intent and reuse it across retries. Burn approval uses txSignature as the dedup key instead.
Flow walkthroughs
End-to-end /v2 integration arcs.