Draft API (v0.3.0-draft) — The transfers surface is currently in draft. Schemas, statuses, and lifecycle behavior may change before the stable release.

What Are Internal Transfers?

Internal transfers move funds between accounts on the same platform. They are the simplest way to rebalance funds across your OpenFX accounts — no external payment rail is involved, and settlement is near-instant.
Source Account (acc_)                Destination Account (acc_)
  │                                    │
  │  ──► Transfer (trf_) ──►          │
  │  Debit $2,500.00 USD              │  Credit $2,500.00 USD
Internal transfers are ideal for:
  • Treasury rebalancing — Moving funds from a collections account to an operating account.
  • Pre-funding payments — Moving funds to an account before initiating an outbound payment.
  • Segregation — Moving client funds to a dedicated virtual account.
  • Multi-currency positioning — After an FX conversion, moving the converted funds to the appropriate account.
Same currency only. Internal transfers require that the source and destination accounts hold the same currency. If you need to move funds between accounts in different currencies, perform an FX conversion first, then transfer the converted funds.

Transfer Lifecycle

Transfers follow a simple lifecycle. Most transfers complete near-instantly.
StatusDescription
pendingTransfer created. Funds are being moved between accounts.
completedTransfer settled. Source debited and destination credited.
failedTransfer failed (e.g., insufficient funds, account suspended).
Internal transfers typically complete within seconds. Unlike external payment rails, there are no settlement delays or business-day dependencies.

Creating an Internal Transfer

curl -X POST https://sandbox.api.openfx.com/v1/transfers \
  -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",
    "destinationAccountId": "acc_01953e1a5f4b7003",
    "amount": {
      "currency": "USD", "value": "250000"
    },
    "reference": "Internal rebalance"
  }'
The response returns the full transfer resource:
{
  "id": "trf_01953e1a5f4b7008",
  "sourceAccountId": "acc_01953e1a5f4b7002",
  "destinationAccountId": "acc_01953e1a5f4b7003",
  "amount": {
    "currency": "USD", "value": "250000"
  },
  "status": "completed",
  "reference": "Internal rebalance",
  "metadata": {},
  "createdAt": "2026-02-25T10:00:00Z",
  "completedAt": "2026-02-25T10:00:01Z"
}

Request Fields

FieldTypeRequiredDescription
sourceAccountIdstringYesAccount to debit (must have sufficient balance)
destinationAccountIdstringYesAccount to credit
amountMoneyYesAmount to transfer ({ "currency": "USD", "value": "250000" })
referencestringNoYour reference for the transfer
metadataobjectNoCustom key-value pairs (max 50 keys)

Listing Transfers

curl -X GET "https://sandbox.api.openfx.com/v1/transfers?limit=20" \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP"

Getting a Transfer

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

Transfers vs OPEN Payments

Internal transfers and OPEN payments both move funds within the OpenFX platform, but they serve different purposes:
Internal TransfersOPEN Payments
EndpointPOST /transfersPOST /payments/open
DirectionBetween your own accountsTo another platform customer
CounterpartyNot required (own accounts)Required (another entity)
Use caseTreasury rebalancing, pre-fundingPaying another OpenFX customer
FeesNoneNone
SettlementNear-instantInstant
Use transfers to move money between accounts you own. Use OPEN payments to send money to a different customer on the OpenFX platform.

Error Handling

HTTP StatusError CodeDescription
409duplicate_idempotency_keySame idempotency key used with different request body
409idempotency_key_in_flightSame idempotency key still processing. Retry after Retry-After header
422insufficient_fundsSource account does not have enough available balance
422invalid_account_combinationAccounts are not compatible (e.g., different currencies, same account)

Webhook Events

Subscribe to these events to track transfer lifecycle changes:
EventDescription
transfer.createdTransfer has been created and is pending.
transfer.completedTransfer settled. Source debited and destination credited.
transfer.failedTransfer failed.

Transaction Ledger Entries

Each transfer creates two transaction entries in the ledger — a debit on the source account and a credit on the destination account. These entries share the same transactionGroupId and are linked via linkedTransactionId.
[
  {
    "id": "txn_01953e1a5f4b7602",
    "accountId": "acc_01953e1a5f4b7002",
    "type": "transfer_out",
    "direction": "debit",
    "value": "250000",
    "currency": "USD",
    "balance": "7500.00",
    "transactionGroupId": "grp_01953e1a5f4b7603",
    "referenceType": "transfer",
    "referenceId": "trf_01953e1a5f4b7008"
  },
  {
    "id": "txn_01953e1a5f4b7604",
    "accountId": "acc_01953e1a5f4b7003",
    "type": "transfer_in",
    "direction": "credit",
    "value": "250000",
    "currency": "USD",
    "balance": "12500.00",
    "transactionGroupId": "grp_01953e1a5f4b7603",
    "referenceType": "transfer",
    "referenceId": "trf_01953e1a5f4b7008"
  }
]
See Transaction Ledger for full details on how transfers are recorded.

API Reference