Skip to main content
Version: v0.10.13

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>

The JWS header identifies the signing algorithm, wire format version, and the key used to sign.

JWS Header
{
"alg": "EdDSA",
"typ": "peac-receipt/0.1",
"kid": "peac-2026-02"
}
FieldValueDescription
algEdDSAEd25519 signature algorithm (RFC 8032)
typpeac-receipt/0.1Wire format identifier
kidKey ID stringIdentifies the signing key in the issuer's JWKS
Wire format frozen

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.

Receipt Payload
{
"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

ClaimRequiredDescription
issYesIssuer URL -- the service that signed the receipt
subYesSubject identifier -- who or what the receipt is about
audNoIntended audience URL
iatYesIssued-at timestamp (Unix seconds)
jtiYesUnique receipt identifier (must be globally unique)
expNoExpiration timestamp (Unix seconds)

PEAC-specific claims

All protocol-specific data lives inside the peac object.

ClaimRequiredDescription
typeYesInteraction type (e.g., api.request, tool.call, payment)
attestation_typeYesOne of: interaction, payment, consent, identity
statusYesLifecycle stage of the interaction
versionYesProtocol 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.

StatusMeaningExample
proposedIntent declared, not yet acted onPayment intent created
authorizedAuthorized to proceedAPI key validated
deniedExplicitly deniedRate limit exceeded
executedSuccessfully completedTool call returned results
erroredCompleted with errorUpstream service failure
tip

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 Response
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:

TransportDelivery mechanism
HTTPPEAC-Receipt response header
MCPResponse _meta field
gRPCTrailing metadata
WebSocketMessage 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.

Extension Example
{
"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"
}
}
}
}
note

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.

issue.ts
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);

Next steps