Withdrawal lifecycle
The typical flow is: preview → initiate → track → complete.- Preview — call
withdrawal-previewto see what’s available and get a sourcing plan. - Initiate — call
withdrawto create the withdrawal. The system reserves liquidity and begins sourcing. - Track — poll the withdrawal or subscribe to webhooks. The withdrawal moves through statuses as positions unwind, funds bridge, and payouts execute.
- Complete — the withdrawal reaches
completedwhen all payout legs are terminal.
Preview a withdrawal
Before initiating, preview what balance is available for a given destination and how liquidity will be sourced.| Field | Description |
|---|---|
balance | Total value in bridge domain(s) that can satisfy this destination (not wallet-wide) |
pendingWithdrawals | Sum of active liquidity reservations in those bridge domain(s) |
availableBalance | balance - pendingWithdrawals (unreserved liquidity) |
positions[] | Best-effort sourcing plan: cash first, then more-liquid positions, then less-liquid |
Initiate a withdrawal
| Field | Required | Description |
|---|---|---|
requestId | Yes | UUID v4 idempotency key |
chain | Yes | Destination chain |
token | Yes | Destination token |
withdrawalAmount | Yes | Amount as a string (max 6 decimal places for USDC, max 500,000) |
destinationAddress | Yes | Recipient address on the destination chain |
chain: "ethereum"refers to Ethereum Sepolia in sandbox.- Solana withdrawals are not currently supported (the endpoint expects an EVM destination address).
List withdrawals
| Param | Description |
|---|---|
limit | Max items (default 50, max 200) |
cursor | Opaque cursor from nextCursor |
sort | Sort field (default createdAt) |
order | desc (default) or asc |
createdAtGte | ISO-8601 timestamp filter |
status | Repeatable status filter (e.g. status=processing&status=completed) |
Fetch a withdrawal
Withdrawal statuses
Withdrawals move through these statuses:| Status | Meaning |
|---|---|
pending_liquidity | Accepted and reserved; sourcing hasn’t started yet |
processing | Unwinding/bridging/sourcing in progress |
queued_for_customer_approval | Ready for approval but blocked behind a prior approval on the same wallet+chain |
pending_customer_approval | Customer action required (Turnkey approval) |
pending_broadcast | Signed but not yet broadcast |
broadcasted | Broadcast submitted; finality pending |
completed | Terminal: all payout legs finished |
partially_completed | Terminal: some legs completed, others failed or were cancelled |
failed | Terminal: withdrawal failed |
cancelled | Terminal: withdrawal was cancelled |
Payout legs
Each withdrawal includes apayouts array representing one or more payout legs. Payout legs track chain transaction hashes and leg-level progress.
| Payout status | Meaning |
|---|---|
planning | Leg is being planned |
waiting_for_prior_approval | Blocked behind a prior approval on the same wallet+chain |
pending_customer_approval | Customer approval required |
pending_broadcast | Signed, waiting to broadcast |
broadcasted | Broadcast submitted |
processing | In progress |
completed | Terminal: success |
failed | Terminal: failed |
cancelled | Terminal: cancelled |
skipped | Terminal: leg not needed |
withdrawal.status is completed and all payout legs are in a terminal state (completed or skipped).
Customer approvals (head-of-line gating)
Only one customer approval can be outstanding perportfolioWalletId + chain. If you submit multiple withdrawals quickly, later ones may enter queued_for_customer_approval until earlier approvals resolve.
Withdrawal responses include:
customerApprovalState:"required"|"queued"|"not_required"blockedBy: when queued, describes the blocking payout leg
Cancel a withdrawal
Cancel an in-progress withdrawal:partially_completed rather than cancelled.
You can also cancel or retry individual payout legs:
Timing expectations
Withdrawal time depends on which yield positions need to unwind and whether cross-chain bridging is required.| Position | Unwind time | Notes |
|---|---|---|
| Cash (USDC) | Instant | No unwind needed |
usdz | Instant (PT0H) | Convert to USDC on Arbitrum |
syrupUsdc | Instant (PT0H) | ERC-4626 redemption on Ethereum |
rlp | Up to 24h (PT24H) | Async Resolv exit window |
POST /v2/portfolio-wallets/withdrawal-time-estimates (see Yield Sources).
Withdrawal webhooks
Subscribe to these events for real-time withdrawal tracking:portfolio_wallet.withdrawal.status_changedportfolio_wallet.withdrawal.payout.status_changed
Bridge domains reference
A bridge domain is a hard boundary for where liquidity can come from. The withdrawal engine never crosses bridge domain boundaries.USDC unified (CCTP)
USDC on CCTP-supported chains forms a singleusdc:unified domain:
- Ethereum, Arbitrum, Base, Polygon, Optimism, Avalanche, Solana
BSC (always isolated)
BSC is always isolated, even for tokens that unify elsewhere:usdc:bsc,usdt:bsc
USDT (always per-chain)
USDT has no cross-chain unification:usdt:ethereum,usdt:arbitrum,usdt:solana, etc.
Default (per-chain)
All other tokens are per-chain:dai:ethereum,usdz:arbitrum, etc.
How cash fits in
Within a bridge domain, cash is always the first source of liquidity. Only if cash is insufficient will the system unwind yield positions in the same domain. Thepositions[] array in the withdrawal preview shows this ordering.
Bridge domain IDs are always lowercase and intended to be stable, but use the withdrawal preview as the canonical source of truth for what’s available.