> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.umbraprivacy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Merkle Proof

> Generate a Merkle inclusion proof for a specific leaf in a Merkle tree.

Returns an authentication path of **20 sibling hashes** — one per level — ordered from the leaf level (index 0) up to the root level (index 19). All hash values are little-endian 64-character hex strings (32 bytes each).

<Warning>
  Merkle proofs become **stale** when new leaves are inserted into the tree (because the root changes). The V18 burner factories fetch a per-batch proof immediately before submitting the burn, so simply re-run the burner on a stale-root failure — it refetches the proof. Never cache proofs across operations.
</Warning>

## Verifying a Proof

Hash the target leaf with each sibling in order and compare the final result to the tree's current root hash (fetch the root via [Tree Metadata](/indexer/api-reference/tree-metadata)).

## Usage in the SDK

The Umbra SDK does **not** use this single-proof endpoint when burning. Burner factories use the [batch endpoint](/indexer/api-reference/batch-proofs) so all proofs in a burn batch share the same root. The single-proof endpoint exists for analytics, debugging, and custom integrations.

## Performance

Proof generation traverses all 20 tree levels. Under normal conditions, proofs are generated in under 100 ms. Operations exceeding 500 ms emit a slow-operation warning server-side but still return a valid proof.


## OpenAPI

````yaml GET /v1/trees/{tree_index}/proof/{insertion_index}
openapi: 3.0.3
info:
  title: Umbra Indexer Read Service
  description: >
    Read-only REST API for querying Umbra mixer tree state, UTXO records,

    and Merkle inclusion proofs.


    ## Response Encoding


    **All endpoints** support `application/x-protobuf` encoding. The encoding

    strategy differs by endpoint category:


    - **Always Protobuf** (no content negotiation): stats, tree metadata, Merkle
    proofs,
      and all UTXO data endpoints. These endpoints always respond with
      `Content-Type: application/x-protobuf` regardless of the `Accept` header.
    - **Content negotiation** (health endpoints only): respond with JSON by
    default,
      or Protobuf when `Accept: application/x-protobuf` is set. Send
      `Accept: application/x-protobuf` for consistent protobuf-only clients.

    ## UTXO Response Layouts


    The three UTXO data endpoints (`GET /v1/utxos`, `GET
    /v1/utxos/{absolute_index}`,

    `GET /v1/trees/{tree_index}/utxos`) support two Protobuf response layouts

    controlled by the `X-Response-Layout` request header:


    - **Row-oriented** (default) -- `UtxoResponse` message: each UTXO is a
      self-contained `UtxoDataItem` sub-message. Easier to iterate record-by-record.
    - **Columnar** (`X-Response-Layout: columnar`) -- `UtxoColumnarResponse`
    message:
      each field across all UTXOs is packed into a parallel array inside a single
      `UtxoColumns` sub-message. Compresses significantly better over the wire and is
      preferred by vectorized consumers (e.g. data pipelines, analytics).

    ## Rate Limiting


    All endpoints are subject to rate limiting. Exceeded limits return `429 Too
    Many Requests`.


    ## Compression


    All responses are compressed. Send `Accept-Encoding: gzip, br`.


    ## Absolute Index


    The **absolute index** is a globally monotonic cursor across all Merkle
    trees:

    ```

    absolute_index = tree_index * MAX_LEAVES_PER_TREE + insertion_index

    ```

    where `MAX_LEAVES_PER_TREE = 1,048,576` (2^20).
  version: 0.1.0
  contact:
    name: Umbra Protocol
servers:
  - url: https://utxo-indexer.api.umbraprivacy.com
    description: Mainnet
  - url: https://utxo-indexer.api-devnet.umbraprivacy.com
    description: Devnet
security: []
tags:
  - name: health
    description: |
      Health and readiness probes for Kubernetes or load-balancer checks.
      Support both JSON and Protobuf via `Accept` header negotiation.
  - name: stats
    description: Aggregate statistics for the UTXO index. Always Protobuf.
  - name: trees
    description: Per-tree Merkle metadata and Merkle inclusion proofs. Always Protobuf.
  - name: utxos
    description: >
      UTXO data queries with absolute-index-based pagination. Always Protobuf.

      Supports row-oriented and columnar response layouts via
      `X-Response-Layout`.
