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

# Withdrawals

> Move funds out of an exchange with the prepare, sign, execute flow

Withdrawals take funds off a venue and back to an EVM wallet. Aster, Hyperliquid, Lighter, Robinhood, and Ondo share one prepare/sign/execute flow; Extended has [its own quote-based flow](/docs/withdraw-extended).

<Info>
  **Prerequisites:** an active VOOI API token and the source venue connected. See [API Tokens](/docs/tokens) and [Use your own keys](/docs/exchanges) if you don't have them yet.
</Info>

<Note>
  **Binance, Bybit, and MEXC expose no withdrawals through VOOI.** Move funds out of those venues directly.
</Note>

## 1. Prepare

[`POST /withdraw/prepare`](/docs/api-reference/withdraw/prepare-a-withdraw-from-an-exchange) takes the `exchange`, the `amount`, and the `address` to withdraw to. Two optional fields are venue-specific:

* `chainId` — the destination chain. Only **Aster** and **Ondo** offer a real choice; both default to Arbitrum One (`42161`).
* `symbol` — the token to withdraw. Only **Aster** offers a real choice; it defaults to USDC.

```bash theme={null}
curl -X POST https://perps-api.vooi.io/withdraw/prepare \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "exchange": "hyperliquid", "amount": "25", "address": "0xYourWalletAddress" }'
```

Every other venue withdraws to exactly one chain and token, listed in [Destination chains and tokens](#destination-chains-and-tokens) below. Passing a value a venue doesn't support is rejected rather than quietly ignored.

The response always carries a `withdrawId` to pass to the execute step, a `fee`, a `withdrawLimit`, and a `signData` whose shape depends on the venue:

| Venue | `signData` | Sign it as |
| - | - | - |
| **Hyperliquid** | EIP-712 typed data | EIP-712 signature from your main wallet |
| **Aster** | EIP-712 typed data | EIP-712 signature from your main wallet |
| **Lighter** / **Robinhood** | A plain string | A plain personal-sign message — **not** EIP-712 |
| **Ondo** | `null` | Nothing to sign; pass `"0x"` as the signature |

<Note>
  `withdrawLimit` is the maximum you can withdraw right now, and it is not the same as your balance. On Lighter and Robinhood it is the **fast** withdrawal ceiling. On Ondo it is your withdrawable margin minus the fee — Ondo debits the fee on top of the amount, not out of it — capped by whatever remains of the period limit.
</Note>

## 2. Sign

Sign whatever `prepare` returned, with the wallet that owns the account.

<CodeGroup>
  ```typescript EIP-712 (Hyperliquid, Aster) theme={null}
  const signature = await account.signTypedData({
    domain: signData.domain,
    types: signData.types,
    primaryType: signData.primaryType,
    message: signData.message,
  });
  ```

  ```typescript Plain message (Lighter, Robinhood) theme={null}
  const signature = await account.signMessage({ message: signData });
  ```

  ```typescript No signature (Ondo) theme={null}
  // prepare returned signData: null — the destination address is already
  // registered with the venue, so there is nothing to sign.
  const signature = '0x';
  ```
</CodeGroup>

## 3. Execute

[`POST /withdraw/execute`](/docs/api-reference/withdraw/execute-a-prepared-withdraw) takes the `withdrawId` from step 1, the `signature` from step 2, and the `address` executing the withdrawal. VOOI submits it to the venue — you don't broadcast anything on-chain.

```bash theme={null}
curl -X POST https://perps-api.vooi.io/withdraw/execute \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "withdrawId": "...", "signature": "0x...", "address": "0xYourWalletAddress" }'
```

<Note>
  On Ondo the `address` must match the one the withdrawal was prepared for — a mismatch is rejected. Prepare a fresh withdrawal rather than reusing a `withdrawId` for a different destination.
</Note>

## Destination chains and tokens

| Venue | Chains | Tokens |
| - | - | - |
| Hyperliquid | Arbitrum One (`42161`) | USDC |
| Lighter | Arbitrum One (`42161`) | USDC |
| Robinhood | Chain `4663` | USDG |
| Aster | Ethereum (`1`), BNB Chain (`56`), Arbitrum One (`42161`) | `USDC`, `USDT`, `USDC.e`, `USD1`, `USDF`, `lisUSD` |
| Ondo | Ethereum (`1`), Arbitrum One (`42161`) | USDC |

Robinhood settles in USDG on its own chain rather than USDC on Arbitrum, matching how it takes [deposits](/docs/deposits). An unsupported `chainId` or `symbol` is rejected with a message naming what the venue does support — see [Errors](/docs/errors).

## Tracking the result

There is no withdrawal-status endpoint. Confirm the debit the same way you confirm a deposit credit: watch the venue balance on [`GET /exchange/accounts`](/docs/api-reference/exchange/get-account-balances), or subscribe to the `accounts` event on the [updates stream](/docs/trading-streams). Arrival in your wallet depends on the destination chain.

## Next steps

<CardGroup cols={2}>
  <Card title="Extended withdrawals" icon="arrow-up" href="/docs/withdraw-extended">
    The separate quote-based flow for Extended
  </Card>

  <Card title="Transfers between exchanges" icon="right-left" href="/docs/transfers">
    Move funds between venues without going to a wallet
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.