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

# Withdraw from Internal Wallet

> Initiate withdrawal from custodial wallet to external blockchain address

## Overview

Withdraw funds from a custodial (internal) wallet to an external blockchain address. Specify the source wallet, destination address, amount, and currency. Withdrawals are processed on-chain and may require network confirmations.

**Use Cases:**

* Send funds to personal external wallet
* Transfer to exchanges or other platforms
* Withdraw to whitelisted addresses
* Move funds between platforms

**Prerequisites:**

* Source internal wallet must have sufficient balance
* For some platforms, destination address may need to be whitelisted
* User must have appropriate verification status

## Authentication

<ParamField header="x-client-key" type="string" required>
  Your public API client key
</ParamField>

<ParamField header="Authorization" type="string" required>
  Bearer token for authentication
</ParamField>

## Query Parameters

<ParamField query="x-us-env" type="boolean" default={false}>
  Route to US backend environment
</ParamField>

## Request Body

<ParamField body="amount" type="string" required>
  Amount to withdraw (decimal string)
</ParamField>

<ParamField body="recipientAddrss" type="string" required>
  Destination blockchain address (Note: field name has typo in API)
</ParamField>

<ParamField body="recipientMemo" type="string">
  Memo/destination tag for recipient address (required for XRP, Stellar, etc.)
</ParamField>

<ParamField body="sourceAddress" type="string" required>
  Source wallet address (obtained from `address` field in `GET /v1/wallet/internal`)
</ParamField>

<ParamField body="sourceMemo" type="string">
  Source wallet memo (obtained from `addressMemo` field in `GET /v1/wallet/internal`)
</ParamField>

<ParamField body="currency" type="string" required>
  Currency code (e.g., "xrp", "usdc", "sol")
</ParamField>

## Response

<ResponseField name="success" type="boolean">
  Whether withdrawal was initiated successfully
</ResponseField>

<ResponseExample>
  ```json 200 - Success theme={null}
  {
    "success": true
  }
  ```

  ```json 400 - Bad Request theme={null}
  {
    "message": "Insufficient balance"
  }
  ```

  ```json 401 - Authentication Error theme={null}
  {
    "message": "Not authenticated"
  }
  ```

  ```json 422 - Validation Error theme={null}
  {
    "message": "Invalid recipient address format"
  }
  ```

  ```json 500 - Internal Server Error theme={null}
  {
    "message": "Internal server error"
  }
  ```
</ResponseExample>

