1. Design goals
- Portable — a receipt is a self-contained JSON document; verification requires only the document and the agent's public key.
- Privacy-preserving — raw decision inputs and outputs are never carried in the receipt. Only their
sha256digests are sealed. - Tamper-evident — every receipt is hashed and Ed25519-signed; each receipt references the previous receipt's hash, forming an append-only chain.
- Verifiable without trust — a third party with no relationship to the issuer can independently confirm authenticity and integrity offline.
- Open — the spec, the canonicalization rules and the verification algorithm are public.
2. Receipt object
All fields are required unless explicitly marked optional. Field order in transport is irrelevant — canonicalization is by sorted keys.
{
"version": "1.0",
"id": "STR-1A2B3C4D5E",
"type": "decision_receipt",
"sequence": 42,
"agent": { "id": "agt_xxxxxxxx", "name": "FinanceBot" },
"model": { "provider": "openai", "name": "gpt-4o", "version": "2026.4" },
"decision": {
"type": "loan_rejection",
"input_hash": "sha256:…",
"output_hash": "sha256:…",
"risk_level": "high",
"human_review": true,
"permissions": ["credit.decide"],
"policies": ["eu-ai-act-high-risk"]
},
"metadata": { "request_id": "req_001" },
"timestamp": "2026-06-07T10:00:00.000Z",
"previous_hash": "sha256:…",
"receipt_hash": "sha256:…",
"signature": { "algorithm": "ed25519", "public_key": "…", "value": "…" }
}
3. Field reference
| Field | Type | Required | Notes |
|---|---|---|---|
version | string | yes | Spec version. Always "1.0" for this revision. |
id | string | yes | Globally unique receipt identifier (issuer-assigned). |
type | string | yes | Always "decision_receipt" for receipts conforming to this spec. |
sequence | integer | yes | Per-agent monotonically increasing counter, starting at 1. |
agent.id | string | yes | Stable public identifier for the agent. |
agent.name | string | yes | Human-readable agent name at the time of signing. |
model | object | yes | Provider, model name and version that produced the decision. |
decision.type | string | yes | Decision class (e.g. loan_approval, content_moderation). |
decision.input_hash | string | yes | sha256:HEX over the canonical input. Computed by the client; raw input never sent. |
decision.output_hash | string | yes | sha256:HEX over the canonical output. Computed by the client; raw output never sent. |
decision.risk_level | string | yes | One of low, medium, high, critical. |
decision.human_review | boolean | yes | true if a qualified human reviewed the decision before it took effect. |
decision.permissions | string[] | optional | Scopes this specific decision claimed to exercise. Used to detect scope violations against the agent's declared allow-list. |
decision.policies | string[] | optional | Policy / regulatory frame identifiers (e.g. eu-ai-act-high-risk). |
metadata | object | optional | Free-form, non-sensitive context. Must not contain raw decision data. |
timestamp | string | yes | RFC 3339 / ISO 8601 UTC, millisecond precision. |
previous_hash | string | yes | sha256:HEX of the previous receipt for the same agent, or sha256:GENESIS for the first. |
receipt_hash | string | yes | See §4. |
signature | object | yes | See §5. |
4. Canonicalization & receipt_hash
- Take the receipt object and remove the
receipt_hashandsignaturefields. - Serialize the remainder as JSON with keys sorted lexicographically at every depth, using UTF-8 and no insignificant whitespace.
- Compute
sha256over those bytes; emit it assha256:HEX(lowercase). - Set
receipt_hashto that value.
Any conforming implementation that runs steps 1–3 on a receipt must produce a value identical to the receipt's stored receipt_hash. This is how third parties verify body integrity without trusting the issuer.
5. Signature
The signature object has three fields:
{
"algorithm": "ed25519",
"public_key": "<base64 raw 32-byte Ed25519 public key>",
"value": "<base64 Ed25519 signature over the UTF-8 bytes of receipt_hash>"
}
Verification:
- Recompute
receipt_hashper §4. It must equal the stored value. - Verify the Ed25519
signature.valueagainst the UTF-8 bytes ofreceipt_hashusingsignature.public_key.
6. Append-only ledger
For a given agent, receipts form a chain ordered by sequence. Each receipt's previous_hash equals the prior receipt's receipt_hash; the first receipt uses sha256:GENESIS. Walking the chain and recomputing each hash (§4) and signature (§5) is sufficient to detect any modification, insertion or deletion of past receipts.
The reference implementation exposes this walk as GET /api/v1/verify/ledger.
7. Verification surface (reference implementation)
GET /api/v1/receipts/{id}/verify — verifies a receipt stored on the issuing node.
POST /api/v1/verify — verifies any receipt JSON against §4 and §5, no relationship with the issuer required.
GET /api/v1/verify/ledger — walks the full agent ledger per §6.
GET /api/v1/agents/{id}/passport — derived, signed Trust Passport summary (see Trust Passport).
GET /.well-known/signatrust.json — machine-readable discovery document advertising all conformant endpoints.
8. Compliance & non-goals
- This spec defines what a Decision Receipt is and how to verify it. It does not mandate when an agent must seal a receipt — that is a policy decision left to operators and regulators.
- It does not transport raw decision content. Anyone needing the raw input or output must obtain it from the operator under a separate, lawful process and check it against
decision.input_hash/decision.output_hash. - It does not, by itself, certify that a decision was correct or compliant — only that it was made, when, by which agent, with what risk classification, and that the record has not been altered since.
9. Versioning
Implementations must reject receipts whose version they do not recognize. Backwards-incompatible changes will bump the major version. Compatible additions (new optional fields) keep the major version.