Skip to content

Receipt format

04 · Receipt format

A receipt is self-contained by design — every field a third party needs to verify travels with it. Nothing here requires a call back to Backrun. If a field were only obtainable from our server, the product would be a database with extra steps.

v1 vs v2

Version 2 is current and is what every receipt issued today looks like: the event hash is salted (see why). Version 1 is a legacy, unsalted format — exactly two anchors on Base mainnet were issued under it, during initial rollout before salting existed. Both remain independently verifiable; a verifier just has to branch on version and hash accordingly (with or without the salt).

Fields

fieldtypeversionwhy it's here
status"pending" | "anchored"bothWhether the batch this event belongs to has been anchored yet. A pending receipt has no proof or root — the API never implies a guarantee that doesn't exist yet.
version1 | 2bothWhich hashing scheme produced leaf. A verifier must branch on this — hashing a v2 event without its salt silently produces a non-matching leaf, indistinguishable from tampering.
eventIdstringbothYour reference to look the receipt up again later; not part of what's hashed.
canonicalstring or nullbothThe exact canonicalised event bytes that were hashed. null once the record has been erased — see erasure & retention. Echoed back so you can confirm your event canonicalised the way you expected.
salt32-byte hex, or absentv2 onlyPresent until erasure, then absent — that absence is the erasure, and it's deliberately irreversible. Without it a v2 leaf cannot be recomputed from the payload, even if the payload were somehow recovered separately.
eventHash32-byte hexbothAlias of leaf in the current implementation — both name the same value; kept as two names because "the hash of this event" and "the Merkle leaf for this event" are the same number for different reasons, and callers may reasonably look for either name.
leaf32-byte hexbothThe Merkle leaf: keccak256(0x00‖salt‖canonical) (v2) or keccak256(0x00‖canonical) (v1). What the proof is a proof of.
proof{sibling, siblingIsLeft}[]bothThe inclusion proof — sibling hashes and their side, bottom to top. See proof construction.
root32-byte hexbothThe Merkle root the proof folds up to — this is the value that was actually published on-chain.
batchId32-byte hexbothPublisher-chosen identifier for the batch, also emitted in the Anchored event. Distinguishes this anchor from any other with the same root (astronomically unlikely, but the field exists so batch identity never depends on hash collision-freedom alone).
chainIdintegerbothEIP-155 chain id where the root was anchored. 8453 for Base mainnet — check this before trusting a root; a matching root on the wrong chain proves nothing about the right one.
contractaddressbothThe AuditAnchor contract address the root was anchored on. Currently 0x256080339DEA7E8F3089C49CCEB0F98547103f91.
publisheraddressbothThe address that called anchor(). Anchors are namespaced per-publisher on-chain — see reading the anchor on-chain — so this is part of identifying which sequence of anchors to check against, not a trust signal in itself.
anchorTxHash32-byte hex, or undefinedbothThe transaction that anchored this root. undefined while status is pending.
anchoredAtISO 8601 string, or undefinedbothWall-clock time derived from the anchoring block. Convenience only — the block itself, not this field, is the authoritative record of when.
erasedAtISO 8601 string, or nullbothWhen an erasure request was processed for this event, if any. See erasure & retention.

Pending receipts have fewer fields

Before its batch is anchored, a receipt is just: status, eventId, leaf, canonical, receivedAt. There is no proof, no root, nothing to check against a chain yet — issuing a proof against a root that doesn't exist on-chain would be worse than issuing nothing. See the exact JSON shapes on the API reference.