Errors cookbook
Per-code real-world remedies: symptom, cause, remedy for every numeric code in the envelope.
Every numeric code maps to a finite set of remedies. Use this when something is failing in integration testing or production. Reference table: Errors.
code: 1 — INVALID_REQUEST
Symptom
HTTP 400. Some endpoints also return HTTP 409 when an Idempotency-Key is reused with a different payload.
Common causes
- The asset code (
fromCcy/toCcy) is misspelled or disabled. directionis not one of"from"/"to".amountparses to zero or a negative number.toAddressdoes not validate against the destination asset’s network.type = "float"withoutrefundAddress.Idempotency-Keyshorter than 8 chars or longer than 128.Idempotency-Keyreused with a different body (HTTP 409).
Remedy
Inspect the request body. There are no hidden hints; the wire body is exactly { "code": 1, "msg": "INVALID_REQUEST" }. If your logs show one cause repeatedly, add a pre-flight validation step before the POST.
code: 2 — AUTH_REQUIRED
Symptom
HTTP 401 on an endpoint that requires authentication.
Cause
You forgot to set X-API-KEY or X-API-SIGN.
Remedy
Check that your HTTP client sends all three headers: X-API-KEY, X-API-SIGN, X-API-NONCE. The third is the one most integrations forget. A missing X-API-NONCE collapses to code: 3 AUTH_INVALID, not code: 2. Those are different failure modes.
code: 3 — AUTH_INVALID
Symptom
HTTP 401, possibly on a request that looks fine at first glance.
Common causes (uniform on the wire, indistinguishable)
- Unknown
apiKey. - Bad HMAC: signature computed over a different byte stream than the body you sent (re-serialised, sorted keys, whitespace difference, or
echoappending a trailing newline). apiSecretwrong, typo, or from a different partner.X-API-NONCEempty, shorter than 16, longer than 64, or already used in the last 5 minutes.
Remedy
- Print
apiKeyand the body bytes you signed; print the body bytes the HTTP client actually sent (after middleware). They must match byte-for-byte. The most common bug is middleware that pretty-prints JSON after the signature step. - Use
printf '%s' "$BODY"and--data "$BODY", neverechoand--data-raw.echoappends a newline. - Generate a fresh nonce on every request. Never reuse across retries.
code: 4 — AUTH_DISABLED
Symptom
HTTP 401, but the signature was valid (you can prove this because the same nonce + signature on a different second still returns AUTH_DISABLED, not AUTH_INVALID).
Cause
The operator has flipped enabled = false on your partner record. Reasons: planned maintenance, credential revocation, or a billing pause.
Remedy
Contact your operator. There is no API path to recover.
code: 5 — RATE_LIMIT
Symptom
HTTP 429 with a Retry-After: <seconds> header.
Cause
Your per-partner weight budget (default 2500 wu/min) is exhausted within the rolling 60-second window. POST /v1/create weighs 50, POST /v1/qr weighs 5, every other endpoint weighs 1.
Remedy
- Sleep for
Retry-Afterseconds (no less) before the next request. - For burst traffic, queue server-side and drain at ≤ budget/60 calls per second.
- For sustained growth, send projected per-endpoint call rates to your operator and request a budget raise.
code: 6 — NOT_FOUND
Symptom
HTTP 404 on /v1/order, /v1/qr, or /v1/emergency.
Causes (collapsed into one envelope, no oracle)
- The
iddoes not exist. - The
idbelongs to another partner. - The
tokendoes not match the stored hash. - The order was soft-deleted by the user.
Remedy
Re-verify that id and token come from a successful /v1/create on this same partner record. If they do, the order was soft-deleted by the end user. Your aggregate counters remain accurate, but the underlying row is no longer queryable.
code: 7 — LIMIT_MIN
Symptom
HTTP 400 on /v1/create.
Cause
Your amount is below the per-pair USD minimum (default $1).
Remedy
Quote /v1/price first and respect the from.min and to.min fields in the response. /v1/price returns these bounds for pre-validation but does not reject out-of-range amounts. Only /v1/create enforces the gate.
code: 8 — LIMIT_MAX
Symptom
HTTP 400 on /v1/create.
Cause
Either the per-pair USD maximum is exceeded (default $5,000), or the daily payout cap on the operator side is exhausted.
Remedy
Lower the amount, split into multiple orders, or contact the operator for a higher per-order cap (operator-mediated).
code: 9 — RESERVE_EXCEEDED
Reserved for a future per-partner capacity control. No current route emits this code. Safe to omit from exhaustive switch statements until announced.
code: 10 — MAINTENANCE
Symptom
HTTP 503.
Cause
The site is in operator-toggled upgrade mode (rare).
Remedy
Retry with exponential backoff capped at 60 seconds. The window is usually short (< 5 min).
code: 11 — UPSTREAM_ERROR
Symptom
HTTP 503.
Cause
A required upstream is temporarily unavailable.
Remedy
Retry with exponential backoff: 1 s, 2 s, 5 s, 10 s, then bail and surface a partner-facing error.
code: 12 — EMERGENCY_INVALID_CHOICE
Symptom
HTTP 400 on /v1/emergency.
Cause
The choice field is not exactly "EXCHANGE" (case-sensitive).
Remedy
Send { "choice": "EXCHANGE" }. The "REFUND" choice is not exposed on the public surface — refunds are operator-mediated.
code: 13 — LIMIT_EXCEEDED
Symptom
HTTP 403 on POST /v1/codes.
Cause
Your partner record has already reached the per-partner cap on reference codes (default 50).
Remedy
Delete (soft-disable) unused codes via DELETE /v1/codes, or ask the operator to raise the cap.
code: 14 — ROTATION_CONFLICT
Symptom
HTTP 409 on /v1/keys/rotate.
Cause
Two concurrent rotation calls from different processes; the other one won. Your call did not rotate anything.
Remedy
The winning call already returned new secrets to someone. If your process was the loser and never saw the new values, request a fresh rotation through the operator (the only recovery path). Do not retry the losing rotation; the secret it would compete for has already changed.
code: 99 — INTERNAL
Symptom
HTTP 500.
Cause
Unexpected server-side condition. Already logged on our side; if it recurs, share the timestamp and the request body shape with your operator contact.
Remedy
Safe to retry once with the same Idempotency-Key for /v1/create. For other endpoints, retry with a fresh nonce.