> ## 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.

# Quickstart

> Go from zero to your first attributed deposit using the Turtle Earn API.

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](https://dashboard.turtle.xyz) — see [Distribution Dashboard](/partner-products/distribution-dashboard) for a walkthrough
2. Your **API key** (`pk_live_` for client-side or `sk_live_` for server-side) — see [API Keys](/sdk/authentication/api-keys)
3. Your **distributor ID** (found in **Distribution > Dashboard** in the [Client Portal](https://dashboard.turtle.xyz))

<Note>
  New organizations require approval from the Turtle team before API keys are issued. Reach out on [Discord](https://discord.turtle.xyz) if you need to expedite.
</Note>

## Step 1: Verify your API key

Test your key by fetching the opportunity catalog. If this works, authentication is set up correctly.

<CodeGroup>
  ```bash curl theme={null}
  curl https://earn.turtle.xyz/v1/opportunities/ \
    -H "X-API-Key: pk_live_xxxxx"
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch('https://earn.turtle.xyz/v1/opportunities/', {
    headers: { 'X-API-Key': 'pk_live_xxxxx' }
  });
  const { opportunities, total } = await res.json();
  console.log(`${total} opportunities available`);
  ```
</CodeGroup>

You should receive a JSON response with an `opportunities` array. If you get a `401`, double-check your API key.

<Info>
  See [API Keys](/sdk/authentication/api-keys) for details on key types, scopes, and rate limits.
</Info>

## Step 2: Register the user's wallet

Every wallet must be a Turtle member before it can deposit. The membership flow uses [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) (Sign-In with Ethereum) for wallet verification.

### 2a. Check if already a member

<CodeGroup>
  ```bash curl theme={null}
  curl "https://earn.turtle.xyz/v1/membership?address=0xYOUR_WALLET&walletEcosystem=evm" \
    -H "X-API-Key: pk_live_xxxxx"
  ```

  ```typescript TypeScript theme={null}
  const check = await fetch(
    `https://earn.turtle.xyz/v1/membership?address=${walletAddress}&walletEcosystem=evm`,
    { headers: { 'X-API-Key': 'pk_live_xxxxx' } }
  );
  const { isMember } = await check.json();
  ```
</CodeGroup>

If `isMember` is `true`, skip to Step 3.

### 2b. Request a signature message

<CodeGroup>
  ```bash curl theme={null}
  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"
    }'
  ```

  ```typescript TypeScript theme={null}
  const agreement = await fetch('https://earn.turtle.xyz/v1/membership/agreement', {
    method: 'POST',
    headers: { 'X-API-Key': 'pk_live_xxxxx', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      address: walletAddress,
      walletEcosystem: 'evm',
      url: 'https://yourapp.com',
      chainId: '1'
    })
  });
  const { nonce, message } = await agreement.json();
  ```
</CodeGroup>

### 2c. Sign the message and create membership

Have the user sign the returned `message` with their wallet, then submit the signature:

```typescript theme={null}
// 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 }
```

<Info>
  See [Register Wallet](/sdk/authentication/register-wallet) for the full endpoint reference including Solana and TON support.
</Info>

## Step 3: Find an opportunity

Fetch your distributor's curated opportunity set, or browse the full catalog with filters.

<CodeGroup>
  ```bash curl theme={null}
  # 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"
  ```

  ```typescript TypeScript theme={null}
  // Your curated set
  const opps = await fetch(
    `https://earn.turtle.xyz/v1/opportunities/distributors/${distributorId}`,
    { headers: { 'X-API-Key': 'pk_live_xxxxx' } }
  );
  const { opportunities } = await opps.json();

  // Pick an opportunity
  const opportunity = opportunities[0];
  console.log(`${opportunity.name} — ${opportunity.estimatedApr}% APR, $${opportunity.tvl} TVL`);
  ```
</CodeGroup>

Each opportunity has an `id` (UUID) you'll use in the next step, plus useful fields:

| Field                                      | What it tells you                                   |
| ------------------------------------------ | --------------------------------------------------- |
| `estimatedApr`                             | Current estimated annual return                     |
| `tvl`                                      | Total value locked in USD                           |
| `depositTokens`                            | Which tokens the vault accepts                      |
| `transactionalProperties.depositStepsType` | `instant` or `complex` (async)                      |
| `swapDirectEnabled` / `swapRouteEnabled`   | Whether direct and swap deposit modes are available |

<Info>
  See [Get Opportunities](/sdk/opportunities/get-opportunities) and [Distributor Opportunities](/sdk/opportunities/distributor-opportunities) for full response schemas.
</Info>

## 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.

<CodeGroup>
  ```bash curl theme={null}
  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"
    }'
  ```

  ```typescript TypeScript theme={null}
  const deposit = await fetch(
    `https://earn.turtle.xyz/v1/actions/deposit/${opportunity.id}`,
    {
      method: 'POST',
      headers: { 'X-API-Key': 'pk_live_xxxxx', 'Content-Type': 'application/json' },
      body: JSON.stringify({
        userAddress: walletAddress,
        tokenIn: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
        amount: '1000000', // 1 USDC (6 decimals)
        distributorId: 'YOUR_DISTRIBUTOR_ID',
        mode: 'direct'
      })
    }
  );
  const { actionId, transactions } = await deposit.json();
  ```
</CodeGroup>

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:

```typescript theme={null}
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
}
```

<Warning>
  The wallet must be a Turtle member (Step 2) before calling action endpoints. A non-member wallet returns `403`.
</Warning>

## Step 5: Verify attribution

After the deposit confirms on-chain, you can verify that Turtle's attribution system picked it up correctly:

<CodeGroup>
  ```bash curl theme={null}
  curl "https://earn.turtle.xyz/v1/actions/verify?chainId=1&txHash=0xYOUR_TX_HASH" \
    -H "X-API-Key: pk_live_xxxxx"
  ```

  ```typescript TypeScript theme={null}
  const verify = await fetch(
    `https://earn.turtle.xyz/v1/actions/verify?chainId=1&txHash=${txHash}`,
    { headers: { 'X-API-Key': 'pk_live_xxxxx' } }
  );
  const { metadata, signatureValid } = await verify.json();
  console.log(`Attributed to: ${metadata.distributorId}, valid: ${signatureValid}`);
  ```
</CodeGroup>

A successful response shows your `distributorId` in the metadata and `signatureValid: true`. Attribution is automatic — you don't need to call any additional endpoints.

## What's next

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

<CardGroup cols={2}>
  <Card title="Distributor Opportunities" icon="filter" href="/sdk/opportunities/distributor-opportunities">
    Curate which opportunities your users see.
  </Card>

  <Card title="Withdraw" icon="arrow-up-from-bracket" href="/sdk/earn-api/actions/withdraw">
    Generate withdrawal transactions.
  </Card>

  <Card title="Wallet Activity" icon="clock-rotate-left" href="/sdk/portfolio/activity">
    Query deposit and withdrawal history by wallet.
  </Card>

  <Card title="Distributor Model" icon="sitemap" href="/sdk/earn-api/distributor-model">
    Understand attribution and revenue share.
  </Card>

  <Card title="Swap Mode" icon="arrows-rotate" href="/sdk/earn-api/swap-mode">
    Let users deposit with any token via DEX routing.
  </Card>
</CardGroup>
