Skip to main content

What is the Indexer?

The Umbra indexer is an off-chain service that continuously watches the Solana chain for Umbra program transactions, extracts Stealth Pool Note commitments and encrypted ciphertexts, and stores them in a queryable database. It acts as the data layer that bridges on-chain Merkle tree state with the SDK and any other client that needs to interact with the pool. It exposes two primary capabilities:
  • Note data — fetch encrypted note ciphertexts by tree or absolute index. The SDK uses this to scan for notes addressable by your X25519 + viewing keys by trying to decrypt each ciphertext.
  • Merkle proofs — generate a Merkle inclusion proof (authentication path) for any leaf. Required to construct a valid burn transaction on-chain. The V18 burner factories fetch a per-batch proof immediately before submission, so the scanner does not pre-bundle proofs.
Beyond the SDK, the indexer is useful for analytics, monitoring, and custom integrations such as building your own wallet or scanning pipeline.
The service is named utxo-indexer for historical reasons. The records it serves are V18 Stealth Pool Notes; the on-chain commitments are still called UTXOs at the protocol level.

Base URLs

  • Mainnethttps://utxo-indexer.api.umbraprivacy.com.
  • Devnethttps://utxo-indexer.api-devnet.umbraprivacy.com.

Response Format

All endpoints return Protobuf (application/x-protobuf) regardless of the Accept header. Protobuf is used by design — binary data like 32-byte hash arrays and ciphertext payloads compress significantly better than JSON. The health endpoints are the exception: they support both JSON (default) and Protobuf via Accept header negotiation.

Rate Limiting

All endpoints are subject to rate limiting. Exceeded limits return 429 Too Many Requests. Contact the Umbra team if you need higher rate limit allowances for production workloads.

Tree Structure

Note commitments are organised into Indexed Merkle Trees, each holding up to 1,048,576 leaves (depth-20). When a tree is full, the indexer begins a new tree at the next sequential index.
  • Tree 0 holds leaves at absolute indices 0 to 1,048,575.
  • Tree 1 holds leaves at absolute indices 1,048,576 to 2,097,151.
  • And so on.
The absolute index of any leaf is:
absolute_index = tree_index × 1_048_576 + insertion_index

SDK Integration

When you pass indexerApiEndpoint to getUmbraClient, the SDK automatically constructs four internal providers on the client:
  • client.fetchMerkleProof — single-leaf Merkle proof fetcher (GET /v1/trees/{tree_index}/proof/{insertion_index}).
  • client.fetchBatchMerkleProof — per-batch Merkle proof fetcher (POST /v1/trees/{tree_index}/proofs). Pass to burner factories.
  • client.fetchUtxoData — Stealth Pool Note data fetcher (GET /v1/utxos/...). Field name retains the legacy spelling for backwards compatibility; the V18 scanner deps override is fetchStealthPoolNoteData.
  • client.fetchTreeSummary — tree summary fetcher.
The scanner factory reads client.fetchUtxoData and client.fetchTreeSummary internally. The burner factories consume fetchBatchMerkleProof (pass it via deps.fetchBatchMerkleProof = client.fetchBatchMerkleProof!). You do not need to call the indexer directly when using the SDK.

Planned Improvements

The SDK currently calls the indexer directly using the indexerApiEndpoint you provide. We are planning to migrate this to an IP Obfuscation Service that will proxy indexer requests through an anonymising relay, so that fetching your notes does not leak your IP address to the indexer. This will be a transparent upgrade — the SDK interface will not change.

API Reference

Stats

Aggregate statistics for the entire Stealth Pool Note index.

Health

Basic, detailed, liveness, and readiness health checks.

Tree Metadata

Current root hash, leaf count, and note count for a specific tree.

Tree Notes

Paginated Stealth Pool Note records for a specific tree.

Merkle Proof

Generate an inclusion proof for a specific leaf.

Batch Merkle Proofs

Retrieve multiple proofs atomically under the same tree root — what the burner factory uses.

Global Notes

Paginated note queries spanning all trees.

Single Note

Fetch a single note by absolute index.