Skip to main content
A verified withdrawal account is a pre-approved bank account or stablecoin wallet that outgoing funds can settle to: fiat on a specific rail, or stablecoin on a specific chain. Every POST /v3/fx/withdrawals call references one via withdrawalAccountId. The currency and rail or chain are bound to the withdrawal account at creation time, so the withdrawal call itself stays minimal.
Verified accounts are managed in the dashboard. Creation, edits, and removal happen in the OpenFX dashboard. Your integration reads the resulting accounts via the API and references them by id on withdrawal requests.

Why verify

  • Risk. Withdrawal accounts are reviewed before they go live, so a compromised JWT can’t send funds to a wallet you’ve never approved.
  • Simplicity. Withdrawals don’t need bank details, beneficiary names, IBANs, or chain identifiers on every call — that’s all pinned to the account record.
  • Auditability. Every dispatched withdrawal traces back to a specific withdrawalAccountId, which traces back to the human who approved it.
Each verified account = one address × one network × one asset. If you want to withdraw USDC and USDT to the same Ethereum address, you must add two records — one per (address, network, asset) tuple. Likewise, the same address on Ethereum mainnet vs Polygon is two separate records.Why the constraint exists. Customers funded via exchanges often don’t control the same nominal address across all chains, and some exchanges issue different deposit addresses per token even on the same chain. Forcing explicit per-tuple add prevents a withdrawal landing at an address that doesn’t actually receive the asset on that chain — a class of permanent loss. If you self-custody and the same address works for every chain × asset, you still add each tuple explicitly.

Add an account (dashboard)

1

Sign in to the dashboard

Open app.openfx.com with an admin user. Withdrawal account management is gated to admins.
2

Pick an account type

Fiat bank account (API assetType: "FIAT") for a wire/SEPA/SPEI/etc rail, or stablecoin wallet (API assetType: "CRYPTO") for an on-chain account.
3

Enter the account details

For fiat: currency, beneficiary name, account number / IBAN, routing number / BIC, and the rail you want OpenFX to dispatch over. The selectable rails depend on the currency (e.g. USD shows Fed Wire and SWIFT; EUR shows SEPA, SEPA Instant, and SWIFT).For stablecoin: token (USDC, USDT, EURC), the chain (one of the supported networks), and the wallet address. The address must match the chain’s format — Solana addresses are not interchangeable with Ethereum addresses.
4

Attach supporting documents

Upload up to 3 files, 10 MB each. For fiat accounts, attach bank statements or beneficiary-ownership documents. For stablecoin wallets, attach proof of address control (signed message, exchange screenshot, or similar). Documents stay in the dashboard — the API never returns them.
5

Submit for review

Every submission passes a two-step gate before going live:
  • Compliance — AML screening, plus on-chain TRM screening for stablecoin wallets.
  • Ops — bank-detail validation and beneficiary-ownership review.
Both gates must clear before the account becomes active. The listing on GET /v3/fx/withdrawal-accounts includes accounts in every status (PENDING, ACTIVE, ARCHIVED, REJECTED, DEACTIVATED, ADDITIONAL_ACTION_NEEDED) by default; pass ?status=ACTIVE (or filter client-side) based on your flow. Only ACTIVE accounts accept withdrawals — submitting against any other status returns an error.
GET /v3/fx/withdrawal-accounts supports ?status= (plus ?assetType=, ?currency=, ?network=, and ?verified=) as server-side filters. Filter to ACTIVE — via the query parameter or client-side — before posting withdrawals.
Duplicates are rejected at submission. Two records with the same rail, currency, and account number (or wallet address) cannot coexist. If a withdrawal account is already on file, edit the existing record instead of resubmitting.

Read accounts via the API

Once approved, accounts appear on GET /v3/fx/withdrawal-accounts.
A typical response — a fiat row and a stablecoin row — looks like:
rail determines the shape of destination: chain name for CRYPTO (destination.address), or one of SWIFT, FEDWIRE, FPS, or another local rail for FIAT (destination.accountName / accountNumber / bankName, plus rail-specific fields like swiftCode, routingNumber, or ukSortCode). See the WithdrawalAccount schema in the API reference for the full per-rail shape.
Cache the account list, not the JWT. Account membership changes on the order of days; tokens change every 60 seconds (and every request also carries a fresh X-Request-Signature). Pull withdrawal_accounts once at startup (or once per request when you need fresh state) — don’t ride a stale cache across new dashboard approvals.

Use the withdrawalAccountId on a withdrawal

The currency on the withdrawal must match the account’s currency. The chain (for stablecoin) or rail (for fiat) is not on the request — both are inferred from withdrawalAccountId.

Lifecycle and changes

  • Locked fields. Once an account is ACTIVE, the account number (or wallet address), rail or chain, and currency are locked. Changing any of them means creating a new account and re-running the two-step review.
  • Editable fields. The display name (and a dashboard memo) can be edited without re-verification. The API returns the latest displayName on the next read.
  • Deactivating. Deactivate in the dashboard; the id stops accepting withdrawals immediately once status reads DEACTIVATED. In-flight withdrawals against a now-deactivated account complete normally.
  • Re-enabling. Re-enable the same id if it gets reactivated; the UUID is stable.
  • Removal. Permanent deletion strips the account from the list (status: "ARCHIVED"). Re-adding the same withdrawal account later returns a new id.
The dashboard is the source of truth for submission status, document history, and review-gate outcomes. The API returns the full account details nested under destination on the ACTIVE record; document attachments live in the dashboard.

Common mistakes

  • Re-using a v2 withdrawal-address record by ID in v3. v3’s withdrawal_accounts is a unified resource that fold v2’s two surfaces (stablecoin wallets and fiat accounts) into one. IDs are not portable. Re-list via GET /v3/fx/withdrawal-accounts.
  • Verifying a Polygon address and assuming USDC is fungible across chains. A USDC withdrawal initiated against a Polygon withdrawalAccountId will only ever settle on Polygon. There is no cross-chain bridge inside OpenFX. See Supported networks.
  • Picking the wrong rail for a fiat withdrawal account. Fed Wire vs SWIFT, FPS vs CHAPS — these have different cut-offs, fee profiles, and value limits. The rail is fixed at account creation; pick it deliberately. See Settlement times.
  • Polling the API for full review status. The API surfaces a status (PENDING, ACTIVE, ARCHIVED, REJECTED, DEACTIVATED, ADDITIONAL_ACTION_NEEDED) but does not expose document follow-up state, reviewer notes, or rejection reasons. For full review status, check the dashboard.

What’s next

Trade Settlement

How balances, deposits, and withdrawals interact.

Settlement times

Per-rail cut-offs for fiat withdrawal accounts.

Supported networks

Per-chain support for stablecoin withdrawal accounts.

Webhooks setup

Receive withdrawals events for your withdrawal accounts.