A record that can't be quietly edited.
Every decision the policy engine makes — not just the payments that succeeded — becomes a durable, append-only entry. Each one records a hash of the entry before it, so altering or removing anything in the past breaks every hash that follows it.
What a receipt holds
- decision
- allow, requiresApproval, or deny — the exact outcome the policy engine returned.
- reason
- Present on a deny. One of a fixed set of codes — see /policy — never free text.
- amount, token, payee
- What was requested, exactly as the agent submitted it.
- signature
- The on-chain transaction signature, once one exists — absent for anything denied or still held.
- prevHash
- A hash of the receipt immediately before this one, linking it into the chain.
If a payment happened, there's a receipt. If it didn't, there's a reason.
Why hash-chaining, not just a database row
A plain row can be edited in place with nothing left to show it happened. Chaining each entry to the one before it means an edit, a deletion, or a reordering anywhere in the history is visible — every hash computed after that point stops matching. Tamper-evidence here comes from the structure itself, not from trusting whoever has database access.
Every outcome, not just the wins
A deny is recorded exactly like an allow — same durability, same position in the chain. That matters for the same reason the reason codes matter: the point of this system is to show what the policy refused and why, not just what it paid.