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

# Get Merkle Proofs

> Fetch Merkle proofs for a wallet to claim token stream rewards on-chain

<Note>
  This endpoint is **permissionless**. No API key is required. Merkle proofs are public data that anyone can verify on-chain.
</Note>

## Overview

`GET /v1/streams/merkle_proofs` returns Merkle proofs for a wallet across one or more token streams. Each proof contains everything needed to call `claim()` on the stream's smart contract: the cumulative allocation amount, the proof array, the contract address, and the chain ID.

Use this endpoint when building a claim UI on a partner website or in any frontend that needs to submit on-chain claim transactions.

<Info>
  This endpoint returns proofs for **token-based streams only**. Point-based streams do not have on-chain Merkle trees.
</Info>

## Endpoint

<CodeGroup>
  ```bash curl theme={null}
  curl -X GET "https://earn.turtle.xyz/v1/streams/merkle_proofs?wallet=0x...&streamIds=7e9c407e-3992-4587-b8e7-9a30f96b12b5"
  ```

  ```typescript TypeScript theme={null}
  const wallet = '0x...';
  const streamId = '7e9c407e-3992-4587-b8e7-9a30f96b12b5';

  const response = await fetch(
    `https://earn.turtle.xyz/v1/streams/merkle_proofs?wallet=${wallet}&streamIds=${streamId}`
  );

  const data = await response.json();
  ```
</CodeGroup>

To query multiple streams, repeat the `streamIds` parameter:

<CodeGroup>
  ```bash curl theme={null}
  curl -X GET "https://earn.turtle.xyz/v1/streams/merkle_proofs?wallet=0x...&streamIds=7e9c407e-3992-4587-b8e7-9a30f96b12b5&streamIds=6ab3c09e-a384-436e-ba8b-c30d249bdf83"
  ```

  ```typescript TypeScript theme={null}
  const wallet = '0x...';
  const streamIds = [
    '7e9c407e-3992-4587-b8e7-9a30f96b12b5',
    '6ab3c09e-a384-436e-ba8b-c30d249bdf83',
  ];

  const params = new URLSearchParams({ wallet });
  streamIds.forEach((id) => params.append('streamIds', id));

  const response = await fetch(
    `https://earn.turtle.xyz/v1/streams/merkle_proofs?${params}`
  );

  const data = await response.json();
  ```
</CodeGroup>

**Query Parameters**

<ParamField query="wallet" type="string" required>
  The user's EVM wallet address.
</ParamField>

<ParamField query="streamIds" type="string[]" required>
  One or more stream UUIDs to fetch proofs for. Repeat the parameter for multiple streams. You can find your stream IDs via [Get Streams](/sdk/streams/get-streams) or from your Turtle dashboard.
</ParamField>

## Response Example

```json theme={null}
{
  "proofs": [
    {
      "streamId": "7e9c407e-3992-4587-b8e7-9a30f96b12b5",
      "chainId": 8453,
      "contractAddress": "0x...",
      "amount": "1500000000000000000000",
      "proof": [
        "0xabc123...",
        "0xdef456...",
        "0x789012..."
      ],
      "rootHash": "0x...",
      "timestamp": "2026-05-20T13:05:10Z"
    }
  ]
}
```

## Claim parameters

The response includes everything needed to call `claim()` on-chain. Three fields map directly to the contract's parameters:

| Response field | Contract parameter      | Notes                                       |
| -------------- | ----------------------- | ------------------------------------------- |
| `amount`       | `uint256 amount`        | Pass as-is (raw token units)                |
| `timestamp`    | `uint40 timestamp`      | Convert from ISO 8601 to Unix epoch seconds |
| `proof`        | `bytes32[] merkleProof` | Pass as-is                                  |

The `timestamp` identifies which Merkle root the proof was generated against. The contract validates the proof against that root. If the timestamp doesn't match a committed root, the transaction reverts. See [Claim Rewards](/sdk/streams/claim-rewards) for the full integration guide.

## Response Fields

<ResponseField name="proofs" type="StreamMerkleProof[]" required>
  Array of Merkle proofs, one per requested stream where the wallet has an allocation.
</ResponseField>

### `StreamMerkleProof`

<ResponseField name="streamId" type="uuid">
  The stream this proof belongs to.
</ResponseField>

<ResponseField name="chainId" type="integer">
  Decimal EVM chain ID where the stream contract is deployed (e.g. `8453` for Base, `1` for Ethereum).
</ResponseField>

<ResponseField name="contractAddress" type="string">
  The stream contract address to call `claim()` on.
</ResponseField>

<ResponseField name="amount" type="string">
  Total cumulative allocation in raw token units. This is the **total ever allocated** to the wallet, not the unclaimed balance. The contract tracks what has already been claimed. Call `getRewardToken()` on the stream contract to look up the token's decimals for display formatting.
</ResponseField>

<ResponseField name="timestamp" type="string">
  ISO 8601 timestamp of the Merkle root this proof was generated against. Required for the `claim()` call. The contract uses it to look up the correct root hash for verification. Must be converted to Unix epoch seconds (`uint40`) before passing to the contract.
</ResponseField>

<ResponseField name="proof" type="string[]">
  Array of `bytes32` hashes for Merkle verification, passed directly to the contract's `claim()` function.
</ResponseField>

<ResponseField name="rootHash" type="string">
  The Merkle root hash this proof was generated against. Informational only; the contract resolves the root from the `timestamp` parameter, so you don't need to pass this on-chain.
</ResponseField>

## Operational Notes

<AccordionGroup>
  <Accordion title="Cumulative amount model">
    The `amount` field is cumulative. It represents the wallet's total allocation across all snapshots, not a per-snapshot delta. The on-chain contract tracks how much has already been claimed and releases the difference when `claim()` is called.
  </Accordion>

  <Accordion title="Proof caching">
    Proofs are recomputed on each snapshot cycle. Between snapshots, the same proof data is returned. There is no need to poll this endpoint. Fetch once when the user is ready to claim.
  </Accordion>

  <Accordion title="Empty proofs array">
    If the wallet has no allocation in any of the requested streams, the `proofs` array will be empty. This is not an error. It means the wallet is not a participant in those streams.
  </Accordion>

  <Accordion title="Using proofs on-chain">
    Pass `amount`, `timestamp`, and `proof` directly to the stream contract's `claim()` function. See [Claim Rewards](/sdk/streams/claim-rewards) for the full on-chain integration guide.
  </Accordion>
</AccordionGroup>

## Error Handling

<AccordionGroup>
  <Accordion title="Missing required parameters">
    **Status Code:** 400 Bad Request

    ```json theme={null}
    {
      "error": "wallet and streamIds are required"
    }
    ```

    **Solution:** Ensure both `wallet` and at least one `streamIds` parameter are present.
  </Accordion>

  <Accordion title="Unexpected internal error">
    **Status Code:** 500 Internal Server Error

    **Solution:** Retry the request and contact Turtle if the issue persists.
  </Accordion>
</AccordionGroup>
