B2B API Reference
Prepare Withdrawal Authorization (Stage 1)
Stage 1 of the two-stage withdrawal flow — returns the authorization (EIP-712 for EVM, an off-chain message for Solana) for the end-user to sign in their wallet.
POST
Stage 1 of the two-stage withdrawal flow. Validates the request, checks
live onchain balances, and returns the authorization message the end-user
signs with their portfolio owner wallet. Stage 2
(
Some yield-share assets must be redeemed through their registered yield source
before the underlying asset can leave a portfolio. A direct request for such a
share returns
POST /v2/portfolios/{portfolioId}/withdraw) accepts the signed
authorization and dispatches the onchain transfer.
Wallet addresses are exchanged as
CAIP-10 account identifiers.
recipientAccountId is always chain-bound — a withdrawal goes to one
address on one specific chain:
-
EVM:
eip155:<chainId>:<address> -
Solana:
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:<base58> -
Auth:
x-api-keyheader (required) -
Scope:
portfolios:withdraw
authorizationId (= the signed message.nonce) is the
idempotency anchor for the matching POST .../withdraw call.
The shape of the authorization depends on the withdrawal chain selected by
recipientAccountId and the listed assets:
- EVM withdrawals — stage 1 returns the full EIP-712 typed-data object at
data.typedData. Pass it directly tosignTypedData(data.typedData)in the user’s wallet, then echotypedData.messageback asbody.messageon stage 2. - Solana withdrawals — stage 1 returns
data.authorizationinstead. Sign it per itskind(see Solana withdrawals) and echoauthorization.messageback asbody.messageon stage 2.
body.message shape is identical for both chains — only the
signature encoding and where you read the message from (typedData.message
vs authorization.message) differ.
The authorization is valid for 10 minutes. If the user takes longer to
sign — or a balance check fails at stage 2 — restart the flow by calling
this endpoint again.
Chain-equivalence rule
Every asset’sassetId must be on the same chain as recipientAccountId. A
single authorization withdraws a set of assets from one chain to one
recipient on that chain. Multi-chain withdrawals require separate
authorizations per chain.
Warning
UNSUPPORTED_WITHDRAW_ASSET; redeem it first, then authorize the
underlying asset withdrawal.
Liquidate to a settlement asset
Setliquidate: true (default false) to have the listed assets swapped to
a single settlement asset and delivered to recipientAccountId, instead of
transferring each asset as-is. Both the flag and the settlement asset are part
of the signed authorization (the EIP-712 typed-data on EVM, the off-chain
digest/message on Solana), so the swap intent is bound to the user’s signature
and cannot be changed at stage 2.
Use settlementAssetId to pick the settlement asset. Its CAIP-19 chain must
match both recipientAccountId and every listed asset:
- EVM withdrawal — USDC by default; configured USDT may be selected explicitly on the same EVM chain.
- Solana withdrawal — Solana USDC only.
400 API_221 UNSUPPORTED_SETTLEMENT_ASSET.
settlementAssetId is part of the signed message only when liquidate: true; plain
transfers don’t carry it.
Assets whose value is below the tenant’s configured swap threshold are
skipped (left in the vault) — the prepared authorization covers only the
assets that clear the threshold. If none clear it, stage 1 returns
400 API_220 NOTHING_TO_LIQUIDATE. Assets already denominated in the
settlement asset are delivered directly without a swap. To liquidate an entire portfolio
without listing assets, use POST .../liquidate-all/signature.
Signed EIP-712 domain
The returneddomain binds the signature to two things:
chainId— the EVM chain the assets and recipient live on. Required for ERC-1271 smart-wallet verification, which re-hashes the typed-data using the wallet’s ownchainId.verifyingContract— the smart account being debited on that chain. Scopes the signature to a specific smart account deployment.
domain, types, primaryType, and message
verbatim to signTypedData in the user’s wallet.
Solana withdrawals
For Solana portfolios, stage 1 returnsdata.authorization (not
data.typedData). Glider’s pooled agent executes the SPL/SOL transfer out
of the portfolio’s Swig sub-account and the Glider paymaster sponsors the
fee — the user’s signature is the off-chain authorization proving the
funds’ owner approved this exact withdrawal. The authorization binds the
portfolio, the smart account being debited, the recipient, every asset and
amount, the nonce, and the expiry, so a captured signature authorizes one
withdrawal and nothing else.
authorization is a discriminated union keyed on kind, matching the
portfolio’s owner model (the same two models as enrollment):
kind: "ecdsa"— EVM-rooted (Model A). The portfolio owner is an EVM key (the Swig root authority). Signauthorization.raw(a 32-byte hash) with the owner’s EVM wallet via EIP-191personal_sign. Submit the resulting0x…hex signature on stage 2.kind: "solana-message"— Solana-rooted (Model B). The portfolio owner is a Solana key. The wallet (Phantom, Backpack, Solflare, …) rendersauthorization.textverbatim; sign its UTF-8 bytes via the wallet’ssignMessage(ed25519). Submit the resulting base58 signature on stage 2.
authorization.message back verbatim as body.message
on stage 2. recipientAccountId and every asset’s assetId must be on
Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/…). Native SOL uses the
slip44:501 asset reference; SPL tokens use spl:<mint>.
What the user sees in their wallet
When rendering the signature prompt, the wallet displays each field of the signed struct. Users seerecipientAccountId as a CAIP-10 string like
eip155:1:0x4444… — chain and address are visible together. They also see
portfolioId, assets (CAIP-19 asset ids + raw atomic amounts),
nonce, expiresAt, and liquidate — plus settlementAssetId (the settlement
asset) on a liquidation. Integrators SHOULD surface a decoded summary in
their own UI (asset symbols, human-readable amounts) so users can verify
the recipient and amounts before approving.
Common error responses:
400 API_210 INSUFFICIENT_BALANCE— live balance belowamountRawfor at least one asset.400 API_211 INVALID_RECIPIENT— zero address, or a self-transfer to the smart account / Swig sub-account being debited. The recipient may otherwise be any address the owner signs for — see the recipient note on stage 2.403 API_104 PERMISSION_DENIED— Solana withdrawal requested but thesvm_b2b_apifeature is not enabled for the tenant.400 API_212 DUPLICATE_WITHDRAW_ASSET— two or more assets inassetsshare the sameassetId.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—liquidate: true(with the default USDC settlement asset) but the recipient’s chain has no canonical USDC to swap into.400 API_220 NOTHING_TO_LIQUIDATE—liquidate: truebut no listed asset’s value clears the tenant swap threshold.400 API_221 UNSUPPORTED_SETTLEMENT_ASSET—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).400when the request body is otherwise invalid (schema errors).401whenx-api-keyheader is missing or the key is invalid.403when the API key lacks theportfolios:withdrawscope.404 API_200 PORTFOLIO_NOT_FOUND—portfolioIddoesn’t exist or belongs to a different tenant.500on unexpected server errors.