Skip to main content
A payout sends money from one of your financial accounts to a recipient’s bank account, in the same currency or with a conversion. This guide covers the whole flow: the resources involved, the one-time recipient setup, and the payout itself.

Account structure

A payout involves three resources:
  • FinancialAccount is yours: the balance the payout is funded from.
  • Account identifies the recipient and holds their capabilities. You create it.
  • PayoutAddress is the recipient’s bank account, where the money lands. You create this too.
A payout debits financialAccountId and credits receiver.payoutAddressId. You don’t name the recipient’s account separately, because the address already belongs to it. For identity, roles, and archiving, see Account model. You can omit financialAccountId when requesting a payout quote or creating an INLINE payout. If you provide it, use a valid finacc_ id. Quote and payout responses always include the financial account used. A FROM_QUOTE payout uses the financial account from its quote.

Endpoints

Every POST takes an Idempotency-Key header, and a request without one fails with 428 IDEMPOTENCY_KEY_MISSING. The samples on this page send a fresh UUID. See Idempotency.

Supported payout currencies

Payouts settle in five currencies. Each is enabled per organization: until yours is, a request in it fails with 403 PAYOUT_CURRENCY_NOT_ENABLED. Capabilities lists the capability each one needs. A dash means this page lists no corridor-specific limit. EUR, INR, MXN, COP, and AED have their own guides. These rules apply to every corridor:
  • sender. Only INR has a sending capability. An INR quote or payout without a sender fails with 422 SENDER_ACCOUNT_REQUIRED. In any other currency, naming one fails with 422 SENDER_NOT_SUPPORTED.
  • purpose. A quote carries no purpose, so send it when you create the payout. Valid values are GOODS_AND_SERVICES, INVESTMENTS, PAYROLL, and TREASURY_MANAGEMENT. A missing or refused purpose fails with 400 PURPOSE_INVALID.
  • receiverMemo. The limit counts letters, digits, and spaces. A memo that is too long or holds any other character fails with 400 RECEIVER_MEMO_INVALID. OpenFX never truncates it.
  • Amount limits. A receiveAmount outside the limit is refused when the payout is priced, with 422 AMOUNT_OUT_OF_RANGE. error.details carries the exact bound.
  • Funding currency. An AED payout is funded only from a USDC, USDT, EURC, or AED balance. Any other send currency fails with 422 QUOTE_SEND_CURRENCY_UNAVAILABLE.

Set up the recipient

You do this once per recipient. Before you can pay them, three things must be ACTIVE: the account, the capability the payout needs, and the payout address. Account model describes how each status changes.
1

Create an account for your recipient

Create an INDIVIDUAL or ORGANIZATION account with its identity and the capabilities it needs, by id. Include the identity fields each capability requires in the same request. Capabilities lists them.
cURL
The response wraps the Account in data. Check it before you wait for webhooks: it shows the account’s status and each capability’s, and anything already resolved won’t send a webhook you need to wait for.
If the identity is missing fields a capability needs, the request fails with 422 IDENTITY_REQUIREMENT_UNMET and error.details.issues lists each missing field. Requesting the same capability twice fails with 422 CAPABILITY_DUPLICATE.
2

Wait for what came back PENDING

The account and its capabilities settle separately, and a payout needs both ACTIVE. The account’s own status is OpenFX’s review of the recipient, so it can be ACTIVE while a capability is still PENDING. A capability usually settles within a minute, though that isn’t guaranteed. Subscribe to these webhooks before you create accounts, instead of polling.
3

Register the payout address

Register the recipient’s bank account against their accountId. routingFamily decides which fields you send:The account must already hold a capability that can receive at this address. It doesn’t need to be ACTIVE yet, but if there’s none at all the request fails with 409 ADDRESS_SERVES_NO_CAPABILITY.
cURL
The response wraps the PayoutAddress in data. It’s PENDING, or already ACTIVE if every check finished during the call:
4

Wait for the address to activate

Skip this if the create response already showed the outcome. status reflects every check on the address, including some that verifications[] doesn’t list, so an address can stay PENDING after all its listed verifications have resolved. Checks also continue after activation, so an ACTIVE address can still change status later.

Send the payout

Once the account, capability, and address are all ACTIVE, you can send payouts. A payout’s status shows how far the money has got. Before your account is debited: After the debit, PROCESSING continues: A payout ends in CANCELLED, EXPIRED, FAILED, PROCESSED, or RETURNED.
1

(Optional) Choose the financial account

List the balances that can fund a payout in a given receiveCurrency. Skip this if you already know the finacc_ id or aren’t specifying a financial account. An empty list means you’re enabled for the currency but hold no balance that can fund it. A 403 PAYOUT_CURRENCY_NOT_ENABLED means you’re not enabled for that currency at all.
cURL
2

Create the payout

