Proof bundle & API
The proof bundle is the self-contained artifact that makes a prediction verifiable without us. This page is its field-by-field reference — for anyone building a verifier we didn't write, which is the highest compliment the system can receive. For the step-by-step how to verify, see Verify it yourself; this page documents the shape.
The endpoint
GET /v1/predictions/{id}/proofPublic, like every read — no auth. It returns a single JSON document with a
Content-Disposition: attachment header (filename aiternam-proof-<id>.json), so a browser
downloads it. The same file backs the "Download proof" button on every prediction page.
The bundle is self-contained by design: it ships the raw prediction (not a hash we computed for you), so you recompute everything yourself. If aiternam disappeared, the file in your hands would still prove the prediction.
The shape
{
"aiternam_proof_version": 1,
"prediction_id": "…",
"anchor_status": "bitcoin",
"hash_version": 1,
"content_hash": "…", // sha256 of the canonical payload
"payload": { // the EXACT sealed forecast (raw values)
"author_id": "…",
"topic": "crypto",
"asset": "BTC",
"direction": "up",
"confidence": 0.7,
"target_price": null,
"horizon_days": 30,
"rationale": "…",
"resolution_source": "coingecko.spot.usd",
"baseline_price": 61000.0,
"baseline_drift": 0.012,
"baseline_vol": 0.18,
"cosigned_from": null,
"created_at": "2026-06-24T09:00:00+00:00"
},
"payload_float_fields": ["baseline_drift","baseline_price","baseline_vol","confidence"],
"merkle": { // present once the batch is anchored
"merkle_version": 1,
"root": "…",
"leaf_index": 12,
"tree_size": 40,
"path": ["…", "…"]
},
"bitcoin": { // present once the batch is anchored
"anchored_at": "…",
"block_height": 849834,
"block_hash": "…",
"explorer_url": "https://mempool.space/block/…",
"ots_base64": "…"
}
}Field reference
| Field | Meaning |
|---|---|
aiternam_proof_version | Version of the bundle envelope — bumped only if the bundle's shape changes. Independent of the crypto-rule versions below. |
prediction_id | The prediction this bundle is for. |
anchor_status | Where it sits on the trust ladder: unanchored → pending → bitcoin (see below). |
hash_version | The canonicalisation rule version to reproduce content_hash (frozen 1). |
content_hash | The claimed sha256 fingerprint. You recompute it from payload and check it matches — never trust this value on its word. |
payload | The raw sealed forecast — every field that went into the fingerprint. created_at is the exact ISO string that was sealed. |
payload_float_fields | Which payload numbers are floats. It exists because some parsers (notably in-browser JS) can't tell 1.0 from 1 after parsing — this list, derived from the schema, tells a verifier which numbers to render as fixed-scale decimals so the hash reproduces byte-for-byte. |
merkle | The RFC 6962 inclusion proof: root (committed to Bitcoin), your leaf_index, the tree_size, and the sibling path leaf→root. |
bitcoin | The Bitcoin attestation: anchored_at, the block_height/block_hash, a read-only explorer_url (convenience only), and ots_base64 — the OpenTimestamps proof. |
Before the first batch anchor, merkle and bitcoin are simply absent and anchor_status
is unanchored or pending. That's the honest residual window:
the prediction is sealed in our records but not yet trustlessly anchored. The bundle never fakes an
anchor it doesn't have.
The anchor_status state machine
unanchored → pending → bitcoin- unanchored — sealed; the next batch hasn't run. No
merkle/bitcoinyet. - pending — the batch root has been stamped to the OpenTimestamps calendars, but Bitcoin hasn't
confirmed it.
merkleis present;bitcoin.block_heightis not yet final. - bitcoin — a Bitcoin block has been confirmed and independently checked against the block's
real Merkle root.
bitcoinis fully populated. See Anchored to Bitcoin.
How the bundle maps to verification
Each field group powers exactly one of the three independent checks in Verify it yourself:
- Integrity — recompute sha256 over the canonicalised
payload(guided byhash_versionandpayload_float_fields); it must equalcontent_hash. - Inclusion — walk
merkle.pathfrom yourcontent_hashup tomerkle.root; it must match. - Bitcoin — verify
bitcoin.ots_base64against the chain (ots verify --digest <merkle.root>); it attests the root's block.
The zero-effort path is the in-browser verifier at /predictions/[id]/verify, which performs
steps 1 and 2 live in your browser (step 3 is confirmed out-of-browser with an OTS client). This
reference page is for when you'd rather build your own.
Forward compatibility
The three version numbers move independently and only ever go up: aiternam_proof_version (the
envelope shape), hash_version (the canonicalisation rule), and merkle.merkle_version (the tree
construction). A proof sealed under a given crypto-rule version verifies under that version
forever — new versions never invalidate old proofs. Build your verifier to read the version fields
and apply the matching rule, and it will keep working.
See also: Verify it yourself · Anchored to Bitcoin · The honest limits.