Skip to main content
POST
/
v2
/
portfolios
/
{portfolioId}
/
withdraw
/
signature
curl --request POST \
  --url 'https://api.glider.fi/v2/portfolios/01JWZEE2MF30KVRMRX53N88VA4/withdraw/signature' \
  --header 'x-api-key: gldr_sk_your_api_key' \
  --header 'Content-Type: application/json' \
  --data '{
    "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444",
    "assets": [
      {
        "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
        "amountRaw": "1000500000"
      }
    ]
  }'
{
  "success": true,
  "data": {
    "authorizationId": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
    "expiresAt": "2026-04-17T12:10:00.000Z",
    "typedData": {
      "primaryType": "Withdrawal",
      "domain": {
        "name": "Glider Withdrawal Authorization",
        "version": "1",
        "chainId": 1,
        "verifyingContract": "0x2222222222222222222222222222222222222222"
      },
      "types": {
        "Withdrawal": [
          { "name": "portfolioId", "type": "string" },
          { "name": "recipientAccountId", "type": "string" },
          { "name": "assets", "type": "WithdrawalAsset[]" },
          { "name": "nonce", "type": "bytes32" },
          { "name": "expiresAt", "type": "uint256" }
        ],
        "WithdrawalAsset": [
          { "name": "assetId", "type": "string" },
          { "name": "amountRaw", "type": "string" }
        ]
      },
      "message": {
        "portfolioId": "01JWZEE2MF30KVRMRX53N88VA4",
        "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444",
        "assets": [
          {
            "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
            "amountRaw": "1000500000"
          }
        ],
        "nonce": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
        "expiresAt": 1744898400
      }
    }
  }
}
Stage 1 of the v2 withdrawal flow. The server validates the request, checks live onchain balances, generates a nonce + 10-minute expiry, and returns an EIP-712-typed authorization message the end-user signs with their portfolio owner wallet. Stage 2 (POST /v2/portfolios/{portfolioId}/withdraw) accepts the signed envelope and dispatches the actual onchain transfer. Wallet addresses are exchanged as CAIP-10 account identifiers throughout the v2 API. recipientAccountId uses the chain-bound EVM form eip155:<chainId>:<address> — a withdrawal always goes to one address on one specific chain. Only EVM recipients are supported today.
  • Auth: x-api-key header (required)
  • Scope: portfolios:withdraw
The returned authorizationId (= the signed message.nonce) is the idempotency anchor for the matching POST .../withdraw call. Stage 1 returns the full EIP-712 typed-data object at data.typedData — pass it directly to signTypedData(data.typedData) in the user’s wallet. On stage 2 the integrator echoes back typedData.message as body.message alongside the signature. The server reconstructs the EIP-712 hash and verifies the signature recovers to the portfolio owner. The authorization is valid for 10 minutes. If the user takes longer to sign — or any balance check fails at stage 2 — the integrator must restart the flow by calling this endpoint again.

Chain-equivalence rule

Every asset’s assetId 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.

Signed EIP-712 domain

The returned domain 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 own chainId.
  • verifyingContract — the vault being debited on that chain. Scopes the signature to a specific vault deployment.
Integrators should pass domain, types, primaryType, and message verbatim to signTypedData in the user’s wallet.

What the user sees in their wallet

When rendering the signature prompt, the wallet displays each field of the signed struct. Users see recipientAccountId as a CAIP-10 string like eip155:1:0x4444… — chain and address are visible together. They also see portfolioId (ULID), assets (CAIP-19 asset ids + raw atomic amounts), nonce, and expiresAt. 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 below amountRaw for at least one asset.
  • 400 API_211 INVALID_RECIPIENT — zero address, vault self-transfer, or non-EVM namespace.
  • 400 API_212 DUPLICATE_WITHDRAW_ASSET — two or more assets in assets share the same assetId.
  • 400 API_213 WITHDRAWAL_CHAIN_MISMATCH — at least one asset is on a different chain than recipientAccountId.
  • 400 API_215 PORTFOLIO_HAS_NO_VAULT_ON_CHAIN — portfolio has no vault deployed on the recipient’s chain.
  • 400 when the request body is otherwise invalid (schema errors).
  • 401 when x-api-key header is missing or the key is invalid.
  • 403 when the API key lacks the portfolios:withdraw scope.
  • 404 API_200 PORTFOLIO_NOT_FOUNDportfolioId doesn’t exist or belongs to a different tenant.
  • 500 on unexpected server errors.
curl --request POST \
  --url 'https://api.glider.fi/v2/portfolios/01JWZEE2MF30KVRMRX53N88VA4/withdraw/signature' \
  --header 'x-api-key: gldr_sk_your_api_key' \
  --header 'Content-Type: application/json' \
  --data '{
    "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444",
    "assets": [
      {
        "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
        "amountRaw": "1000500000"
      }
    ]
  }'
{
  "success": true,
  "data": {
    "authorizationId": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
    "expiresAt": "2026-04-17T12:10:00.000Z",
    "typedData": {
      "primaryType": "Withdrawal",
      "domain": {
        "name": "Glider Withdrawal Authorization",
        "version": "1",
        "chainId": 1,
        "verifyingContract": "0x2222222222222222222222222222222222222222"
      },
      "types": {
        "Withdrawal": [
          { "name": "portfolioId", "type": "string" },
          { "name": "recipientAccountId", "type": "string" },
          { "name": "assets", "type": "WithdrawalAsset[]" },
          { "name": "nonce", "type": "bytes32" },
          { "name": "expiresAt", "type": "uint256" }
        ],
        "WithdrawalAsset": [
          { "name": "assetId", "type": "string" },
          { "name": "amountRaw", "type": "string" }
        ]
      },
      "message": {
        "portfolioId": "01JWZEE2MF30KVRMRX53N88VA4",
        "recipientAccountId": "eip155:1:0x4444444444444444444444444444444444444444",
        "assets": [
          {
            "assetId": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
            "amountRaw": "1000500000"
          }
        ],
        "nonce": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
        "expiresAt": 1744898400
      }
    }
  }
}