## Code Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://dev.api.baanx.com/v1/wallet/internal/withdraw" \
    -H "x-client-key: YOUR_CLIENT_KEY" \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "amount": "10.5",
      "recipientAddrss": "rNxp4h8apvRis6mJf9Sh8C6iRxfrDWN7AA",
      "recipientMemo": "12345",
      "sourceAddress": "rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY",
      "sourceMemo": "78",
      "currency": "xrp"
    }'
  ```

  ```python Python theme={null}
  import requests

  url = "https://dev.api.baanx.com/v1/wallet/internal/withdraw"
  headers = {
      "x-client-key": "YOUR_CLIENT_KEY",
      "Authorization": "Bearer YOUR_ACCESS_TOKEN",
      "Content-Type": "application/json"
  }

  internal_wallet_response = requests.get(
      "https://dev.api.baanx.com/v1/wallet/internal",
      headers=headers
  )
  wallets = internal_wallet_response.json()

  xrp_wallet = next(w for w in wallets if w['currency'] == 'xrp')

  withdrawal_data = {
      "amount": "10.5",
      "recipientAddrss": "rNxp4h8apvRis6mJf9Sh8C6iRxfrDWN7AA",
      "recipientMemo": "12345",
      "sourceAddress": xrp_wallet['address'],
      "sourceMemo": xrp_wallet['addressMemo'],
      "currency": "xrp"
  }

  response = requests.post(url, headers=headers, json=withdrawal_data)

  if response.status_code == 200:
      print("Withdrawal initiated successfully!")
  else:
      error = response.json()
      print(f"Error: {error['message']}")
  ```

  ```typescript TypeScript theme={null}
  interface WithdrawalRequest {
    amount: string;
    recipientAddrss: string;
    recipientMemo?: string;
    sourceAddress: string;
    sourceMemo?: string;
    currency: string;
  }

  async function withdrawFromInternal(
    sourceWallet: InternalWallet,
    recipientAddress: string,
    amount: string,
    recipientMemo?: string
  ): Promise<boolean> {
    const withdrawalData: WithdrawalRequest = {
      amount,
      recipientAddrss: recipientAddress,
      sourceAddress: sourceWallet.address,
      currency: sourceWallet.currency
    };

    if (recipientMemo) {
      withdrawalData.recipientMemo = recipientMemo;
    }

    if (sourceWallet.addressMemo) {
      withdrawalData.sourceMemo = sourceWallet.addressMemo;
    }

    const response = await fetch(
      'https://dev.api.baanx.com/v1/wallet/internal/withdraw',
      {
        method: 'POST',
        headers: {
          'x-client-key': 'YOUR_CLIENT_KEY',
          'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(withdrawalData)
      }
    );

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message);
    }

    const result = await response.json();
    return result.success;
  }
  ```
</CodeGroup>

## Complete Withdrawal Flow

<Steps>
  <Step title="Get Internal Wallets">
    Retrieve available internal wallets

    ```javascript theme={null}
    const wallets = await fetch('/v1/wallet/internal')
      .then(r => r.json());
    const xrpWallet = wallets.find(w => w.currency === 'xrp');
    ```
  </Step>

  <Step title="Validate Balance">
    Ensure sufficient balance for withdrawal

    ```javascript theme={null}
    const balance = parseFloat(xrpWallet.balance);
    const withdrawAmount = 10.5;

    if (balance < withdrawAmount) {
      throw new Error('Insufficient balance');
    }
    ```
  </Step>

  <Step title="Prepare Withdrawal Data">
    Collect all required information

    ```javascript theme={null}
    const withdrawalData = {
      amount: withdrawAmount.toString(),
      recipientAddrss: 'rNxp4h8apvRis6mJf9Sh8C6iRxfrDWN7AA',
      recipientMemo: '12345',
      sourceAddress: xrpWallet.address,
      sourceMemo: xrpWallet.addressMemo,
      currency: xrpWallet.currency
    };
    ```
  </Step>

  <Step title="Initiate Withdrawal">
    Execute the withdrawal request

    ```javascript theme={null}
    const response = await fetch('/v1/wallet/internal/withdraw', {
      method: 'POST',
      body: JSON.stringify(withdrawalData)
    });

    const result = await response.json();
    if (result.success) {
      console.log('Withdrawal initiated!');
    }
    ```
  </Step>

  <Step title="Monitor Completion">
    Check wallet history for confirmation

    ```javascript theme={null}
    setTimeout(async () => {
      const history = await fetch(
        `/v1/wallet/history?walletId=${xrpWallet.id}&walletType=INTERNAL&walletCurrency=${xrpWallet.currency}`
      ).then(r => r.json());

      const withdrawal = history.find(tx =>
        tx.sign === 'debit' &&
        tx.amount === withdrawAmount.toString()
      );

      if (withdrawal) {
        console.log('Withdrawal confirmed!');
      }
    }, 5000);
    ```
  </Step>
</Steps>

## Important Notes

<Warning>
  **Field Name Typo**: The recipient address field is named `recipientAddrss` (missing an 'e'). This is a known API quirk that must be used exactly as shown.
</Warning>

<Note>
  **Source Address Fields**: The `sourceAddress` corresponds to the `address` field and `sourceMemo` corresponds to the `addressMemo` field from the GET /v1/wallet/internal response.
</Note>

<Tip>
  **Memo Requirements**: For XRP, Stellar, and other memo-based networks, always include recipient memo if withdrawing to an exchange or custodial service. Missing memos can result in lost funds.
</Tip>

## Error Handling

### Common Errors

**Insufficient Balance**

```json theme={null}
{
  "message": "Insufficient balance for withdrawal"
}
```

Solution: Check balance before withdrawal and account for network fees.

**Invalid Address Format**

```json theme={null}
{
  "message": "Invalid recipient address format"
}
```

Solution: Validate address format for the specific blockchain before submitting.

**Missing Memo**

```json theme={null}
{
  "message": "Recipient memo required for this network"
}
```

Solution: Include `recipientMemo` for XRP, Stellar, and similar networks.

**Withdrawal Not Allowed**

```json theme={null}
{
  "message": "Withdrawals temporarily disabled"
}
```

Solution: Check platform status or contact support. May be due to maintenance or security holds.

## Address Validation

Validate addresses before withdrawal to prevent errors:

<CodeGroup>
  ```javascript Address Validation theme={null}
  function validateAddress(address, currency) {
    const patterns = {
      xrp: /^r[1-9A-HJ-NP-Za-km-z]{25,34}$/,
      eth: /^0x[a-fA-F0-9]{40}$/,
      solana: /^[1-9A-HJ-NP-Za-km-z]{32,44}$/
    };

    const pattern = patterns[currency.toLowerCase()];

    if (!pattern) {
      throw new Error(`Validation pattern not found for ${currency}`);
    }

    if (!pattern.test(address)) {
      throw new Error(`Invalid ${currency.toUpperCase()} address format`);
    }

    return true;
  }

  try {
    validateAddress('rNxp4h8apvRis6mJf9Sh8C6iRxfrDWN7AA', 'xrp');
    console.log('Address is valid');
  } catch (error) {
    console.error(error.message);
  }
  ```

  ```python Python Address Validation theme={null}
  import re

  def validate_address(address: str, currency: str) -> bool:
      patterns = {
          'xrp': r'^r[1-9A-HJ-NP-Za-km-z]{25,34}$',
          'eth': r'^0x[a-fA-F0-9]{40}$',
          'solana': r'^[1-9A-HJ-NP-Za-km-z]{32,44}$'
      }

      pattern = patterns.get(currency.lower())

      if not pattern:
          raise ValueError(f"Validation pattern not found for {currency}")

      if not re.match(pattern, address):
          raise ValueError(f"Invalid {currency.upper()} address format")

      return True

  validate_address('rNxp4h8apvRis6mJf9Sh8C6iRxfrDWN7AA', 'xrp')
  ```
</CodeGroup>

## Edge Cases

### Minimum Withdrawal Amounts

Networks may have minimum withdrawal thresholds:

```javascript theme={null}
const minimums = {
  xrp: 10,
  usdc: 1,
  eth: 0.01
};

if (parseFloat(amount) < minimums[currency]) {
  throw new Error(`Minimum withdrawal is ${minimums[currency]} ${currency.toUpperCase()}`);
}
```

### Network Fees

Withdrawal amounts should account for network fees:

```javascript theme={null}
const balance = parseFloat(wallet.balance);
const estimatedFee = 0.1;
const maxWithdrawal = balance - estimatedFee;

console.log(`Maximum withdrawal: ${maxWithdrawal} ${wallet.currency.toUpperCase()}`);
```

### Whitelist Requirements

Some configurations require whitelisted addresses:

```javascript theme={null}
const whitelistedAddresses = await fetch(
  `/v1/wallet/whitelist?currency=${currency}`
).then(r => r.json());

const isWhitelisted = whitelistedAddresses.some(
  w => w.walletAddress === recipientAddress
);

if (!isWhitelisted) {
  console.warn('Address not whitelisted. Withdrawal may fail.');
}
```

## Related Endpoints

* [Get Internal Wallets](/api-reference/wallet/internal) - List available internal wallets
* [Whitelist External Addresses](/api-reference/wallet/whitelist) - Manage approved withdrawal destinations
* [Get Wallet History](/api-reference/wallet/history) - Verify withdrawal completion
