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.
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-02"
}
| Field | Value | Description |
|---|---|---|
alg | EdDSA | Ed25519 signature algorithm (RFC 8032) |
typ | peac-receipt/0.1 | Wire format identifier |
kid | Key ID string | Identifies the signing key in the issuer's JWKS |
The wire format peac-receipt/0.1 is frozen and will not change until v1.0. All implementations can rely on this identifier remaining stable.
Payload
The payload contains standard JWT claims alongside the PEAC-specific peac object.
{
"iss": "https://api.example.com",
"sub": "user:agent-123",
"aud": "https://client.example.com",
"iat": 1740000000,
"jti": "rec_01HZQX...",
"peac": {
"type": "api.request",
"attestation_type": "interaction",
"status": "executed",
"version": "0.10.13"
}
}
Standard JWT claims
| Claim | Required | Description |
|---|---|---|
iss | Yes | Issuer URL -- the service that signed the receipt |
sub | Yes | Subject identifier -- who or what the receipt is about |
aud | No | Intended audience URL |
iat | Yes | Issued-at timestamp (Unix seconds) |
jti | Yes | Unique receipt identifier (must be globally unique) |
exp | No | Expiration timestamp (Unix seconds) |
PEAC-specific claims
All protocol-specific data lives inside the peac object.
| Claim | Required | Description |
|---|---|---|
type | Yes | Interaction type (e.g., api.request, tool.call, payment) |
attestation_type | Yes | One of: interaction, payment, consent, identity |
status | Yes | Lifecycle stage of the interaction |
version | Yes | Protocol version that produced this receipt |
Lifecycle stages
Every receipt records a lifecycle stage via the status field. The status reflects the outcome of the interaction at the time the receipt was signed.
| Status | Meaning | Example |
|---|---|---|
proposed | Intent declared, not yet acted on | Payment intent created |
authorized | Authorized to proceed | API key validated |
denied | Explicitly denied | Rate limit exceeded |
executed | Successfully completed | Tool call returned results |
errored | Completed with error | Upstream service failure |
Receipts are immutable once signed. To record a status change (e.g., from authorized to executed), issue a new receipt -- do not modify the original.
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 |
| gRPC | Trailing metadata |
| WebSocket | Message envelope field |
Extensions
The peac object supports extensions via reverse-DNS namespaced keys. Extensions allow protocols and integrations to attach structured metadata to receipts without modifying the core schema.
{
"peac": {
"type": "tool.call",
"attestation_type": "interaction",
"status": "executed",
"version": "0.10.13",
"extensions": {
"org.peacprotocol/interaction@0.1": {
"tool_name": "search",
"tool_server": "https://tools.example.com"
}
}
}
}
Extension schemas are validated during verification (check #11). Only registered extension keys pass validation. See the PEAC registries for the full list.
Issuance
Use @peac/protocol to issue receipts programmatically.
import { issueReceipt } from '@peac/protocol';
const receipt = await issueReceipt({
privateKey: process.env.PEAC_PRIVATE_KEY,
kid: 'peac-2026-02',
claims: {
iss: 'https://api.example.com',
sub: 'user:agent-123',
peac: {
type: 'api.request',
attestation_type: 'interaction',
status: 'executed',
},
},
});
// receipt is a compact JWS string ready for delivery
console.log(receipt);