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.
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
- Mainnet —
https://utxo-indexer.api.umbraprivacy.com. - Devnet —
https://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 return429 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
0to1,048,575. - Tree 1 holds leaves at absolute indices
1,048,576to2,097,151. - And so on.
SDK Integration
When you passindexerApiEndpoint 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 isfetchStealthPoolNoteData.client.fetchTreeSummary— tree summary fetcher.
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.