Receipts
A PEAC receipt is a compact JWS (JSON Web Signature) token signed with Ed25519 that provides cryptographic proof of an AI agent interaction. Receipts are the core proof artifact of the protocol: every interaction that matters gets a receipt.
Two wire formats coexist: Wire 0.1 (peac-receipt/0.1, frozen legacy) and Wire 0.2 (interaction-record+jwt, stable). Both use Ed25519 JWS signatures and the PEAC-Receipt header. verifyLocal() auto-detects wire version.
Format
Receipts use compact JWS serialization: three Base64url-encoded segments separated by dots.
<base64url-header>.<base64url-payload>.<base64url-signature>
Header
The JWS header identifies the signing algorithm, wire format version, and the key used to sign.
{
"alg": "EdDSA",
"typ": "peac-receipt/0.1",
"kid": "peac-2026-03"
}
{
"alg": "EdDSA",
"typ": "interaction-record+jwt",
"kid": "peac-2026-03"
}
| Field | Value | Description |
|---|---|---|
alg | EdDSA | Ed25519 signature algorithm (RFC 8032) |
typ | peac-receipt/0.1 or interaction-record+jwt | Wire format identifier (0.1 or 0.2) |
kid | Key ID string | Identifies the signing key in the issuer's JWKS (max 256 chars) |
Wire 0.2 rejects embedded keys (jwk, x5c, x5u, jku), crit, b64: false, and zip headers.
Wire 0.1 Payload
Wire 0.1 uses flat payload claims with a simple structure.
{
"iss": "https://api.example.com",
"aud": "https://client.example.com",
"iat": 1709500000,
"amt": 100,
"cur": "USD",
"rail": "x402",
"reference": "tx_abc123",
"subject": "https://api.example.com/inference"
}
| Claim | Required | Description |
|---|---|---|
iss | Yes | Issuer URL: the service that signed the receipt |
aud | No | Intended audience URL |
iat | Yes | Issued-at timestamp (Unix seconds) |
amt | No | Payment amount |
cur | No | Currency code (ISO 4217 or token symbol) |
rail | No | Payment rail identifier (e.g., x402, stripe) |
reference | No | Transaction reference |
subject | No | Subject resource URL |
Wire 0.2 Payload
Wire 0.2 adds structured kinds, typed extensions, and policy binding.
{
"iss": "https://api.example.com",
"iat": 1709500000,
"peac_version": "0.2",
"kind": "evidence",
"type": "org.peacprotocol/payment",
"pillars": ["commerce"],
"extensions": {
"org.peacprotocol/commerce": {
"payment_rail": "x402",
"amount_minor": "10000",
"currency": "USD"
}
},
"policy": {
"uri": "https://api.example.com/.well-known/peac.txt",
"version": "peac-policy/0.1",
"digest": "sha256:a1b2c3..."
}
}
| Claim | Required | Description |
|---|---|---|
iss | Yes | Issuer (canonical: https:// or did: only) |
iat | Yes | Issued-at timestamp (Unix seconds) |
peac_version | Yes | Must be "0.2" |
kind | Yes | Structural kind: evidence or challenge |
type | Yes | Semantic type (reverse-DNS or absolute URI) |
pillars | No | Verification domains (sorted, unique, from 10-pillar taxonomy) |
extensions | No | Typed extension groups (keyed by reverse-DNS) |
policy | No | Policy binding (uri, version, digest) |
occurred_at | No | ISO 8601 event time (evidence kind only) |
actor | No | Actor binding (identity proof) |
representation | No | Content representation fields |
Kind semantics
evidence: records something that happened. May includeoccurred_at.challenge: declares what is required before proceeding. Uses RFC 9457 problem details. Must NOT includeoccurred_at.
Delivery
Receipts are delivered via the PEAC-Receipt HTTP response header.
HTTP/1.1 200 OK
PEAC-Receipt: eyJhbGciOiJFZERTQSIsInR5cCI6...
Content-Type: application/json
{"result": "success"}
For non-HTTP transports, receipts are included in the response metadata using the appropriate transport convention:
| Transport | Delivery mechanism |
|---|---|
| HTTP | PEAC-Receipt response header |
| MCP | Response _meta field |
| A2A | metadata[extensionURI].carriers[] |
| gRPC | Trailing metadata |
| WebSocket | Message envelope field |
Extension Groups (Wire 0.2)
Wire 0.2 defines 12 typed extension groups under extensions, keyed by reverse-DNS:
| Key | Description |
|---|---|
org.peacprotocol/commerce | Payment evidence: payment_rail, amount_minor, currency, event |
org.peacprotocol/access | Access control: resource, action, decision (allow/deny/review) |
org.peacprotocol/challenge | Challenge requirements: challenge_type, problem (RFC 9457) |
org.peacprotocol/identity | Identity attestation: proof_ref |
org.peacprotocol/correlation | Workflow correlation: trace_id, span_id, workflow_id, parent_jti |
org.peacprotocol/consent | Consent records: consent_type, consent_status, data_subject |
org.peacprotocol/privacy | Privacy controls: data_categories, retention, legal_basis |
org.peacprotocol/safety | Safety signals: safety_category, severity, model_id |
org.peacprotocol/compliance | Compliance evidence: framework, requirement_id, status |
org.peacprotocol/provenance | Content provenance: source_uri, content_hash, derivation |
org.peacprotocol/attribution | Attribution records: creator, license, contribution_type |
org.peacprotocol/purpose | Purpose declaration: declared_purpose, enforced_purpose |
{
"extensions": {
"org.peacprotocol/commerce": {
"payment_rail": "x402",
"amount_minor": "10000",
"currency": "USD"
},
"org.peacprotocol/correlation": {
"trace_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"workflow_id": "checkout-flow-123"
}
}
}
Third-party extensions use reverse-DNS keys under their own domain (e.g., com.example/custom-data). Unrecognized-but-well-formed keys are preserved with a warning during verification.
Issuance
Use @peac/protocol to issue receipts programmatically.
Wire 0.1
import { issue } from '@peac/protocol';
const receipt = await issue({
privateKey: process.env.PEAC_PRIVATE_KEY,
kid: 'peac-2026-03',
claims: {
iss: 'https://api.example.com',
aud: 'https://client.example.com',
amt: 100,
cur: 'USD',
rail: 'x402',
},
});
// receipt is a compact JWS string ready for delivery
res.setHeader('PEAC-Receipt', receipt);
Wire 0.2
import { issueWire02 } from '@peac/protocol';
const receipt = await issueWire02({
privateKey: process.env.PEAC_PRIVATE_KEY,
kid: 'peac-2026-03',
claims: {
iss: 'https://api.example.com',
kind: 'evidence',
type: 'org.peacprotocol/payment',
pillars: ['commerce'],
extensions: {
'org.peacprotocol/commerce': {
payment_rail: 'x402',
amount_minor: '10000',
currency: 'USD',
},
},
},
});
res.setHeader('PEAC-Receipt', receipt);