What is a conversion?

A conversion debits one currency from a customer’s account and credits another currency to a destination account at a specified exchange rate. It is a balance-to-balance operation within the platform — no external payment is involved. For example, converting USD to EUR debits the customer’s USD account and credits their EUR account. The customer sees “I converted 10,000 USD to 9,250 EUR.” No internal settlement or liquidity mechanisms are exposed.
Conversions are for standalone balance operations where a customer wants to change the currency they hold. For cross-currency payments to external counterparties, use POST /payments with executionPreference: use_quote or at_market instead — the payment endpoint handles FX and delivery in a single call.

Creating a conversion

Use POST /fx/conversions to execute a conversion. There are two execution modes:

Option 1: Quote-based conversion

Provide a quoteId from a previously created FX quote. The conversion executes at the locked rate. This is the recommended approach when rate certainty matters.
curl -X POST https://sandbox.api.openfx.com/v1/fx/conversions \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "quoteId": "qte_01953e1a5f4b7006",
    "sellCurrency": "USD",
    "buyCurrency": "GBP",
    "sellAmount": "10000.00",
    "sourceAccountId": "acc_01953e1a5f4b7002",
    "destinationAccountId": "acc_01953e1a5f4b7003"
  }'

Option 2: At-market conversion

Omit quoteId and provide the asset pair and amount directly. The conversion executes at the current market rate. Use this when speed matters more than rate certainty.
curl -X POST https://sandbox.api.openfx.com/v1/fx/conversions \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "sellCurrency": "EUR",
    "buyCurrency": "USD",
    "sellAmount": "5000.00",
    "sourceAccountId": "acc_01953e1a5f4b7010",
    "destinationAccountId": "acc_01953e1a5f4b7002"
  }'
At-market conversions execute at the prevailing rate, which may differ from the last indicative rate you displayed. For large amounts or volatile pairs, use a quote-based conversion to guarantee the rate.

Request fields

FieldTypeRequiredDescription
quoteIdstringNoFX quote ID (qte_ prefix) for quote-based execution. Omit for at-market.
sellCurrencyAssetCodeYesThe currency to sell
buyCurrencyAssetCodeYesThe currency to buy
sellAmountstringOne of sellAmount or buyAmountAmount to sell (provide this OR buyAmount)
buyAmountstringOne of sellAmount or buyAmountAmount to buy (provide this OR sellAmount)
sourceAccountIdstringYesAccount to debit (acc_ prefix). Must hold the sell currency.
destinationAccountIdstringYesAccount to credit (acc_ prefix). Must accept the buy currency.
metadataobjectNoCustom key-value pairs (max 50 keys)

Example response

{
  "id": "cnv_01953e1a5f4b7007",
  "sellCurrency": "USD",
  "buyCurrency": "GBP",
  "sellAmount": "10000.00",
  "buyAmount": "7850.00",
  "rate": "0.7850",
  "quoteId": "qte_01953e1a5f4b7006",
  "sourceAccountId": "acc_01953e1a5f4b7002",
  "destinationAccountId": "acc_01953e1a5f4b7003",
  "status": "completed",
  "metadata": {},
  "createdAt": "2026-02-23T12:00:00Z",
  "completedAt": "2026-02-23T12:00:01Z"
}

Conversion lifecycle

Every conversion progresses through a defined set of statuses:
StatusDescriptionTerminal?
pendingConversion has been initiated.No
processingConversion is being executed. Balances are being adjusted.No
completedSuccessfully converted. Sell currency debited, buy currency credited.Yes
failedConversion could not be completed (e.g., insufficient funds, expired quote).Yes
A completed conversion generates two transaction entries on the account ledger:
  • A conversion_debit transaction on the source account (sell currency debited)
  • A conversion_credit transaction on the destination account (buy currency credited)

Webhook events

Subscribe to conversion events to track status changes in real time:
EventTrigger
conversion.createdA new conversion has been initiated
conversion.processingThe conversion is being executed
conversion.completedThe conversion finished successfully
conversion.failedThe conversion could not be completed

Listing conversions

Retrieve conversions with optional filters for status and date range.
curl -X GET "https://sandbox.api.openfx.com/v1/fx/conversions?status=completed&limit=20" \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP"

Query parameters

ParameterTypeDescription
limitintegerNumber of results per page (default 50, max 200)
starting_afterstringCursor for forward pagination
ending_beforestringCursor for backward pagination
statusstringFilter by status: pending, processing, completed, failed
createdAt[gte]stringFilter by creation date (inclusive lower bound, ISO 8601)
createdAt[lte]stringFilter by creation date (inclusive upper bound, ISO 8601)

Getting a specific conversion

curl -X GET https://sandbox.api.openfx.com/v1/fx/conversions/cnv_01953e1a5f4b7007 \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP"

End-to-end example

Here is a complete flow: discover pairs, lock a quote, and execute a conversion.
import requests
import uuid

BASE_URL = "https://sandbox.api.openfx.com/v1"
HEADERS = {
    "Authorization": f"Bearer {api_key}",
    "X-Signature": signature,
    "X-Timestamp": timestamp,
    "Content-Type": "application/json",
}

# Step 1: Verify the pair is available
pairs_resp = requests.get(f"{BASE_URL}/fx/asset-pairs", headers=HEADERS)
pairs = pairs_resp.json()

usd_gbp = next(
    (p for p in pairs["items"]
     if p["sellCurrency"] == "USD" and p["buyCurrency"] == "GBP"),
    None,
)

if not usd_gbp:
    raise ValueError("USD/GBP pair not available")

print(f"Pair available. Min: {usd_gbp['minAmount']}, Max: {usd_gbp['maxAmount']}")

# Step 2: Lock a quote
quote_resp = requests.post(
    f"{BASE_URL}/fx/quotes",
    headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
    json={
        "sellCurrency": "USD",
        "buyCurrency": "GBP",
        "sellAmount": "10000.00",
    },
)
quote = quote_resp.json()
print(f"Quote locked: {quote['id']} at rate {quote['rate']}")
print(f"You sell: {quote['sellAmount']} USD")
print(f"You get:  {quote['buyAmount']} GBP")
print(f"Expires:  {quote['expiresAt']}")

# Step 3: Execute the conversion (immediately, while quote is active)
conversion_resp = requests.post(
    f"{BASE_URL}/fx/conversions",
    headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
    json={
        "quoteId": quote["id"],
        "sellCurrency": "USD",
        "buyCurrency": "GBP",
        "sellAmount": "10000.00",
        "sourceAccountId": "acc_01953e1a5f4b7002",
        "destinationAccountId": "acc_01953e1a5f4b7003",
    },
)
conversion = conversion_resp.json()
print(f"Conversion {conversion['id']}: {conversion['status']}")
For cross-currency payments to external counterparties, you do not need to create a separate conversion. Use POST /payments with executionPreference: use_quote (to reference an FX quote) or executionPreference: at_market (to convert at the current rate). The payment endpoint handles FX conversion and delivery in one operation.

Error scenarios

ErrorCauseResolution
400 — unsupported asset pairThe sell/buy pair is not availableCheck GET /fx/asset-pairs for valid pairs
400 — amount below minimumThe sell amount is below the pair’s minAmountIncrease the amount or check pair limits
400 — amount above maximumThe sell amount exceeds the pair’s maxAmountReduce the amount or contact support for higher limits
400 — quote expiredThe referenced quoteId has expiredCreate a new quote and retry immediately
400 — quote consumedThe referenced quoteId was already usedCreate a new quote — each quote can only be used once
400 — insufficient balanceThe source account does not have enough of the sell currencyFund the source account before retrying

API Reference