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 /api/v1/create weighs 50, POST /api/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 /api/v1/order, /api/v1/qr, or /api/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 /api/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 /api/v1/create.
Cause
Your amount is below the per-pair USD minimum (default $30).
Remedy
Quote /api/v1/price first and respect the from.min and to.min fields in the response. /api/v1/price returns these bounds for pre-validation but does not reject out-of-range amounts. Only /api/v1/create enforces the gate.
code: 8 — LIMIT_MAX
Symptom
HTTP 400 on /api/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 /api/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 /api/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 /api/v1/codes, or ask the operator to raise the cap.
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 /api/v1/create. For other endpoints, retry with a fresh nonce.