Errors, idempotency & rate limits
Errors
Every error shares one shape:error.code (a stable slug), not the human-readable message.
Authentication
Requests
upstream_error
FX accept and submit, and cross-currency transfers, reuse the same internal trade path as the BlankFX app. When that path refuses with a known reason, you get that reason’s code: source_wallet_forbidden, kyc_verification_required or wallet_suspended (all 403). Any other refusal comes back as:
Idempotency
These calls require anIdempotency-Key header (any unique string, for example a UUID):
Without the header they return
400 idempotency_key_required. Other routes ignore the header.
- Replaying the same key on the same route within 24h returns the original response, with
Idempotency-Replayed: true. The operation does not run twice. - If the first request is still running, the replay returns
409 idempotency_in_progresswithRetry-After: 1. 5xxresponses are not stored, so retrying after a5xxruns the operation again.POST /webhooksreturns the signing secret only on the first response. A replay returns the same body withoutsecret.
Rate limits
Limits are per key, counted in fixed one-minute windows.
Over the limit returns
429 with a Retry-After header (seconds until the window resets) and the body { "error": { "code": "rate_limited", "message": "Too many requests. Try again later." } }. Wait for Retry-After, then re-sign and retry.