FX

Exchange between your own accounts

POST/fx/exchanges

Two forms, exactly one per request:

  • quoteId — executes the quote at its locked rate; every term comes from the quote, so send nothing else. An expired, already used or unknown quote is 400 fx_quote_expired.
  • sellAmount + buyCurrency — exchanges at the rate of the moment, priced and executed in the same request (as the legacy POST /v1/exchange did). The rate you got is in the response. Accounts resolve as on POST /fx/quotes, with the same codes.

Always 202. A fiat exchange completes inside the request and normally reads completed; a crypto one settles on a venue and reads processing until it does — poll Location. A refusal at execution (insufficient_funds, limit_exceeded) is a 400 with its own code; the exchange stays failed and keeps its merchantReference, so a new attempt needs a new reference as well as a new key. There is no approval step for API exchanges. Requires fx:write.

A 500 is never a refusal. It means the outcome is not known yet — retry with the same Idempotency-Key and you get the exchange as it stands, never a second one. An Idempotency-Key already used for something other than this API key's exchange is 400 idempotency_key_reused.

Authentication
ApiKeyAuth
Execution
asynchronous

Parameters

  • Idempotency-Keystringheaderrequired
    Client-generated unique key for this logical request (UUID recommended). See Idempotency.

    min length 1 · max length 255

  • X-Request-Idstringheader
    Optional client correlation id; echoed back unchanged.

    max length 128

Request body application/jsonrequired

Either quoteId, alone — or sellAmount and buyCurrency (with optional accounts) to exchange at the current rate.
  • merchantReferencestringrequired
    Your own unique reference for this exchange; reusing it under a new Idempotency-Key is 400 duplicate_merchant_reference.

    min length 1 · max length 100

  • quoteIdstring

    max length 64

  • sellAmountMoney
    Decimal amount as a string — never a float, never minor units. An amount you send may carry no more decimal places than its currency has (and never more than 8); trailing zeros do not count, so 10.100 EUR is fine and 10.123 EUR is 400 validation_failed on amount.value.
    • valuestringrequired

      Example: 149.90

      pattern ^-?\d{1,18}(\.\d{1,18})?$

    • currencystringrequired
      ISO 4217 code for fiat (EUR), asset ticker for crypto (USDT).

      Example: EUR

      pattern ^[A-Z0-9]{3,10}$

  • buyCurrencystring
    ISO 4217 code for fiat (EUR), asset ticker for crypto (USDT).

    Example: EUR

    pattern ^[A-Z0-9]{3,10}$

  • sellAccountIdstring
    Opaque account identifier. Treat as a string; never parse or construct it.

    Example: acc_AAAAAAAAAAc

    pattern ^acc_[A-Za-z0-9_-]{11}$

  • buyAccountIdstring
    Opaque account identifier. Treat as a string; never parse or construct it.

    Example: acc_AAAAAAAAAAc

    pattern ^acc_[A-Za-z0-9_-]{11}$

Responses

Exchange accepted. The code is always 202 — read status: completed, failed or cancelled is final; pending / processing means it is still settling (crypto pairs), so poll Location — Retry-After is sent only then. An idempotent replay returns this original body.

Headers

  • Locationstring (uri-reference)
    The resource URL to poll for the current state (RFC 9110 §15.3.3 status monitor).
  • Retry-Afterinteger
    Seconds to wait before polling Location. On a 202 it is present only while the body's status is not yet terminal — its absence is a second signal that the work is already done.
  • X-Request-Idstring
    The client's X-Request-Id if supplied, otherwise a server-generated id. Equals traceId in problems.
  • Idempotent-Replayedboolean
    true when this response was replayed from an earlier request with the same Idempotency-Key.
  • RateLimit-Limitinteger
  • RateLimit-Remaininginteger
  • RateLimit-Resetinteger
    Seconds until the window resets.

Schema application/json

  • objectstringrequired

    Allowed values: fx_exchange

  • idstringrequired
    An exchange's id. Opaque — use it as you received it.

    Example: fx_019985c24a6e7c3b9f102d5e8a1b3c4d

    pattern ^fx_[0-9a-f]{32}$

  • merchantReferencestring
    Absent on an exchange made in the portal.
  • statusstringrequired

    Allowed values: pendingprocessingcompletedfailedcancelled

  • sellMoneyrequired
    Decimal amount as a string — never a float, never minor units. An amount you send may carry no more decimal places than its currency has (and never more than 8); trailing zeros do not count, so 10.100 EUR is fine and 10.123 EUR is 400 validation_failed on amount.value.
    • valuestringrequired

      Example: 149.90

      pattern ^-?\d{1,18}(\.\d{1,18})?$

    • currencystringrequired
      ISO 4217 code for fiat (EUR), asset ticker for crypto (USDT).

      Example: EUR

      pattern ^[A-Z0-9]{3,10}$

  • buyobject
    The quoted amount until a venue exchange settles; then what it filled.
    • valuestringrequired

      Example: 149.90

      pattern ^-?\d{1,18}(\.\d{1,18})?$

    • currencystringrequired
      ISO 4217 code for fiat (EUR), asset ticker for crypto (USDT).

      Example: EUR

      pattern ^[A-Z0-9]{3,10}$

  • ratestring
    The rate applied, as a string.
  • feeobject
    Absent while it is not known — as on a portal exchange that has not executed yet, and on some older portal exchanges.
    • valuestringrequired

      Example: 149.90

      pattern ^-?\d{1,18}(\.\d{1,18})?$

    • currencystringrequired
      ISO 4217 code for fiat (EUR), asset ticker for crypto (USDT).

      Example: EUR

      pattern ^[A-Z0-9]{3,10}$

  • totalDebitedobject
    Present with fee.
    • valuestringrequired

      Example: 149.90

      pattern ^-?\d{1,18}(\.\d{1,18})?$

    • currencystringrequired
      ISO 4217 code for fiat (EUR), asset ticker for crypto (USDT).

      Example: EUR

      pattern ^[A-Z0-9]{3,10}$

  • sellAccountIdstringrequired
    Opaque account identifier. Treat as a string; never parse or construct it.

    Example: acc_AAAAAAAAAAc

    pattern ^acc_[A-Za-z0-9_-]{11}$

  • buyAccountIdstring
    Opaque account identifier. Treat as a string; never parse or construct it.

    Example: acc_AAAAAAAAAAc

    pattern ^acc_[A-Za-z0-9_-]{11}$

  • createdAtstring (date-time)required
    RFC 3339, UTC, with millisecond precision.

    Example: 2026-09-18T10:15:30.123Z

  • updatedAtstring (date-time)required
    RFC 3339, UTC, with millisecond precision.

    Example: 2026-09-18T10:15:30.123Z

  • completedAtstring (date-time)
    RFC 3339, UTC, with millisecond precision.

    Example: 2026-09-18T10:15:30.123Z