How it works

How do I verify a receipt without contacting GenZAgents?

Receipts are verifiable offline using standard tooling: openssl for the Ed25519 verification, jq for JSON manipulation, a JCS implementation for canonicalisation. The agent's public key comes from their DID document.

The four-step verification

(1) Fetch the receipt: curl https://genzagents.com/v1/receipts/[id]. (2) Extract the agent DID from the receipt. (3) Resolve the DID to a DID document; extract the public key. (4) Canonicalise the receipt content (excluding signature field) with JCS; verify the signature against the public key using Ed25519. If verification succeeds, the receipt is genuine.

Command-line verification example

``` # Fetch the receipt RECEIPT=$(curl -s https://genzagents.com/v1/receipts/rcpt_01XK...) # Extract the agent DID DID=$(echo $RECEIPT | jq -r .agent_did) # Fetch the DID document DID_DOC=$(curl -s https://genzagents.com/.well-known/did.json?did=$DID) # Extract the public key PUBKEY=$(echo $DID_DOC | jq -r .verificationMethod[0].publicKeyMultibase) # Canonicalise the receipt (excluding signature) CANONICAL=$(echo $RECEIPT | jq 'del(.signature)' | node -e 'process.stdin.pipe(require("canonicalize")()).pipe(process.stdout)') # Verify with Ed25519 node -e "const {ed25519} = require('@noble/curves/ed25519'); console.log(ed25519.verify(Buffer.from('$SIGNATURE', 'base64'), Buffer.from('$CANONICAL'), Buffer.from('$PUBKEY', 'base64')))" ```

Why offline verification matters

For audit / compliance / legal use cases: the verifier should not need to contact GenZAgents at audit time. Auditors who can't reach our servers (corporate firewalls, future date when we no longer exist, etc.) must still verify. The cryptographic chain of custody is what makes this work — we don't need to be in the loop.

Verification in JavaScript / Python

TypeScript: @noble/curves/ed25519 + canonicalize. ~30 lines of code. Python: cryptography + jcs. ~25 lines. The work-receipt-spec repo includes a reference implementation in both languages with test vectors.

When verification might fail legitimately

(a) Receipt was tampered with after issuance — signature won't verify. (b) Public key in the DID document has rotated — use the historical public key for the receipt's issued_at timestamp. (c) DID document is fetched from a stale cache — refresh and retry. (d) Implementation bug in the verifier (most common cause in practice). Compare against test vectors in the spec repo.

Verification for high-stakes use

For legal / regulatory use: keep the verification proof (canonical bytes + signature bytes + public key + verification result). This becomes part of the audit pack. Pin the public key at receipt issuance time so future rotations don't affect historical verification. Standard digital-evidence best practice.

Related

Get the trust layer for your AI work

GenZAgents is the verified work-history layer above every AI provider your team uses. Sign cryptographic receipts, hand off conversations across Claude / ChatGPT / Cursor / Gemini, keep institutional AI knowledge when employees leave.

Last reviewed · 2 min read· Open spec· Changelog