HESTIAdocs

API routes

The backend HTTP surface — every /api/v1 route, its query parameters, and its response shape.


The console backend exposes the route service over a small HTTP API under /api/v1. Every route runs on the Node runtime and reflects only public chain state — plus the relayer. All numeric field values are returned as decimal strings (they are field elements / uint256, too large for JSON numbers).

GET /api/v1/health

Indexer liveness. Syncs, then reports.

json
{ "status": "ok", "lastBlock": "12345678", "leafCount": 42 }

On failure it returns 503 with { "status": "error", "error": "…" }.

GET /api/v1/pool/state

Current pool summary.

json
{ "root": "1234…", "leafCount": 42, "treeDepth": 32, "lastBlock": "12345678" }

GET /api/v1/tree/proof

Merkle inclusion proof for a leaf.

QueryTypeNotes
leafinteger ≥ 0the leaf index
json
{
  "leaf": "1234…",
  "pathElements": ["…", "…"],
  "pathIndices": [0, 1, 0],
  "root": "1234…"
}

A non-integer or negative leaf returns 400. The SDK uses this to build the input for the circuit.

GET /api/v1/notes/scan

The encrypted note blobs the caller trial-decrypts to discover what it owns.

QueryTypeNotes
sincebigintblock number to scan from (default 0)
json
[
  {
    "leafIndex": 7,
    "commitment": "1234…",
    "encryptedNote": "0x…",
    "blockNumber": "12345670"
  }
]

The server cannot read these blobs — only a holder of the matching viewing key can (viewing keys).

GET /api/v1/association/status

Whether an association root is currently approved.

QueryTypeNotes
rootbigintassociation root (default 0)
json
{ "root": "1234…", "valid": true, "uri": "https://asp.example/set/…" }

uri is null when the root isn't known/approved.

POST /api/v1/relay

Submit a client-built proof on-chain. The relayer pays gas with its server key, so the recipient address has no funding history. It cannot alter recipient, withdrawAmount, or feeAmount — they are bound into the proof.

Request body (values are decimal/hex strings, produced by the SDK):

jsonc
{
  "arity": "1x2",                      // "1x2" | "2x2"
  "proof": {
    "a": ["…", "…"],
    "b": [["…", "…"], ["…", "…"]],
    "c": ["…", "…"]
  },
  "nullifiers": ["…"],                 // 1 for 1x2, 2 for 2x2
  "outCommitments": ["…", "…"],
  "data": {
    "root": "…",
    "associationRoot": "…",
    "withdrawAmount": "…",
    "token": "0x…",
    "recipient": "0x…",
    "feeAmount": "…",
    "relayer": "0x…"
  },
  "encryptedNotes": ["0x…", "0x…"]
}

Response:

json
{ "txHash": "0x…" }

Failure modes: 503 { "error": "relayer not configured" } if no HESTIA_RELAYER_KEY is set; 400 { "error": "…" } if submission reverts or the body is malformed.

Notes

  • Read routes use the Node runtime and are dynamic (always reflect the latest sync); there is no caching layer in front of them.
  • The API mirrors the @hestia/route handlers exactly, so self-hosting the route service gives you the same surface — see self-hosting.