aiternam Docsaiternam.com ↗

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}/proof

Public, 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

FieldMeaning
aiternam_proof_versionVersion of the bundle envelope — bumped only if the bundle's shape changes. Independent of the crypto-rule versions below.
prediction_idThe prediction this bundle is for.
anchor_statusWhere it sits on the trust ladder: unanchoredpendingbitcoin (see below).
hash_versionThe canonicalisation rule version to reproduce content_hash (frozen 1).
content_hashThe claimed sha256 fingerprint. You recompute it from payload and check it matches — never trust this value on its word.
payloadThe raw sealed forecast — every field that went into the fingerprint. created_at is the exact ISO string that was sealed.
payload_float_fieldsWhich 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.
merkleThe RFC 6962 inclusion proof: root (committed to Bitcoin), your leaf_index, the tree_size, and the sibling path leaf→root.
bitcoinThe 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/bitcoin yet.
  • pending — the batch root has been stamped to the OpenTimestamps calendars, but Bitcoin hasn't confirmed it. merkle is present; bitcoin.block_height is not yet final.
  • bitcoin — a Bitcoin block has been confirmed and independently checked against the block's real Merkle root. bitcoin is 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:

  1. Integrity — recompute sha256 over the canonicalised payload (guided by hash_version and payload_float_fields); it must equal content_hash.
  2. Inclusion — walk merkle.path from your content_hash up to merkle.root; it must match.
  3. Bitcoin — verify bitcoin.ots_base64 against 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.