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

# Withdrawals

> Withdrawing USDC from an Ondo perps account to the connected wallet

An Ondo withdrawal moves USDC from the perps margin wallet to the connected wallet address. The provider plugin's `SDK` signer HMAC-signs the request with the client-held API key, and the backend relays it to `POST /v1/withdraw`. The wallet is not prompted for the withdrawal itself. The API key needs the `transfer` scope. See [Signing Model — The API key](/providers/ondo/signing-model#the-api-key).

## Withdrawal setup

Ondo sends a withdrawal only to an address in its address book. The SDK offers one destination: the address that logged in. Call `getWithdrawFlow()` to see if a setup step is outstanding.

```typescript theme={null}
const flow = await perps.getWithdrawFlow({
  provider: 'ondo',
  address: userAddress,
});

if (flow?.kind === 'setupRequired') {
  // flow.setup lists ActionType values in execution order
}
if (flow?.kind === 'ready') {
  // flow.destination is the checksummed login address
}
```

The result depends on the account state:

| State | Result |
| - | - |
| No session | `setupRequired` with `siweLogin`, then `addWithdrawalAddress` |
| Session, address not in the address book | `setupRequired` with `addWithdrawalAddress` |
| Session, address in the address book | `ready` with `destination` |

`addWithdrawalAddress` takes no params. It is a `session` step with signer `USER`. The venue issues a challenge and the user's wallet signs it, so a wallet prompt appears. The SDK adds the address with the label `LI.FI`. See [Signing Model](/providers/ondo/signing-model#client-only-session-steps).

## Discover the withdrawable balance

Call `getWithdrawableBalances()` before you show a withdrawal form.

```typescript theme={null}
const rows = await perps.getWithdrawableBalances({
  provider: 'ondo',
  address: userAddress,
});
```

Ondo returns at most one row. The row has `route: 'perps'` and the USDC asset. The `available` field is the `withdrawableMargin` value of the venue balance. Ondo returns no row when `withdrawableMargin` is zero. Without a session, the read returns an empty array.

The `withdrawalFee` field is the `withdrawalFeeUSD` value of the venue account. Ondo charges the fee in USD. The collateral is USDC, so the SDK reports the fee as the same number in the units of the row. An amount at or below the fee delivers nothing.

## Submit a withdrawal

| | |
| - | - |
| **Signing method** | `hmac` |
| **Signer** | `SDK` |
| **Params** | `WithdrawalParams` — `{ destination, amount }` |
| **Asset** | USDC only |
| **Route** | `perps` only |
| **Destination** | The connected wallet address only |
| **SDK methods** | `withdraw()` |

```typescript theme={null}
const { results } = await perps.withdraw({
  provider: 'ondo',
  address: userAddress,
  withdrawal: {
    destination: userAddress,
    amount: rows[0].available,
  },
});
```

`amount` is a human-readable USDC string. The SDK reads your Ondo `accountId` from the stored session and adds it to the request. You do not pass it. The backend rejects any other destination, asset, or route with a validation error. The network is always Ethereum.

## Withdrawal history

`getActivity()` returns a `WITHDRAWAL` item for each withdrawal that the venue did not fail or cancel. The item has no status field. It carries the amount, the asset, and the `fee` in `USD` when the venue reports one. It has an `explorerLink` when the venue reports a transaction.

The SDK includes a withdrawal row when the venue status is `WITHDRAWAL_PENDING`, `WITHDRAWAL_SUCCESS`, `complete`, `pending`, or `unknown`. The SDK omits a row with status `failure` or `cancelled`, because no value left the account.

## Errors

| Code | Cause | Action |
| - | - | - |
| `SetupRequired` (2070) | The address is not in the address book | Run `addWithdrawalAddress`, then submit the withdrawal again |
| `Unauthorized` (2013) | The API key is not found or lacks the `transfer` scope | Submit the withdrawal again. The SDK replaces the key |
| `InsufficientBalance` (2022) | The margin balance is too low | Reduce the amount |
| `ValidationError` (2002) | The amount is below the minimum, not positive, or has too many decimal places | Correct the amount |
| `ValidationError` (2002) | The address is rejected, or is an Ondo deposit address | Use the connected wallet address |
| `ExchangeRejected` (2020) | The amount is above the withdrawal limit, or the venue could not complete the withdrawal | Reduce the amount, or try again later |
| `ThirdPartyError` (2004) | The venue cannot process withdrawals now | Read the withdrawal history, then try again later |
| `NonceAlreadyUsed` (2041) | The venue already received a withdrawal with this id | Read the withdrawal history before you submit again |
| `FeatureUnavailable` (2080) | The venue disabled the feature | Do not offer the withdrawal until the venue enables it |

See [Error Codes](/error-codes) for the full code list.
