B2B API Reference
Submit Signed Withdrawal (Stage 2)
Stage 2 of the two-stage withdrawal flow — submits the user-signed authorization. Accepted operations dispatch async.
POST
Stage 2 of the two-stage withdrawal flow. Accepts the
message object
returned by stage 1 verbatim, plus the user’s signature over the
authorization. Verifies the signature against the portfolio owner,
re-checks live onchain balances, and dispatches the transfer onchain.
body.message is the structured message from stage 1 — typedData.message
for EVM portfolios, authorization.message for Solana portfolios (the shape
is identical), including the liquidate flag. Echo it back verbatim: the
signature was computed over these exact bytes. body.signature encoding
depends on how the owner signed:
- EVM (and Solana Model A, EVM-rooted):
0x…hex (EIP-712 / EIP-191 ECDSA; EVM also accepts ERC-1271 smart-contract-wallet signatures). - Solana Model B (Solana-rooted): base58 ed25519.
- Auth:
x-api-keyheader (required) - Scope:
portfolios:withdraw
Recipient:
recipientAccountId may be any address the owner signs for
(e.g. a user’s smart wallet), on both EVM and Solana — the recipient is
bound into the owner-signed authorization, so any payout is explicitly
authorized by the owner. A self-transfer to the smart account / Swig
sub-account being debited is always rejected (400 API_211 INVALID_RECIPIENT).Idempotency
Keyed onnonce (scoped to your API key). Retries with the same
message + signature replay the cached 202 response. Retries with the
same nonce but a different body return 409 IDEMPOTENCY_KEY_CONFLICT
— fix the client, don’t retry. Two parallel requests with the same body:
one proceeds, the other sees 409 IDEMPOTENCY_IN_PROGRESS — retry the
identical body after 2–3 seconds.
If stage 2 fails after accepting the request (e.g., a transient signature
verification error, or balance drops below the requested amount between
stages), the idempotency lock is released so the integrator can re-submit
the same message + signature once the transient issue resolves.
Liquidation
When the signedmessage.liquidate is true, the authorized assets are
swapped to the signed message.settlementAssetId settlement asset (USDC by
default; USDT also allowed on EVM) and delivered to the recipient instead of
being transferred as-is. The assets, settlement asset, and recipient must all
reference the same chain; this flow does not perform cross-chain delivery or
bridging. On Solana the swaps and the final delivery run as one engine
operation inside the portfolio’s smart account. The returned
operationId tracks the swap. Both liquidate
and settlementAssetId are part of the signed message and cannot be changed here. A
400 API_219 WITHDRAW_AS_USDC_UNSUPPORTED_CHAIN is returned when the default
USDC settlement asset is unavailable on the chain; a 400 API_221 UNSUPPORTED_SETTLEMENT_ASSET
is returned when settlementAssetId is not an allowed settlement asset on the chain.
Polling for onchain status
The response returnsoperationId. Poll
GET /v2/portfolios/{portfolioId}/operations/{operationId}
until the operation reaches a terminal (completed / failed / cancelled)
state.
Common error responses
400 API_216 WITHDRAWAL_AUTHORIZATION_EXPIRED— signed authorization’sexpiresAtis in the past. Restart the flow via stage 1.400 API_217 INVALID_WITHDRAWAL_SIGNATURE— signature does not recover to the portfolio owner. Almost always a client bug.400 API_210 INSUFFICIENT_BALANCE— balance dropped belowamountRawbetween stage 1 and stage 2. Restart stage 1 to issue a fresh authorization.400 API_214 WITHDRAWAL_PORTFOLIO_MISMATCH—message.portfolioIddoesn’t match the URL path.400 API_213 WITHDRAWAL_CHAIN_MISMATCH— at least one asset is on a different chain thanrecipientAccountId.400 API_215 PORTFOLIO_HAS_NO_VAULT_ON_CHAIN— portfolio has no smart account deployed on the recipient’s chain.400 API_219 WITHDRAW_AS_USDC_UNSUPPORTED_CHAIN—message.liquidateistrue(with the default USDC settlement asset) but the recipient’s chain has no canonical USDC.400 API_221 UNSUPPORTED_SETTLEMENT_ASSET—message.settlementAssetIdis not an allowed settlement asset on the recipient’s chain (configured USDC or USDT for an EVM withdrawal, or USDC for a Solana withdrawal).400 API_212 DUPLICATE_WITHDRAW_ASSET— two or more assets inassetsshare the sameassetId.400 API_218 UNSUPPORTED_WITHDRAW_ASSET— the requested asset cannot use the direct-transfer withdrawal path. For a redemption-required yield share, redeem through its registered yield source and then withdraw the underlying asset.400 API_211 INVALID_RECIPIENT— zero address, or a self-transfer to the smart account / Swig sub-account being debited.403 API_104 PERMISSION_DENIED— Solana withdrawal but thesvm_b2b_apifeature is not enabled for the tenant.404 API_200 PORTFOLIO_NOT_FOUND—portfolioIddoesn’t exist or your API key cannot access it (can surface at stage 2 if the portfolio was archived/reassigned between stages).409 API_007 IDEMPOTENCY_IN_PROGRESS— another request with the same nonce is still running. Retry the identical body.409 API_008 IDEMPOTENCY_KEY_CONFLICT— nonce reused with a different body. Don’t retry; restart stage 1.503 API_506 SIGNATURE_VERIFIER_UNAVAILABLE— signature verification is temporarily unavailable. Safe to retry; the idempotency lock has been released.