paths:
  /v1/trees/{tree_index}/proof/{insertion_index}:
    get:
      tags:
        - trees
      summary: Merkle inclusion proof
      description: >
        Generates and returns a Merkle inclusion proof (authentication path) for
        the

        leaf at `insertion_index` in the specified tree.


        **Always Protobuf** -- responds with `application/x-protobuf` regardless

        of the `Accept` header.


        ## Proof structure


        The proof is an ordered list of 20 sibling hashes (one per level of the

        depth-20 tree), ordered from leaf level (index 0) to root level (index
        19).

        Together with `leaf` and `root`, it can be used to verify membership and

        construct a Groth16 zero-knowledge proof compatible with the on-chain

        Umbra verifier.


        ## Hash encoding


        All hash values (`root`, `leaf`, each `proof` element) are
        **little-endian

        64-character hex strings** (32 bytes), matching Solana on-chain byte
        order

        and Circom ZK circuit input format.


        ## Performance


        Proof generation traverses 20 tree levels. Operations exceeding 500 ms
        emit

        a slow-operation `WARN` log server-side.
      operationId: getMerkleProof
      parameters:
        - name: tree_index
          in: path
          required: true
          description: Zero-based index of the Merkle tree to query.
          schema:
            type: integer
            format: int64
            minimum: 0
          example: 0
        - name: insertion_index
          in: path
          required: true
          description: |
            Zero-based leaf position for which to generate the proof. Must be
            strictly less than the tree's current `num_leaves`.
          schema:
            type: integer
            format: int64
            minimum: 0
          example: 42
      responses:
        '200':
          description: Merkle proof generated successfully.
          content:
            application/x-protobuf:
              schema:
                $ref: '#/components/schemas/ProofResponse'
        '400':
          description: Invalid path parameter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: |
            `tree_index` does not exist, or `insertion_index >= num_leaves`
            (leaf not yet inserted).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Storage backend error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    ProofResponse:
      type: object
      description: >
        Protobuf `ProofResponse` message returned by

        `GET /v1/trees/{tree_index}/proof/{insertion_index}`.


        All hash string fields are **little-endian hex** representations of
        32-byte

        Poseidon hashes (matching Solana's on-chain byte order and Circom ZK
        circuit

        input conventions). Each hash is a 64-character hex string (32 bytes).


        **Usage**: Together with `leaf`, `root`, and the 20-element `proof`
        array,

        a caller can verify membership and construct a Groth16 zero-knowledge
        proof

        verifiable against the on-chain Umbra Merkle root.


        **Depth**: The Umbra mixer uses a depth-20 Indexed Merkle Tree, so
        `proof`

        always contains exactly 20 sibling hashes.
      required:
        - root
        - tree_index
        - insertion_index
        - proof
        - leaf
      properties:
        root:
          type: string
          description: |
            Little-endian hex-encoded Poseidon root hash at query time.
            64 hex characters (32 bytes).
          example: 1a2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890ab
        tree_index:
          type: integer
          format: int64
          description: Echo of the requested `tree_index` path parameter.
          example: 0
        insertion_index:
          type: integer
          format: int64
          description: Echo of the requested `insertion_index` path parameter.
          example: 42
        proof:
          type: array
          description: >
            Ordered sibling hashes forming the authentication path from the leaf
            up

            to the root. Always exactly **20 elements** (depth-20 tree), ordered
            from

            leaf level (index 0) to root level (index 19). Each element is a

            64-character little-endian hex-encoded Poseidon hash.
          items:
            type: string
          minItems: 20
          maxItems: 20
          example:
            - '0000000000000000000000000000000000000000000000000000000000000000'
            - 2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864
        leaf:
          type: string
          description: >
            Little-endian hex-encoded `final_commitment = Poseidon(h1_hash,
            h2_hash)`

            at `insertion_index`. 64 hex characters (32 bytes).
          example: deadbeefcafebabe1234567890abcdef1234567890abcdef1234567890abcdef12
    ErrorResponse:
      type: object
      description: Standard JSON error body returned on 4xx and 5xx responses.
      required:
        - error
        - message
      properties:
        error:
          type: string
          description: |
            Short error category, e.g. `"Bad Request"`, `"Not Found"`,
            `"Too Many Requests"`, `"Internal Server Error"`.
          example: Not Found
        message:
          type: string
          description: Human-readable explanation of what went wrong.
          example: Tree 99 not found

````