Overview

A cross-currency payment converts funds from one currency to another as part of the payment flow. For example, sending USD from your account and delivering EUR to a counterparty in Germany. OpenFX handles the currency conversion internally through the FX Engine (Layer 1). You do not need to perform a separate conversion before creating a payment — the conversion is embedded in the payment itself.
Your USD Account                          Counterparty in Germany
  |                                             ^
  |  Debit: $10,025.00                          |  Credit: EUR 9,175.00
  |  (send + fees)                              |
  +---- FX Engine --------------------------------+
         USD -> EUR @ 0.9175
         Fee: $25.00

How It Works

  1. You create a payment specifying a source account in one currency and a counterparty payment method in another currency.
  2. OpenFX detects the currency mismatch and applies an FX conversion automatically.
  3. The response includes the exchangeRate details showing the exact rate applied.
  4. The fees breakdown includes any FX markup as a separate feeComponent.

Execution Preferences

You control how the FX rate is determined using the executionPreference field.

at_market — Spot rate execution

The payment executes at the current market rate at the time of processing. This is the simplest approach and works well when exact rate certainty is not required.
{
  "executionPreference": "at_market"
}
Advantages: No need to create a quote first. Single API call. Trade-off: The actual rate may differ slightly from what was previewed, especially if there is a delay between preview and execution (e.g., during compliance screening).

use_quote — Locked rate execution

The payment executes at a previously locked rate from an FX quote. This guarantees the exact rate your user saw when they confirmed the payment.
{
  "executionPreference": "use_quote",
  "quoteId": "qte_01953e1a5f4b7006"
}
Advantages: Rate certainty. The counterparty receives exactly the amount shown in the quote. Trade-off: Requires an extra API call to create the FX quote first. The quote has a short expiration window (typically 60 seconds).
When to use which: Use at_market for automated, programmatic payments where rate fluctuation is acceptable. Use use_quote when you are showing a confirmation screen to a user and need the exact price to match what they approved.

Full Walkthrough: USD to EUR via SWIFT

This example sends USD 10,000 from a US account and delivers EUR to a counterparty in Germany via SWIFT, using a locked FX quote.

Step 1: Create an FX quote

curl -X POST https://sandbox.api.openfx.com/v1/fx/quotes \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "sellCurrency": "USD",
    "buyCurrency": "EUR",
    "sellAmount": "10000.00",
    "accountId": "acc_01953e1a5f4b7002"
  }'

Step 2: Show the user the pricing breakdown

Present the quote details to your user for confirmation:
Payment Summary
-------------------------------
You send:           $10,000.00 USD
Exchange rate:      1 USD = 0.9175 EUR
They receive:       EUR 9,175.00
-------------------------------
Fees:
  SWIFT wire fee:   $15.00
  FX markup:        $10.00
  Total fees:       $25.00
-------------------------------
Total debit:        $10,025.00 USD
-------------------------------
Rate valid for:     60 seconds

Step 3: Create the payment with the locked quote

curl -X POST https://sandbox.api.openfx.com/v1/payments \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceAccountId": "acc_01953e1a5f4b7002",
    "counterpartyId": "cpt_01953e1a5f4b7008",
    "paymentMethodId": "pm_01953e1a5f4b7400",
    "rail": "swift",
    "sendAmount": {
      "currency": "USD", "value": "1000000"
    },
    "amountInputSide": "send",
    "executionPreference": "use_quote",
    "quoteId": "qte_01953e1a5f4b7006",
    "purpose": "invoice_payment",
    "senderReference": "INV-2026-0099"
  }'
The payment is created at the locked rate. The exchangeRate in the payment response matches the quote exactly:
{
  "id": "pmt_01953e1a5f4b7020",
  "status": "created",
  "rail": "swift",
  "sendAmount": { "currency": "USD", "value": "1000000" },
  "receiveAmount": { "currency": "EUR", "value": "917500" },
  "exchangeRate": {
    "sellCurrency": "USD",
    "buyCurrency": "EUR",
    "rate": "0.9175",
    "rateTimestamp": "2026-02-25T14:30:00Z",
    "quoteId": "qte_01953e1a5f4b7006"
  },
  "fees": {
    "feeAmount": "25.00",
    "feeCurrency": "USD",
    "feeComponents": [
      { "type": "fedwire_fee", "value": "1500", "currency": "USD" },
      { "type": "fx_markup", "value": "1000", "currency": "USD" }
    ]
  },
  "totalDebitAmount": { "currency": "USD", "value": "1002500" }
}

Fee Breakdown

Cross-currency payments have richer fee structures. The fees object provides a full breakdown:
FieldDescription
fees.feeAmountTotal fee amount as a string
fees.feeCurrencyCurrency of the fee (typically matches the send currency)
fees.feeComponentsArray of individual fee line items

Fee Component Types

TypeDescription
fx_markupSpread applied to the FX rate
fedwire_feeDomestic Fedwire origination fee
processing_feeGeneral processing fee
network_feeBlockchain network/gas fee (crypto rail)
intermediary_feeCorrespondent bank fee (SWIFT)
The totalDebitAmount is always the sum of sendAmount + all fees. This is the exact amount debited from your source account. Use this field for reconciliation.

At-Market Cross-Currency Payment

For a simpler flow without a separate quote step, use executionPreference: "at_market":
curl -X POST https://sandbox.api.openfx.com/v1/payments \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceAccountId": "acc_01953e1a5f4b7002",
    "counterpartyId": "cpt_01953e1a5f4b7008",
    "paymentMethodId": "pm_01953e1a5f4b7400",
    "rail": "swift",
    "receiveAmount": {
      "currency": "EUR", "value": "900000"
    },
    "amountInputSide": "receive",
    "executionPreference": "at_market",
    "purpose": "invoice_payment"
  }'
In this example, amountInputSide: "receive" tells the API to back-calculate the send amount so the counterparty receives exactly EUR 9,000.00.

API Reference