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

# Withdraw

> Generate ready-to-sign transactions for withdrawing from an opportunity

<Note>
  Requires an API key via the `X-API-Key` header. See [API Keys](/sdk/authentication/api-keys). {/* VERIFY: withdraw auth. The original action page carried no auth note and its examples omit the header; the public spec defines no security scheme on action endpoints. Confirm whether withdraw requires `X-API-Key` (and whether `Authorization: Bearer` is also accepted, per KB earn-api.md) before publish. */}
</Note>

## Overview

`POST /v2/actions/withdraw/{opportunityId}`

The withdraw endpoint generates transactions to redeem shares from a vault/opportunity. The typical flow is:

<Steps>
  <Step title="Create withdraw action">
    Call the endpoint to generate the transactions.
  </Step>

  <Step title="Sign & submit transactions">
    The user signs and submits each transaction in order (e.g., approve + withdraw). No further action needed.
  </Step>
</Steps>

<Info>
  All action endpoints require the user to be a [Turtle member](/sdk/authentication/register-wallet). Make sure the user has completed the membership flow before calling these endpoints.
</Info>

## Create Withdraw

Generate the transactions needed to withdraw from an opportunity. The response contains an ordered list of transactions the user must sign and submit (e.g., approve + withdraw).

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://earn.turtle.xyz/v2/actions/withdraw/{opportunityId}" \
    -H "X-API-Key: pk_live_xxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "userAddress": "0x1234...",
      "amount": "1000000000000000000",
      "distributorId": "your-distributor-id",
      "tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "slippageBps": 50
    }'
  ```

  ```typescript TypeScript theme={null}
  const opportunityId = '550e8400-e29b-41d4-a716-446655440000';

  const response = await fetch(
    `https://earn.turtle.xyz/v2/actions/withdraw/${opportunityId}`,
    {
      method: 'POST',
      headers: { 'X-API-Key': 'pk_live_xxxxx', 'Content-Type': 'application/json' },
      body: JSON.stringify({
        userAddress: '0x1234...',
        amount: '1000000000000000000', // shares to redeem in smallest unit
        distributorId: 'your-distributor-id',
        tokenOut: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
        slippageBps: 50,
      }),
    }
  );
  const data = await response.json();
  ```
</CodeGroup>

**Path Parameters**

<ParamField path="opportunityId" type="uuid" required>
  The unique identifier of the opportunity to withdraw from. Get this from the [Opportunities API](/sdk/opportunities/get-opportunities).
</ParamField>

**Body Parameters**

<ParamField body="userAddress" type="string" required>
  The user's EVM wallet address. Must belong to a registered Turtle member.
</ParamField>

<ParamField body="amount" type="string" required>
  The number of shares to redeem in the smallest unit. Must be greater than 0.
</ParamField>

<ParamField body="distributorId" type="string" required>
  Your distributor ID for attribution tracking.
</ParamField>

<ParamField body="tokenOut" type="string">
  The address of the token to receive after withdrawal. Required for some providers (e.g., Midas, Mellow, Veda). If not provided, the vault's default withdraw token is used.
</ParamField>

<ParamField body="slippageBps" type="integer">
  Slippage tolerance in basis points. For example, `50` = 0.5% slippage. Only applied to opportunities that require it.
</ParamField>

**Response**

```json theme={null}
{
  "actionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "transactions": [
    {
      "type": "approve",
      "transaction": {
        "to": "0x...",
        "data": "0x095ea7b3000000000000000000000000...",
        "value": "0",
        "gasLimit": "60000",
        "chainId": 1
      },
      "description": "Approve shares spending"
    },
    {
      "type": "withdraw",
      "transaction": {
        "to": "0x...",
        "data": "0x...",
        "value": "0",
        "gasLimit": "300000",
        "chainId": 1
      },
      "description": "Withdraw from vault"
    }
  ]
}
```

### Transaction Object

Each transaction in the `transactions` array contains:

<ResponseField name="type" type="string" required>
  Transaction type, e.g. `approve`, `deposit`, `withdraw`, `claimDeposit`, `cancelDeposit`.
</ResponseField>

<ResponseField name="transaction" type="object" required>
  The raw transaction data to sign and submit.
</ResponseField>

<ResponseField name="transaction.to" type="string" required>
  Target contract address.
</ResponseField>

<ResponseField name="transaction.data" type="string" required>
  Encoded calldata (hex string with `0x` prefix).
</ResponseField>

<ResponseField name="transaction.value" type="string" required>
  Value in wei. Usually `"0"` for token interactions; non-zero for native token deposits.
</ResponseField>

<ResponseField name="transaction.gasLimit" type="string" required>
  Estimated gas limit.
</ResponseField>

<ResponseField name="transaction.chainId" type="integer" required>
  Chain ID for the transaction.
</ResponseField>

<ResponseField name="description" type="string" required>
  Human-readable description of what this transaction does.
</ResponseField>

<ResponseField name="metadata" type="object">
  Optional metadata for swap transactions, including provider info, amount out, gas estimate, and route details.
</ResponseField>

## Complete Example

Here's a full withdraw flow from start to finish:

```typescript theme={null}
const opportunityId = '550e8400-e29b-41d4-a716-446655440000';
const walletAddress = '0x1234...';

