Endpoint reference
Every endpoint in the indexer contract. Read API conventions first for types, cursors, and sync state.
- Which call to make first, and the two fields on it that decide whether any later answer means anything.
- What
GET /windowreports before, during, and after the founding commit window, and why both countdowns are block counts. - How to read an artifact record, including the two fields that are
nullat Elder and the one that isnullfor a relic. - What
POST /safety/outpointsclassifies an outpoint as, and what a wallet owes the user for each answer. - Which reads the app backend proxies, and what its two build helpers never do.
These pages publish no statistics. Counters in the examples are zero and heights are round numbers, so that nothing here can be mistaken for a measurement. To see real numbers, point the prober below at an indexer, or call one yourself.
Try it against your own indexer
Nothing has been requested. Enter an indexer you trust and the raw responses will appear here.
The request goes from your browser straight to the host you typed. This site has no backend, stores nothing, and has no default indexer. Opening the page from disk may cause the browser to block the request, which the panel will tell you plainly.
Status and health
GET /status
curl -s https://indexer.example.org/patina/status
{
"network": "signet",
"protocol_id": "PTNA",
"spec_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
"tip_height": 0,
"indexed_height": 0,
"synced": false,
"parser_version": "",
"indexer_version": "",
"counters": {
"artifacts_alive": 0,
"artifacts_relic": 0,
"founding_total": 0,
"rings_total": 0,
"deepest_live_depth": 0,
"endowment_total_sats": "0"
}
}
The first call any client should make. Compare indexed_height with tip_height
before trusting anything else, and log spec_sha256 with anything you store.
GET /health, GET /ready, GET /metrics
Three endpoints an operator wires into probes. The one that carries weight is /ready. An
indexer that reports ready while it is replaying a reorg will be handed traffic it cannot answer honestly,
and the wrong answers will look exactly like right ones.
| Endpoint | Answers | Use it for |
|---|---|---|
/health | Is the process alive | Liveness probes. It does not mean the data is current. |
/ready | Is it safe to send traffic | Readiness probes. Should fail while replaying a reorg or during initial sync. |
/metrics | Operational counters for scraping | Dashboards and alerts. See Verify and monitor. |
The founding window
GET /window
curl -s https://indexer.example.org/patina/window
{
"state": "PENDING",
"h_open": 0,
"h_close": 0,
"grace_end": 0,
"tip_height": 0,
"blocks_until_open": 0,
"blocks_remaining": 0,
"founding_total": 0
}
- state
- Where the chain is relative to the window. Before
h_open, between the two heights, or pasth_closewith or without grace remaining. - blocks_until_open
- Counts down before the window opens. Zero once it has opened.
- blocks_remaining
- Blocks left to commit while the window is open.
- founding_total
- How many founding artifacts exist so far. This is a real count from indexed chain data, which is why it can only be read from an indexer and never from this page.
Both countdowns are block counts, not clocks. Convert to time only as a labelled estimate. The window is 4032 blocks long and the reveal grace after it is another 4032, and what each of those two stretches is for is drawn on Firstlight Seals.
Artifacts
GET /artifacts
curl -s 'https://indexer.example.org/patina/artifacts?founding=true&status=ALIVE&limit=50'
| Name | Values | Effect |
|---|---|---|
cursor | opaque string | Continue a previous page |
limit | integer | Requested page size. The server may return fewer. |
founding | true, false | Restrict to the founding cohort, or exclude it |
status | ALIVE, RELIC | Restrict by state |
address | address string | Only artifacts whose carrier pays that address |
{
"items": [],
"next_cursor": null
}
Each item is an artifact record. Repeat every filter when you send a cursor.
GET /artifacts/:id
curl -s https://indexer.example.org/patina/artifacts/d3b8d3013c23dd3df76882034df80935ba55aa75772007f0b142b63adce5eff0
{
"artifact_id": "d3b8d3013c23dd3df76882034df80935ba55aa75772007f0b142b63adce5eff0",
"birth_txid": "1d29e5ba13cecf357a9218518914617f872eeaa83a33bf6be6fd78dfdd1ce9c6",
"birth_height": 880000,
"birth_vout": 1,
"endowment_sats": "100000",
"founding": true,
"status": "ALIVE",
"carrier": {
"txid": "1d29e5ba13cecf357a9218518914617f872eeaa83a33bf6be6fd78dfdd1ce9c6",
"vout": 1,
"height": 880000,
"value": "100000"
},
"depth": 13000,
"tier": 3,
"tier_name": "Verdigris",
"next_tier": 4,
"blocks_to_next_tier": 13280,
"rings": []
}
Field by field explanation on Reading your artifact. At Elder,
next_tier and blocks_to_next_tier are null. For a relic,
carrier is null. tier is the index and tier_name is the
name for it; all eight, with the block threshold each one starts at, are set out on
The tier journey.
GET /artifacts/:id/card
curl -s https://indexer.example.org/patina/artifacts/d3b8d3013c23dd3df76882034df80935ba55aa75772007f0b142b63adce5eff0/card
Returns the share card payload: the identifying fields, the current tier and depth, the ring count, and the height the numbers were taken at. A card without that height cannot be checked by whoever sees it, so renderers must print it. See Rules that keep renders honest.
Lookups
GET /addresses/:address/holdings
curl -s https://indexer.example.org/patina/addresses/tb1p.../holdings
Artifacts whose current carrier pays that address. This is a lookup by output script, not a claim about ownership or identity. One person may use many addresses, and one address may be used by many people.
GET /carriers/:txid/:vout
curl -s https://indexer.example.org/patina/carriers/1d29e5ba13cecf357a9218518914617f872eeaa83a33bf6be6fd78dfdd1ce9c6/1
The carrier record for one outpoint and every artifact riding it. This is how you discover a bundle: more than one artifact on the same carrier.
History and views
Eight read only endpoints, every one of them derived from indexed blocks rather than stored separately. The split to hold on to while you read the table: some of these are settled history that can never change, and some are a picture of the tip that changes whenever somebody spends a carrier.
| Endpoint | Returns | Ordering |
|---|---|---|
GET /census/current | The survival table for the current 2016-block epoch | Deterministic per epoch |
GET /census/:epoch | The same table for a past epoch | Fixed once the epoch is complete |
GET /museum | The longest completed rings | Deepest first |
GET /leaderboard?scope=all | Deepest live stretches | Deepest first |
GET /leaderboard?scope=founding | Same, restricted to the founding cohort | Deepest first |
GET /shatter | Recently closed rings | Newest first |
GET /invalid-events | Recent invalid events with reason codes | Newest first |
GET /stats | Counters and distribution health | Single object |
The census is per epoch because 2016 blocks is the difficulty period, which is the natural unit for a table that says how many stretches survived. The museum is history and cannot shrink. The leaderboard is the present and changes every time somebody moves.
GET /stats is where distribution health is published, including during the founding window.
It is deliberately a first class endpoint rather than an internal metric, because
farming the founding cohort is a known limit and hiding the numbers
would be the dishonest response to it.
Safety check
POST /safety/outpoints
curl -s -X POST https://indexer.example.org/patina/safety/outpoints \
-H 'content-type: application/json' \
-d '{"outpoints":["1d29e5ba13cecf357a9218518914617f872eeaa83a33bf6be6fd78dfdd1ce9c6:1"]}'
{
"protected": true,
"kind": "carrier",
"artifact_ids": ["d3b8d3013c23dd3df76882034df80935ba55aa75772007f0b142b63adce5eff0"]
}
Three classifications, and the wallet behaviour each one asks for. Treating carrier the same
as none is how a user loses a stretch to coin selection without ever being asked.
kind | Means | What a wallet should do |
|---|---|---|
carrier | Spending this ends a stretch and resets depth | Warn loudly, name the artifacts, require confirmation |
commit | An unspent commit output waiting to be revealed | Warn that spending it destroys a pending claim |
none | Not known to the protocol | Nothing special |
The endpoint stores nothing that you send it. It is a POST because a coin selection can be long, not because it writes anything.
The app backend proxy
The Bitcoin Universe backend re-exposes a read only subset under /patina, with fail closed
behaviour when the indexer is unavailable, plus two build helpers.
/patina/status /patina/window
/patina/artifacts /patina/artifacts/:id
/patina/census /patina/museum
/patina/leaderboard /patina/shatter
/patina/stats /patina/addresses/:address/holdings
POST /patina/safety/outpoints
POST /patina/plan/commit unsigned commit plan and a review summary
POST /patina/plan/seed unsigned reveal plan and a review summary
The planners never hold key material, never take custody, and never broadcast anything you have not signed. Fail closed means an unavailable indexer produces an error, not a stale or invented answer.