01Overview
An Adjuro receipt is a JOSE-standard JWS (JSON Web Signature) issued the moment an AI voice agent places a call on behalf of a brand. It is a sworn attestation — adjurō, "I attest under oath" — that binds five facts together under a single Ed25519 signature: who the agent is, what consent authorized the call, on whose behalf it was placed, under what scope and jurisdiction, and at what instant.
A receipt is evidence of the tenant's attestation; it is not, by itself, proof that the recipient consented. Its value is that the attestation is cryptographically non-repudiable and independently verifiable: a TCPA defense attorney, a compliance dashboard, or opposing counsel can verify it against Adjuro's published keys without trusting — or even contacting — Adjuro at verification time.
What a verifier learns
The signature proves the receipt payload was attested byte-for-byte by the holder of a specific Adjuro signing key at issuance time, and has not been altered since. Everything a relying party needs to make that determination is in the receipt and the public JWKS — no Adjuro account, no API call to us, no network dependency on our uptime.
02Receipt payload schema
Receipts use dual naming: each JOSE-standard claim (RFC 7519) is paired with a human-readable alias carrying the identical value. Both are populated on every receipt. Verifier code may use either set; non-developer compliance reviewers and FRE 901 evidence packets benefit from the human-readable form.
| Purpose | JOSE-standard | Human-readable alias | Type / Format |
|---|---|---|---|
| Issuer URL | iss | issued_by | string (URL), same value in both |
| Receipt opaque ID | jti | receipt_id | string (adj_rcpt_<base32>), same value |
| Issuance instant | iat | issued_at | iat = unix seconds (int); issued_at = RFC 3339 (string). Same instant. |
| Expiry instant | exp | expires_at | exp = unix seconds (int); expires_at = RFC 3339 (string). Same instant. |
| Replay nonce | nonce | replay_token | string (16+ chars opaque), same value |
Adjuro-specific claims
These are not JOSE-standard and are single-named:
| Field | Type | Description |
|---|---|---|
tenant_id | uuid string | The Adjuro tenant that authorized this receipt |
trust_root_id | string | The federation trust root that signed (e.g., adjuro-root-2026w20) |
agent_id | string | adj:<base32-blake3> — deterministic from tenant + agent name |
brand | string | Display name of the brand the call is made on behalf of |
event_type | enum | V0: voice_call. Reserved: api_request, chat_message, email_send, payment_auth, workflow_action |
caller_number | E.164 string | Caller side, plaintext (no PII concern on the caller side) |
callee_hash | base64url string | HMAC-SHA256 of callee E.164 under the per-tenant salt — recipient's plaintext number is never received or stored |
campaign_id | string | Customer-supplied opaque reference (indexed for audit queries) |
consent_id | string | Customer-supplied opaque CRM reference — Adjuro stores but never resolves |
scope | string[] | V0 enum: account_servicing, appointment_reminder, debt_collection, marketing, support, verification |
jurisdiction | string | ISO 3166-2 (e.g., US-CA, GB, DE-BY) |
source_url | string | null | Optional customer-controlled URL pointing to the consent artifact |
03JWS protected header
Every receipt carries this protected header:
alg: EdDSA— pure Ed25519 per RFC 8037. Verifiers MUST reject any receipt whosealgis not exactlyEdDSA(algorithm-confusion defense).kid— the signing-key id from Adjuro'ssigning_keyshistory at issuance time. Used to resolve the public key in the JWKS. Keys rotate every 14 days with a 48-hour overlap.typ: JWT— for out-of-the-box compatibility with standard JOSE libraries on the verify path.
Load-bearing: pure Ed25519, not pre-hash (D15)
Adjuro signs with ED25519_SHA_512 (pure Ed25519, RFC 8032 §5.1), never ED25519_PH_SHA_512 (pre-hash). JOSE RFC 8037 defines alg: EdDSA as pure mode, and there is no registered JWS identifier for pre-hash Ed25519 — so pre-hash would break every standard JOSE library (jose, python-jose, go-jose). The HSM signs the actual receipt bytes, not an application-computed hash, which also yields a cleaner FRE 901 evidentiary statement: "AWS KMS attested to this specific receipt payload byte-for-byte."
04Verification algorithm
Verification is fully offline against the published JWKS. The only network fetch — the JWKS itself — is cacheable for 5 minutes at the CDN edge and can be pinned for air-gapped review. Steps 7 and 8 are optional hardening.
The open-source verifier SDK (github.com/adjuro/sdk, Apache 2.0, single-file Node + Python + Go) implements exactly this algorithm. Because alg: EdDSA is a JOSE standard, a hand-rolled verifier using any compliant JOSE library also works without an Adjuro dependency.
05Trust model
Adjuro is designed as a federated trust network, not a single issuer. The long-term shape is "Let's Encrypt for AI agents": multiple operators run their own signing roots, and Adjuro runs the verifier registry that lets any relying party resolve and trust them.
Trust roots
A receipt's trust_root_id names the signing root that attested it. Each root publishes its own keys; a verifier decides which roots it accepts. Three roots ship in V0:
adjuro-root-2026w20— active. The production Adjuro signing root, rotated every 14 days (the2026w20suffix is ISO year + week number).vapi-root-reserved— reserved placeholder for the Vapi platform to run its own root.retell-root-reserved— reserved placeholder for the Retell platform.
When a voice-agent platform operates its own signing root, every receipt it issues verifies through the same registry with no retrofit on the verifier side. A separate adjuro-degraded-2026w20 key signs degraded-mode entries (issued when signing is briefly unavailable and the call proceeds unsigned) — kept distinct from the production root so degraded evidence is never confused with a clean attestation.
Why federation is the structural defense
Because Adjuro runs the verifier registry rather than being the only issuer, it stays neutral across trust boundaries — an incumbent bundling identity into its own platform cannot replicate cross-operator neutrality without becoming a neutral registry itself. Verification is winner-take-most once a transparency log plus federation reach critical mass.
06Schema stability commitment
Frozen at V0 ship. Every field name in §02 — the JOSE-standard set (iss, jti, iat, exp, nonce), the human-readable alias set (issued_by, receipt_id, issued_at, expires_at, replay_token), and every Adjuro-specific field — is a public contract. Changing any field name after V0 ships breaks every deployed verifier in the wild.
Allowed without a breaking change
- Adding new fields to the payload (verifiers ignore unknown fields per JOSE convention).
- Adding new enum values to
scopeorevent_type(existing values keep working). - Adding new top-level Adjuro-specific claims.
Requires a breaking-version bump + multi-year deprecation window
- Renaming any existing field.
- Removing any existing field.
- Changing a field's type or format — the dual-naming convention exists specifically so neither representation of a timestamp ever needs to change.
- Removing enum values from
scopeorevent_type.
This commitment is enforced in CI by the integration test "receipt payload contains both JOSE-standard and human-readable claim names". If anyone ever drops a field, that test fails immediately.