Skip to main content

The five most common error envelopes — worked end-to-end

The Error Code Reference lists every code the platform can return — 732 at last count. This guide picks the five you will actually hit during integration and shows you the full response body, not just the code. Each section is a complete request-response pair: a cURL call, the exact error envelope you get back, and how to handle it in Node.js and Python. For the code registry and how to classify errors as retriable or terminal, see Error Codes and the Error-handling runbook.

1. 422 — validation failure (VALIDATION_ERROR)

A 422 means the request body failed schema validation before any business logic ran. The envelope carries details.issues, an array of per-field objects naming the field, the error code, and a human-readable message.

Request

Response — 422

The issues array is the actionable part. Each entry names the failing field, a machine-readable code you can branch on, and a message you can show to an operator. Walk the array and map each entry back to your form rather than showing the raw JSON:
Fix the fields named in issues, then retry with the same Idempotency-Key — the replay cache stores the corrected payload and returns the successful result. A 422 never dispatched anything, so reusing the key is safe.

2. 409 — idempotency conflict (CONFLICT)

When you send a second request with the same Idempotency-Key while the first is still running, the platform returns 409 CONFLICT. The first request owns the lock; the second must wait for it to finish and then replay the cached result.

Request

Response — 409 (in-flight lock)

The response carries a Retry-After: 2 header. Wait that interval, then retry with the same key and body. When the first request completes, the retry receives the cached result — the response body you would have gotten from the original — and the header Idempotency-Replay: true confirms it was a replay, not a fresh execution.
A 409 is always terminal for the current attempt — you fix nothing, just wait. If the body differs from the original, the code is IDEMPOTENCY_KEY_REUSED (also 409) and the fix is to correct the body or use a new key.

3. 429 — rate limited (RATE_LIMITED)

The global request limiter returns 429 with a body that tells you exactly how long to wait. The Retry-After header mirrors error.retry_after in the body — read whichever your HTTP client surfaces.

Request

Response — 429

Response headers:
The retry_after value (seconds) reflects the actual bucket refill time. Honor it before any exponential fallback — a blind doubling may retry too soon and hit 429 again.
Use the same Idempotency-Key on the retry — a 429 is always a pre-dispatch rejection, so reusing the key cannot double-send. The response also carries draft-ietf-httpapi-ratelimit-headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-RateLimit-Bucket and their no-prefix mirrors). Read them to track how close you are to the limit before you get a 429.

4. 401 — wrong API key class (UNAUTHORIZED)

Orbit API keys come in two classes: secret keys (dv_live_sk_…) for server-side calls and publishable keys (dv_live_pk_…) for client-side operations that do not move money. Using a publishable key where a secret key is required returns 401.

Request — publishable key on a send endpoint

Response — 401

This is a key-class mismatch: the request authenticates via X-API-Key (or Authorization: Bearer) but the key prefix does not match the endpoint’s required class. The fix is to use the right key:

5. 403 — missing scope (INSUFFICIENT_PERMISSIONS)

When the API key authenticates but lacks the scope required for the endpoint, the platform returns 403 INSUFFICIENT_PERMISSIONS.

Request

Response — 403

This is a scope problem, not an auth problem. The key is valid, but it was provisioned without the scope this endpoint needs. Check which scopes your key carries:
Then either create a new key with the required scope in the dashboard (Developers → API keys), or have an admin add the scope to the existing key. Retry with the updated key.
A 403 is terminal for the current key — do not retry without first fixing the scope.

Distinguishing 401 from 403

Check GET /me with the same key to confirm which case you are in: a 200 with the key’s scopes means 403; a 401 on /me means the key itself is the problem.

Where to go next