You submit a withdrawal and then wait for it to settle. Surfacing the
distinction between “still in flight” and “settled” lets you tell a user what
to expect next.
Status
status is a plain string reflecting the withdrawal’s current lifecycle state at the moment of response (e.g. COMPLETED once funds have left OpenFX). Read the current value from GET /v3/fx/withdrawals/{id} or the resource embedded in your webhook payload rather than assuming a fixed set of intermediate states client-side.
v2 origin. v3 unifies what v2 split across
POST /v2/.../withdrawal
(stablecoin) and POST /v2/.../fiat_withdrawal
(fiat). The rail is inferred from the verified withdrawal account. Look up a
withdrawal by ID via GET /v2/brokerage/{orgId}/withdrawal/{withdrawalId} on v2, or GET /v3/fx/withdrawals/{id} on v3.Where money goes
The rail is bound to the verified withdrawal account. You don’t pick it on the request. Look up the rail or network on the account record, then consult the appropriate reference page for chain or rail detail.- Fiat withdrawals. See Settlement times for the submission cut-off per currency and rail. Submissions after a cut-off are processed the next banking day.
- Stablecoin withdrawals. See Supported networks for the per-stablecoin chain matrix. Once broadcast,
transactionHashon the withdrawal identifies the on-chain transaction.
Each verified withdrawal account = one destination × one network × one
asset. USDC and USDT on the same Ethereum address are two separate
withdrawalAccountId records; Ethereum-mainnet USDC and Polygon USDC are
likewise two separate records. This is intentional — it protects clients
funding from exchanges (where the same nominal address may not actually be
controllable across all chains, and tokens may have distinct deposit addresses
even on the same chain) from sending to an unreachable destination. Add each
destination explicitly via the dashboard; there is
no bulk-add.Polling pattern with adaptive backoff
Don’t poll every 5 seconds for fiat; you’ll burn rate-limit budget waiting on a rail whose settlement spans banking hours. Adaptive backoff (start short, grow longer):When a withdrawal is stuck
Step 1 — read the trace ID
Every response carriesX-Trace-Id. Share that ID with OpenFX support if you escalate — it reconstructs the full request server-side. Capture it before doing anything else.
Step 2 — re-fetch the resource
Idempotency-Key (see Idempotency persistence).
Step 3 — match the state against the rail clock
Not-yet-settled past the rail cut-off is the most common cause of “stuck.” See Settlement times for per-rail submission windows. Common cases:- Fiat USD via Fedwire: submissions after 4:30 PM ET batch the next business day.
- Fiat EUR via SEPA: submissions after 12:30 PM London time batch the next business day.
- Stablecoin on Ethereum: network congestion can delay confirmation 10+ minutes.
Step 4 — check for background blockers
Step 5 — check the error catalog for synchronous failures
For failures surfaced synchronously on the POST, the error catalog (Errors) lists every code’s retry semantics. Quick guide:- Validation codes (
WITHDRAWAL_INVALID_*,WITHDRAWAL_ADDRESS_*): fix root cause, retry with newIdempotency-Key. The original key + original body would replay the same failure. - State codes (
WITHDRAWAL_INSUFFICIENT_BALANCE,WITHDRAWAL_PENDING_DEPOSIT_REQUIRED): clear the blocking state, retry with new key. - Write-path 500s (
WITHDRAWAL_INITIATION_FAILED): indeterminate —GET /v3/fx/withdrawals/{id}first to confirm whether the withdrawal was actually created before retrying with the same key. See Idempotency → Crash recovery. INTERNAL_ERROR(500): follow its cataloged retry strategy and include the responseX-Trace-Idif you contact support.
Step 6 — file a support ticket
Include in the ticket:- The
X-Trace-Idfrom the original POST - The
id(readable,wtd_-prefixed) of the stuck withdrawal - Current
status - The
Idempotency-Keyused - Timestamps:
createdAtfrom the resource and your client-side request time
Don’t do this
- Replay a rejected withdrawal with the same
Idempotency-Key. The cached response is the rejection. Use a fresh key after fixing the root cause. - Poll faster than every 5s. It won’t make a SWIFT wire arrive sooner; it will trip
RATE_LIMIT_EXCEEDED. - Assume unsettled means stuck. Settlement times defines the windows; treat unsettled past the window as the alert threshold, not unsettled itself.
Common mistakes
- Fixed-interval polling at 5s. Fine for the first minute; wasteful for a fiat withdrawal that spans banking hours.
- Polling without an upper bound. Always have a “give up and alert” condition.
What’s next
Trade Settlement
The full deposit / balance / withdrawal model.
Errors
Recovery patterns for rejected withdrawals.
Idempotency
7-day TTL on withdrawal keys (carried forward from v2).
Webhooks setup
Receive
withdrawals event notifications instead of polling.