Skip to main content

Overview

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

Create withdraw action

Call POST /v1/actions/withdraw/{opportunityId} to generate the transactions.
2

Sign & submit transactions

The user signs and submits each transaction in order (e.g., approve + withdraw). No further action needed.
All action endpoints require the user to be a Turtle member. Make sure the user has completed the membership flow before calling these endpoints.

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).
curl -X POST "https://earn.turtle.xyz/v1/actions/withdraw/{opportunityId}" \
  -H "Content-Type: application/json" \
  -d '{
    "userAddress": "0x1234...",
    "amount": "1000000000000000000",
    "distributorId": "your-distributor-id",
    "tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    "slippageBps": 50
  }'
Path Parameters
opportunityId
uuid
required
The unique identifier of the opportunity to withdraw from. Get this from the Opportunities API.
Body Parameters
userAddress
string
required
The user’s EVM wallet address. Must belong to a registered Turtle member.
amount
string
required
The number of shares to redeem in the smallest unit. Must be greater than 0.
distributorId
string
required
Your distributor ID for attribution tracking.
tokenOut
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.
slippageBps
integer
Slippage tolerance in basis points. For example, 50 = 0.5% slippage. Only applied to opportunities that require it.
Response
{
  "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:
type
string
required
Transaction type, e.g. approve, deposit, withdraw, claimDeposit, cancelDeposit.
transaction
object
required
The raw transaction data to sign and submit.
transaction.to
string
required
Target contract address.
transaction.data
string
required
Encoded calldata (hex string with 0x prefix).
transaction.value
string
required
Value in wei. Usually "0" for token interactions; non-zero for native token deposits.
transaction.gasLimit
string
required
Estimated gas limit.
transaction.chainId
integer
required
Chain ID for the transaction.
description
string
required
Human-readable description of what this transaction does.
metadata
object
Optional metadata for swap transactions, including provider info, amount out, gas estimate, and route details.

Complete Example

Here’s a full withdraw flow from start to finish:
const opportunityId = '550e8400-e29b-41d4-a716-446655440000';
const walletAddress = '0x1234...';

// 1. Create the withdraw action
const withdrawResponse = await fetch(
  `https://earn.turtle.xyz/v1/actions/withdraw/${opportunityId}`,
  {
    method: 'POST',
    headers: { '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
}

Error Handling

Status Code: 403 Forbidden
{
  "error": "not_a_member",
  "message": "User is not a Turtle member. Please complete the membership flow first.",
  "docsUrl": "https://docs.turtle.club/sdk/earn-api/membership"
}
Solution: Complete the membership flow before calling action endpoints.
Status Code: 404 Not Found
{
  "error": "distributor_not_found",
  "message": "Distributor not found with the provided ID"
}
Solution: Verify your distributor ID is correct and active.
Status Code: 404 Not Found
{
  "error": "opportunity_not_found",
  "message": "Opportunity not found"
}
Solution: Check the opportunity ID using the Opportunities API.
Status Code: 400 Bad Request
{
  "error": "INVALID_ARGUMENT",
  "message": "invalid tokenOut address format"
}
Solution: Provide a valid EVM address for the tokenOut field.