Skip to main content
Withdrawals don’t complete instantly. The transition timing depends on the rail: seconds-to-minutes for stablecoins, hours-to-days for fiat. Use this page to know how long settlement typically takes, and how to poll without burning rate-limit budget waiting on an ACH wire.
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, transactionHash on 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):
Polling is a fallback. Where possible, prefer receiving webhook events over polling for terminal status. See Webhook authentication for the current webhook contract.

When a withdrawal is stuck

Step 1 — read the trace ID

Every response carries X-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

If the row is missing entirely (404), the original POST never landed — replay with the same 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.
A withdrawal still unsettled overnight on a Friday after a Thursday-evening submission is expected, not stuck.

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 new Idempotency-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): indeterminateGET /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 response X-Trace-Id if you contact support.

Step 6 — file a support ticket

Include in the ticket:
  • The X-Trace-Id from the original POST
  • The id (readable, wtd_-prefixed) of the stuck withdrawal
  • Current status
  • The Idempotency-Key used
  • Timestamps: createdAt from the resource and your client-side request time
A trace ID lets support correlate the request. Do not include request bodies or credentials unless support asks for a specific field through an approved channel.

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.