With type: "INLINE", one request prices and creates the payout. Send the pytadd_ to credit, either sendAmount or receiveAmount (not both), the receiveCurrency, a sender if the currency requires one, and a purpose if the currency requires one. Optionally, include the finacc_ to debit, as this example does. Supported payout currencies lists which.
cURL
INR requires a sender; omitting it returns 422 SENDER_ACCOUNT_REQUIRED. See INR payout integration. Every other currency refuses one.The payout starts in REQUIRES_CONFIRMATION, and confirmations[] lists what you need to accept. The price holds for quoteForSeconds (60 seconds unless you ask for longer). If you confirm after fxRates.expiresAt, the payout moves to EXPIRED.You can also get the price first. Create a payout quote moves no money and returns a pytqte_ id, with the rate locked until fxRates.expiresAt. Then create the payout with type: "FROM_QUOTE" and that quoteId. Use this when someone needs to see the price before the payout exists, for example in an approval step.If a required capability is missing or not ACTIVE, you get 409 CAPABILITY_NOT_ACTIVE. If the financial account can’t cover the payout, you get 409 PAYOUT_INSUFFICIENT_FUNDS. If the shortfall shows up a moment later, the payout is still created, but it goes straight to FAILED.
3

Read the pricing, then confirm or cancel

Check fxRates, fees, and confirmations[] in the create response. Which confirmations appear depends on the payout.Confirm the payout to accept every entry in confirmations[] at once and move it to ACCEPTED. You can’t accept entries one at a time. To decline, cancel the payout. You can only cancel before confirming, so no money has moved.
cURL
If the payout is no longer waiting for confirmation (it expired, or was already confirmed or cancelled), you get 409 PAYOUT_CONFIRMATION_NOT_ALLOWED or 409 PAYOUT_CANCELLATION_NOT_ALLOWED.
After ACCEPTED, OpenFX handles the rest. Each status change above has a webhook event: see Payout webhooks. For statuses with more than one origin, data.previousStatus tells you where the payout came from.

Trace a payout with the receiving bank

settlementTraceId carries the reference the receiving side uses for the payment. Its status starts PENDING and moves to AVAILABLE, or to UNSUPPORTED where the rail issues no reference. It runs on its own clock, so it can still read PENDING after the payout reaches PROCESSED. When it is AVAILABLE, key names the kind of reference and value holds it. The rail decides the key:

Verifications and checks

The recipient is checked in two places, and each check answers a different question. On the payout address, at setup. A name match runs when you create the address, wherever the payment scheme offers one, and verifications[] shows its latest result. An address that isn’t ACTIVE can’t receive payouts. On the payout, before any money moves. Each entry in confirmations[] is something you accept by confirming. A QUOTE entry covers the rate and fees. An AccountHolderNameConfirmation shows the name check. The check runs again only where regulation requires it, as SEPA does. Otherwise the payout reuses the address’s result. NameMatchResult lists every possible outcome. On a payout, no result blocks you. NO_MATCH rejects a payout address, but on a payout it’s your call: confirming accepts the name as it is, and cancelling declines it. A check that gets no answer returns UNAVAILABLE, and one left to the receiving bank returns DEFERRED_TO_RECEIVING_FI. Neither blocks the payout. ACCOUNT_HOLDER_MISMATCH is different. The receiving bank runs its own check at settlement, whatever you accepted. If the name doesn’t match, the payout moves to FAILED with that code on data.failure.

Common mistakes

  • Paying before the capability is ACTIVE. You can create an address while the capability is PENDING, but the payout itself needs it ACTIVE. Otherwise it fails with 409 CAPABILITY_NOT_ACTIVE.
  • Confirming after the price expires. fxRates.expiresAt is the deadline. After it, confirming fails with 409 PAYOUT_CONFIRMATION_NOT_ALLOWED and the payout moves to EXPIRED.
  • Treating PROCESSED as final. The payment can still be returned, which you’ll see as payments.payout.returned with data.previousStatus: "PROCESSED".
  • Assuming NO_MATCH blocks a payout. It doesn’t. Confirming accepts the name as it is.
  • Ignoring data.previousStatus on payments.payout.processing, .failed, and .returned. Each of these events can fire for more than one reason. Check previousStatus to know which.

Archiving does not stop a payout in flight

Archiving an account or payout address only blocks new payouts to it. A payout that’s already ACCEPTED, PROCESSING, or IN_REVIEW carries on to the end. Archiving doesn’t cancel or redirect it.

What’s next

EUR payout integration guide

Paying euro accounts over SEPA or SWIFT.

INR payout integration guide

Paying Indian bank accounts, including NRE accounts.

MXN payout integration guide

Paying Mexican bank accounts over SPEI.

COP payout integration guide

Paying Colombian bank accounts and Bre-B keys.

AED payout integration guide

Paying UAE bank accounts by IBAN.

Account model

How accounts, capabilities, and addresses change status.

Payment Webhooks

Every payout, account, and address event.