// 1. Create the withdraw action
const withdrawResponse = await fetch(
  `https://earn.turtle.xyz/v2/actions/withdraw/${opportunityId}`,
  {
    method: 'POST',
    headers: { 'X-API-Key': 'pk_live_xxxxx', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      userAddress: walletAddress,
      amount: '1000000000000000000', // shares to redeem
      distributorId: 'your-distributor-id',
      tokenOut: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // receive USDC
    }),
  }
);
const { transactions } = await withdrawResponse.json();

// 2. Sign and submit each transaction in order
for (const tx of transactions) {
  const txResponse = await wallet.sendTransaction(tx.transaction);
  await txResponse.wait(); // Wait for confirmation
}
```

## Operational Notes

<AccordionGroup>
  <Accordion title="Amount is in shares, in the smallest unit">
    `amount` is the number of vault shares to redeem, expressed in the share token's smallest unit. Pass the raw integer string. Never send a human-readable decimal.
  </Accordion>

  <Accordion title="Submit transactions in order">
    The `transactions` array is ordered. A withdrawal typically returns an `approve` then a `withdraw`. Confirm each before broadcasting the next.
  </Accordion>

  <Accordion title="tokenOut is required for some providers">
    `tokenOut` sets the token the user receives. It is required for some providers (e.g., Midas, Mellow, Veda). If not provided, the vault's default withdraw token is used.
  </Accordion>

  <Accordion title="slippageBps applies where required">
    `slippageBps` is the slippage tolerance in basis points (`50` = 0.5%). It is only applied to opportunities that require it.
  </Accordion>

  <Accordion title="Membership is required">
    The user must be a registered Turtle member. Complete the [membership flow](/sdk/authentication/register-wallet) first.
  </Accordion>
</AccordionGroup>

## Error Handling

<AccordionGroup>
  <Accordion title="User is not a Turtle member">
    **Status Code:** 403 Forbidden

    ```json theme={null}
    {
      "error": "not_a_member",
      "message": "User is not a Turtle member. Please complete the membership flow first.",
      "docsUrl": "https://docs.turtle.xyz/sdk/authentication/register-wallet"
    }
    ```

    **Solution:** Complete the [membership flow](/sdk/authentication/register-wallet) before calling action endpoints.
  </Accordion>

  <Accordion title="Distributor not found">
    **Status Code:** 404 Not Found

    ```json theme={null}
    {
      "error": "distributor_not_found",
      "message": "Distributor not found with the provided ID"
    }
    ```

    **Solution:** Verify your distributor ID is correct and active.
  </Accordion>

  <Accordion title="Opportunity not found">
    **Status Code:** 404 Not Found

    ```json theme={null}
    {
      "error": "opportunity_not_found",
      "message": "Opportunity not found"
    }
    ```

    **Solution:** Check the opportunity ID using the [Opportunities API](/sdk/opportunities/get-opportunities).
  </Accordion>

  <Accordion title="Invalid tokenOut address">
    **Status Code:** 400 Bad Request

    ```json theme={null}
    {
      "error": "INVALID_ARGUMENT",
      "message": "invalid tokenOut address format"
    }
    ```

    **Solution:** Provide a valid EVM address for the `tokenOut` field.
  </Accordion>
</AccordionGroup>

## Related Endpoints

* [Deposit](/sdk/earn/deposit) - Generate deposit transactions
* [Get Opportunities](/sdk/opportunities/get-opportunities) - Discover available opportunities and their IDs
