> ## Documentation Index
> Fetch the complete documentation index at: https://docs.glider.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Prepare Withdrawal Authorization (Stage 1)

> Stage 1 of the two-stage withdrawal flow — returns an EIP-712 typed-data envelope for the end-user to sign in their wallet.

Stage 1 of the two-stage withdrawal flow. Validates the request, checks
live onchain balances, 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 onchain transfer.

Wallet addresses are exchanged as
[CAIP-10](https://chainagnostic.org/CAIPs/caip-10) account identifiers.
`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 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'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 smart account being debited on that chain. Scopes
  the signature to a specific smart account 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, smart account 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 smart
  account 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_FOUND` — `portfolioId` doesn't exist or belongs
  to a different tenant.
* `500` on unexpected server errors.

<RequestExample>
  ```bash cURL theme={null}
  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"
        }
      ]
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://api.glider.fi/v2/portfolios/01JWZEE2MF30KVRMRX53N88VA4/withdraw/signature",
    {
      method: "POST",
      headers: {
        "x-api-key": "gldr_sk_your_api_key",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        recipientAccountId:
          "eip155:1:0x4444444444444444444444444444444444444444",
        assets: [
          {
            assetId:
              "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
            amountRaw: "1000500000",
          },
        ],
      }),
    },
  );
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "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
        }
      }
    }
  }
  ```

  ```json 400 theme={null}
  {
    "success": false,
    "error": {
      "code": "API_213",
      "message": "Asset eip155:137/erc20:0x... is not on the recipient's chain (eip155:1)"
    }
  }
  ```

  ```json 404 theme={null}
  {
    "success": false,
    "error": {
      "code": "API_200",
      "message": "Portfolio not found or not owned by tenant: 01JWZEE2MF30KVRMRX53N88VA4"
    }
  }
  ```
</ResponseExample>
