Issued before execution
A receipt is created at authorization time, before the action runs. Infrastructure verifies the receipt at execution time. No receipt, no action.
The complete format for the AuthorityReceipt primitive, the signed artifact that proves an AI action was authorized before it happened.
Spec v1
Each gated action authorized through Permission Protocol produces an AuthorityReceipt: a signed document that any enforcement point can verify independently. This page defines the complete field schema, signature algorithm, verification API, and lifecycle rules.
The primitive that separates authorization from execution.
A receipt is created at authorization time, before the action runs. Infrastructure verifies the receipt at execution time. No receipt, no action.
Every receipt is signed with an Ed25519 key controlled by the issuing Permission Protocol authority. The signature is verifiable offline or via the API.
The receipt ID travels with the action, in CI environment variables, API headers, database columns. Any enforcement point can independently resolve and verify it.
A complete authority receipt issued for a production deployment.
{
"receipt_id": "pp_r_8f91c2a4b3d7e1",
"version": "1",
"actor": "deploy-bot",
"action": "deploy",
"resource": "billing-service:production",
"approved_by": "sarah.kim",
"policy": "production-deploy",
"context": {
"pull_request": 184,
"commit_sha": "a3f9c2b1",
"environment": "production"
},
"timestamp": "2026-03-03T10:14:22Z",
"expires_at": null,
"authority_issuer": "permissionprotocol.com",
"organization_id": "org_acme_corp",
"shareable": true,
"signature": "pp_sig_MEQCIBx4r..."
}// GET /v1/receipts/pp_r_8f91c2a4b3d7e1/verify
{
"verified": true,
"receipt_id": "pp_r_8f91c2a4b3d7e1",
"action": "deploy",
"resource": "billing-service:production",
"approved_by": "sarah.kim",
"timestamp": "2026-03-03T10:14:22Z",
"expires_at": null,
"reason": null
}
// When not verified:
{
"verified": false,
"reason": "expired" | "invalid_signature"
| "not_found" | "revoked"
}Complete schema for the AuthorityReceipt object.
receipt_idrequiredstringGlobally unique identifier for this receipt. Format: pp_r_<base58>. Stable across verification calls and suitable for use as a foreign key in audit logs.
versionrequiredstringReceipt schema version. Current value: "1". Increment is a breaking change; consumers must handle unknown versions gracefully.
actorrequiredstringIdentifier of the AI agent or system that requested the action. Typically a service account name, agent ID, or bot username.
actionrequiredstringThe action that was authorized. Recommended values: "deploy", "merge", "delete", "transfer", "access", "execute". Custom values are permitted.
resourcerequiredstringThe target of the authorized action. Use colon-separated namespace:resource:environment notation where applicable. Example: billing-service:production.
approved_byrequiredstring | nullUsername or identity of the human approver. null when authorization was issued by a policy engine with no human-in-the-loop.
policystring | nullThe named policy under which the action was evaluated. null when no explicit policy is configured. Policies are defined in your Permission Protocol dashboard.
contextobject | nullCaller-supplied key-value metadata attached to the receipt at issuance. Common keys: pull_request, commit_sha, environment, run_id. Not interpreted by Permission Protocol.
timestamprequiredstring (ISO 8601)UTC timestamp of receipt issuance in ISO 8601 format. Example: 2026-03-03T10:14:22Z. Set by Permission Protocol at authorization time; not caller-controlled.
expires_atstring (ISO 8601) | nullOptional expiry time after which the receipt is no longer valid for enforcement. Null means the receipt does not expire. Verification will return verified: false after this time.
authority_issuerrequiredstringDomain of the Permission Protocol instance that issued the receipt. Default: "permissionprotocol.com".
organization_idrequiredstringYour Permission Protocol organization identifier. Scopes the receipt to your tenant.
shareablerequiredbooleanWhen true, the receipt proof page at /r/:receipt_id is publicly viewable without authentication. When false, viewing requires an authenticated session in the issuing organization.
signaturerequiredstringEd25519 signature. The signed message is the SHA-256 digest of the canonical payload bytes, a frozen field set with sorted keys and no whitespace. The exact field set, the byte construction, and the verify procedure are in the open receipt specification at github.com/permission-protocol/receipt-spec.
How receipts are signed and how enforcement points verify them.
Receipts are signed using Ed25519. The signed message is the SHA-256 digest of the canonical payload bytes: a frozen field set, keys sorted, no whitespace. The exact field set, the byte construction, and the verify procedure are in the open receipt specification.
The issuer key set is published at /.well-known/permission-protocol/keys.json on app.permissionprotocol.com. Each receipt names its key id, and a verifier resolves the key by that id, so rotation never invalidates an issued receipt. No key has been rotated in production so far.
Enforcement points must treat any non-verified: true response as a denied action, including network errors, timeouts, and unknown reasons. The authority layer is fail-closed by design.
import { verify } from "permission-protocol";
// At your enforcement point
const result = await verify(receipt_id);
if (!result.verified) {
throw new Error(`Action blocked: ${result.reason}`);
}
// Proceed with the authorized action
await deploy(result.resource);from permission_protocol import verify
result = verify(receipt_id)
if not result.verified:
raise PermissionDenied(
f"Action blocked: {result.reason}"
)
# Proceed with the authorized action
deploy(result.resource)States a receipt moves through from issuance to enforcement.
pendingAuthorization request submitted. Waiting for human approval or policy evaluation. Action cannot proceed.
issuedReceipt has been signed and issued. The action is authorized. Enforcement points will return verified: true.
expiredThe receipt has passed its expires_at timestamp. Verification returns verified: false. A new authorization is required.
revokedThe receipt was explicitly revoked by an authorized admin. Verification returns verified: false. Cannot be reinstated.
Common questions about authority receipts, verification, and enforcement.
An authority receipt is a signed, portable artifact that proves a specific action was explicitly authorized before it was executed. It records who requested the action, who approved it, under which policy, and when, all bound together with a cryptographic signature that any enforcement point can verify.
An audit log records what happened after the fact. An authority receipt is issued before execution and travels with the action: it is the proof the infrastructure checks before allowing the action to proceed. The receipt exists at authorization time; the audit log references it.
Yes. When a configured policy clears an action automatically, the receipt is issued with approved_by set to null and policy set to the matched policy name. The receipt is still cryptographically signed and verifiable. Teams typically require human approval for production environments and allow policy-engine approval for staging.
Call GET /v1/receipts/:receipt_id/verify or use the SDK's verify() function. The response includes verified: true|false and a reason when false. Enforcement points should treat any non-verified receipt as a denied action and fail closed.
Verification returns verified: false with reason: 'expired'. Enforcement points fail closed. Receipts cannot be renewed; a new authorization request must be submitted.
Paid plans are designed for long-horizon receipt retention to support compliance and audit requirements. The receipt ID remains stable and resolvable for the lifetime of the record.
Yes, via the context field at issuance. Permission Protocol stores the context verbatim and includes it in the receipt. Common uses: pull request number, commit SHA, deployment run ID, environment name.
Ed25519 over the SHA-256 digest of the canonical payload bytes. The issuer key set is published at https://app.permissionprotocol.com/.well-known/permission-protocol/keys.json, and each receipt names its key id, so a verifier resolves the key by id rather than assuming one current key. Rotation is a documented procedure; no key has been rotated in production so far.