Skip to main content
Requires an API key via the X-API-Key header. See API Keys.
The user must be a registered Turtle member before you can deposit on their behalf. A non-member request fails (see Error Handling).
Depositing is three moves: build the deposit, have the user broadcast the returned transactions in order, then attribution lands on its own. Direct and swap deposits use the same endpoint and differ only by the mode field. For the concept behind direct vs swap and instant vs async, see Deposit modes.

Overview

POST /v1/actions/deposit/{opportunityId} The endpoint returns an actionId and an ordered transactions array. The user signs and submits each transaction in sequence (for an ERC-20 deposit, typically an approve followed by a deposit).

Build the deposit

Pass the user’s address, the input token, the amount in the token’s smallest unit, and your distributorId. Use mode: "direct" to deposit the vault’s native token.
curl -X POST "https://earn.turtle.xyz/v1/actions/deposit/{opportunityId}" \
  -H "X-API-Key: pk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "userAddress": "0x1234...",
    "tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    "amount": "1000000",
    "distributorId": "your-distributor-id",
    "mode": "direct",
    "slippageBps": 50
  }'
Path Parameters
opportunityId
uuid
required
The opportunity to deposit into. Get it from Get Opportunities. IDs are UUIDs.
Body Parameters
userAddress
string
required
The user’s EVM wallet address. Must belong to a registered Turtle member.
tokenIn
string
required
Address of the token being deposited. Must be supported on the opportunity’s chain. Cannot be the vault’s receipt token.
amount
string
required
Deposit amount in the token’s smallest unit (wei). Must be greater than 0.
distributorId
string
required
Your distributor ID. Embedded into the deposit calldata for automatic attribution.
mode
string
default:"direct"
direct deposits the vault’s native token. swap routes a different input token through a DEX first. See the swap-mode section below.
slippageBps
integer
default:"50"
Maximum acceptable slippage in basis points, applied in swap mode. 50 = 0.5%.
referralCode
string
Optional referral code for deposit attribution.
Response
{
  "actionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "transactions": [
    {
      "type": "approve",
      "transaction": {
        "to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        "data": "0x095ea7b3000000000000000000000000...",
        "value": "0",
        "gasLimit": "60000",
        "chainId": 1
      },
      "description": "Approve USDC spending"
    },
    {
      "type": "deposit",
      "transaction": {
        "to": "0x...",
        "data": "0x...",
        "value": "0",
        "gasLimit": "250000",
        "chainId": 1
      },
      "description": "Deposit USDC into 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.

Swap mode

When the user wants to deposit a token that is not the vault’s native deposit token, set mode to swap and pass that token as tokenIn. The API routes the swap through a DEX before depositing. Swap mode is available when swapRouteEnabled is true on the opportunity. See Deposit modes for the full availability matrix.
curl -X POST "https://earn.turtle.xyz/v1/actions/deposit/{opportunityId}" \
  -H "X-API-Key: pk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "userAddress": "0x1234...",
    "tokenIn": "0xInputTokenAddress",
    "amount": "1000000",
    "distributorId": "your-distributor-id",
    "mode": "swap",
    "slippageBps": 100
  }'
The swap-mode response carries swap details in each transaction’s metadata (provider, amount out, route). The rest of the broadcast flow is identical.

Broadcast

Sign and submit each transaction in the returned order. Wait for each to confirm before sending the next, since a later transaction often depends on an earlier one (a deposit cannot land before its approve).
// 1. Build the deposit action
const depositResponse = await fetch(
  `https://earn.turtle.xyz/v1/actions/deposit/${opportunityId}`,
  {
    method: 'POST',
    headers: { 'X-API-Key': 'pk_live_xxxxx', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      userAddress: walletAddress,
      tokenIn: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
      amount: '1000000000', // 1000 USDC
      distributorId: 'your-distributor-id',
      mode: 'direct',
    }),
  }
);
const { actionId, transactions } = await depositResponse.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 on-chain confirmation before the next
}

// 3. Attribution is automatic. Turtle detects the tracking signature in
//    the deposit calldata and attributes it to your distributor.

Verify the deposit

Attribution is automatic, so there is nothing to call to record it. If you want to confirm a specific deposit carried your tracking data, pass its transaction hash to the verify endpoint. That step has its own page: Verify Attribution. If the opportunity is async (complex settlement), the deposit will sit pending until you claim it. Handle that on Async Deposits.

Operational Notes

amount is in wei for the input token’s decimals. 1 USDC (6 decimals) is "1000000"; 1 DAI (18 decimals) is "1000000000000000000". Pass the raw integer string. Never send a human-readable decimal.
The transactions array is ordered. An ERC-20 deposit returns an approve then a deposit. Confirm each before broadcasting the next.
mode: "swap" only works when the opportunity has swapRouteEnabled: true. Read availability off the opportunity object. See Deposit modes.
For opportunities with complex settlement (for example Mellow, Lagoon), the deposit queues and is not active until claimed. See Async Deposits.
The user must be a registered Turtle member. Complete the membership flow first.

Error Handling

Status: 403 Forbidden
{
  "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 before depositing.
Status: 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: 404 Not Found
{
  "error": "opportunity_not_found",
  "message": "Opportunity not found"
}
Solution: Check the opportunity ID with Get Opportunities.
Status: 400 Bad Request
{
  "error": "deposits_disabled",
  "message": "Deposits are disabled for this opportunity"
}
Solution: Deposits are temporarily disabled for this opportunity. Try a different one.
Status: 400 Bad Request
{
  "error": "invalid_token",
  "message": "Token 0x... not supported for chain 1"
}
Solution: Use a supported deposit token. Check the opportunity’s depositTokens array.

Async deposits

Claim or cancel a queued deposit.

Verify attribution

Confirm a deposit carries your tracking data.

Withdraw

Redeem shares from a position.

Find opportunities

Discover vaults and their IDs.