Skip to main content
This guide walks through the full integration flow: authenticate, register a wallet, browse opportunities, generate a deposit transaction, and verify attribution. By the end you’ll have a working deposit attributed to your distributor.

Prerequisites

Before starting, make sure you have:
  1. A Turtle organization created at dashboard.turtle.xyz — see Distribution Dashboard for a walkthrough
  2. Your API key (pk_live_ for client-side or sk_live_ for server-side) — see API Keys
  3. Your distributor ID (found in Distribution > Dashboard in the Client Portal)
New organizations require approval from the Turtle team before API keys are issued. Reach out on Discord if you need to expedite.

Step 1: Verify your API key

Test your key by fetching the opportunity catalog. If this works, authentication is set up correctly.
curl https://earn.turtle.xyz/v1/opportunities/ \
  -H "X-API-Key: pk_live_xxxxx"
You should receive a JSON response with an opportunities array. If you get a 401, double-check your API key.
See API Keys for details on key types, scopes, and rate limits.

Step 2: Register the user’s wallet

Every wallet must be a Turtle member before it can deposit. The membership flow uses EIP-4361 (Sign-In with Ethereum) for wallet verification.

2a. Check if already a member

curl "https://earn.turtle.xyz/v1/membership?address=0xYOUR_WALLET&walletEcosystem=evm" \
  -H "X-API-Key: pk_live_xxxxx"
If isMember is true, skip to Step 3.

2b. Request a signature message

curl -X POST https://earn.turtle.xyz/v1/membership/agreement \
  -H "X-API-Key: pk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "0xYOUR_WALLET",
    "walletEcosystem": "evm",
    "url": "https://yourapp.com",
    "chainId": "1"
  }'

2c. Sign the message and create membership

Have the user sign the returned message with their wallet, then submit the signature:
// Sign with the user's wallet (ethers.js example)
const signature = await signer.signMessage(message);

// Submit to create membership
const membership = await fetch('https://earn.turtle.xyz/v1/membership', {
  method: 'POST',
  headers: { 'X-API-Key': 'pk_live_xxxxx', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    address: walletAddress,
    walletEcosystem: 'evm',
    signature,
    nonce,
    distributorId: 'YOUR_DISTRIBUTOR_ID' // optional — attributes signup to your distributor
  })
});
// Response: { "isMember": true }
See Register Wallet for the full endpoint reference including Solana and TON support.

Step 3: Find an opportunity

Fetch your distributor’s curated opportunity set, or browse the full catalog with filters.
# Your curated set
curl "https://earn.turtle.xyz/v1/opportunities/distributors/YOUR_DISTRIBUTOR_ID" \
  -H "X-API-Key: pk_live_xxxxx"

# Or filter the full catalog (e.g. USDC on Ethereum with TVL > $1M)
curl "https://earn.turtle.xyz/v1/opportunities/?chainIds=1&tvlGreaterThan=1000000" \
  -H "X-API-Key: pk_live_xxxxx"
Each opportunity has an id (UUID) you’ll use in the next step, plus useful fields:
FieldWhat it tells you
estimatedAprCurrent estimated annual return
tvlTotal value locked in USD
depositTokensWhich tokens the vault accepts
transactionalProperties.depositStepsTypeinstant or complex (async)
swapDirectEnabled / swapRouteEnabledWhether direct and swap deposit modes are available
See Get Opportunities and Distributor Opportunities for full response schemas.

Step 4: Generate a deposit transaction

Call the deposit action endpoint with the opportunity ID, wallet address, token, amount, and your distributor ID. The API returns unsigned transactions ready for the user to sign.
curl -X POST "https://earn.turtle.xyz/v1/actions/deposit/OPPORTUNITY_ID" \
  -H "X-API-Key: pk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "userAddress": "0xYOUR_WALLET",
    "tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    "amount": "1000000",
    "distributorId": "YOUR_DISTRIBUTOR_ID",
    "mode": "direct"
  }'
The response contains an ordered transactions array — typically an approve transaction followed by a deposit transaction. Have the user sign and submit each one in order:
for (const tx of transactions) {
  console.log(`Signing: ${tx.description}`);
  const response = await signer.sendTransaction(tx.transaction);
  await response.wait(); // Wait for on-chain confirmation
}
The wallet must be a Turtle member (Step 2) before calling action endpoints. A non-member wallet returns 403.

Step 5: Verify attribution

After the deposit confirms on-chain, you can verify that Turtle’s attribution system picked it up correctly:
curl "https://earn.turtle.xyz/v1/actions/verify?chainId=1&txHash=0xYOUR_TX_HASH" \
  -H "X-API-Key: pk_live_xxxxx"
A successful response shows your distributorId in the metadata and signatureValid: true. Attribution is automatic via the Lumon collector — you don’t need to call any additional endpoints.

What’s next

You now have a working integration with an attributed deposit. From here:

Distributor Opportunities

Curate which opportunities your users see.

Withdraw

Generate withdrawal transactions.

Wallet Activity

Query deposit and withdrawal history by wallet.

Distributor Model

Understand attribution and revenue share.

Swap Mode

Let users deposit with any token via DEX routing.

Earn Widget SDK

Skip the API and embed a drop-in React component instead.