Receipts & Verification
Cryptographic receipts — your tamper-proof audit trail.
How Receipts Work
Every case execution automatically generates a cryptographic receipt:
- A canonical JSON payload is built from all inputs, outputs, and metadata
- The payload is serialized with sorted keys and no whitespace (deterministic)
- A SHA-256 hash is computed
- The hash is signed with Ed25519 using Aira's signing key
- An RFC 3161 trusted timestamp is obtained from an independent authority
- The receipt is stored in an append-only table (updates and deletes are blocked at the database level)
The result: a tamper-proof artifact that proves what each model said, when, and whether they agreed — verifiable by anyone, without calling Aira.
Receipt Fields
| Field | Description |
|---|---|
receipt_id | Unique receipt identifier |
payload_hash | sha256:... hash of the canonical payload |
signature | ed25519:... digital signature (base64url) |
public_key_id | Which signing key was used (supports rotation) |
timestamp | When the receipt was created (ISO 8601 UTC) |
timestamp_authority | Independent TSA that certified the timestamp |
receipt_version | Schema version (currently 1.0) |
verify_url | Public URL for verification (no auth required) |
Get Receipt
GET /api/v1/receipts/{receipt_id}
Authorization: Bearer aira_live_xxxxxReturns the full receipt with case consensus and all model results.
Export Receipt
GET /api/v1/receipts/{receipt_id}/export
Authorization: Bearer aira_live_xxxxxReturns the receipt as a structured JSON document suitable for auditors.
Verify Receipt (Public)
Verification requires no authentication. Share the URL with anyone who needs to verify.
GET /api/v1/verify/{receipt_id}{
"valid": true,
"receipt_id": "rct_01J8X...",
"verified_at": "2026-03-14T15:00:00Z",
"public_key_id": "aira-signing-key-v1",
"message": "Receipt exists and signing key is valid.",
"request_id": "req_..."
}Offline Verification
You can verify receipts without calling Aira:
- Fetch the public key from
/.well-known/keys - Reconstruct the canonical JSON payload (sorted keys, no whitespace)
- Compute SHA-256 hash
- Verify the Ed25519 signature against the public key
import base64, hashlib, json
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
# 1. Get public key (cache this)
pub_bytes = base64.b64decode(public_key_b64)
public_key = Ed25519PublicKey.from_public_bytes(pub_bytes)
# 2. Reconstruct canonical payload
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
# 3. Verify signature
sig_bytes = base64.urlsafe_b64decode(signature.removeprefix("ed25519:"))
public_key.verify(sig_bytes, canonical.encode()) # Raises on failure
print("Receipt is valid and untampered")Signing Keys
GET /api/v1/.well-known/keysReturns all signing keys (active and retired) with their validity periods:
{
"keys": [
{
"id": "aira-signing-key-v1",
"public_key": "base64-encoded-ed25519-public-key",
"algorithm": "Ed25519",
"status": "active",
"valid_from": "2026-03-01T00:00:00Z",
"valid_until": null
}
]
}Old receipts always verify against their original signing key — key rotation never invalidates existing receipts.
Compliance Mapping
| Regulation | Requirement | How Receipts Satisfy It |
|---|---|---|
| EU AI Act Art. 12 | Automatic event logging | Every case run logged with crypto proof |
| EU AI Act Art. 13 | Transparency | Each model's reasoning preserved |
| EU AI Act Art. 14 | Human oversight | Disagreement triggers review flag |
| EU AI Act Art. 86 | Right to explanation | Full model responses in receipt |
| SR 11-7 | Model benchmarking | Multi-model comparison documented |
| GDPR Art. 22 | Automated decision transparency | Decision path auditable |