Skip to main content

Errors, idempotency & rate limits

Errors

Every error shares one shape:
Match on 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:
The HTTP status is passed through from the trade path, so read the status first:

Idempotency

These calls require an Idempotency-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_progress with Retry-After: 1.
  • 5xx responses are not stored, so retrying after a 5xx runs the operation again.
  • POST /webhooks returns the signing secret only on the first response. A replay returns the same body without secret.
Pair idempotency keys with fresh request signatures: the signature must always be fresh (replay-protected), but the Idempotency-Key is what makes the operation safe to retry.

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.

List limits

List endpoints return the newest records and have no pagination: