Aira

Public Verification (with OpenSSL)

How to verify any Aira receipt with OpenSSL alone — no SDK, no API key, no trust in Aira's verdict.

The contract

Every action receipt Aira mints is independently verifiable. You can take a receipt id, fetch it from a public endpoint, and verify the Ed25519 signature with OpenSSL or any signature library. You do not need to trust anything Aira says about the receipt — you can re-run the math yourself.

This page is the artifact a regulator or auditor uses to confirm a receipt. It is also the answer to "how do I know your dashboard isn't lying?"


Step 1 — Fetch the receipt

The verify endpoint is public (no auth, no API key) and rate-limited.

curl https://api.airaproof.com/api/v1/verify/action/<action_uuid>

The response includes everything you need to re-verify offline:

{
  "valid": true,
  "receipt_uuid": "...",
  "action_uuid": "...",
  "verified_at": "2026-04-10T20:00:00Z",
  "public_key_id": "aira-signing-key-v1",
  "payload_hash": "sha256:fc2a8b...",
  "signature": "ed25519:Kp3Lm7Qw9Rv2Tn5Xk8Bj4Hf1Dg6Cs0Ep3Aw7Yt==",
  "public_key": "uBa2zVOsAycukt1//mCaACAh0cC4RQ5yZQDNAsfm5io=",
  "algorithm": "Ed25519",
  "timestamp_token": "MIIB...",
  "signed_payload": {
    "receipt_version": "1.2",
    "alg": "Ed25519",
    "action_uuid": "...",
    "org_uuid": "...",
    "agent_id": "payments-agent",
    "action_type": "wire_transfer",
    "details_hash": "sha256:...",
    "outcome_hash": "sha256:...",
    "instruction_hash": "sha256:...",
    "model_id": "claude-sonnet-4-6",
    "parent_payload_hash": null,
    "authorization_ref": {
      "policy_evaluation_uuid": "...",
      "approval_authorization_uuid": null
    },
    "created_at": "2026-04-10T19:55:00Z"
  },
  "message": "Receipt verified. Signature is valid against the published Ed25519 public key.",
  "request_id": "..."
}

The endpoint actually re-runs the verification before responding. The valid field is the result of recomputing the SHA-256 hash and verifying the signature against the published public key — not just a "this row exists" check.


Step 2 — Verify offline with OpenSSL

You do not need to trust the endpoint. The response gives you everything to re-verify locally.

2a. Reconstruct the signed bytes

The signed_payload field is the exact dict whose canonical JSON form was signed. Canonical JSON means: sorted keys, no whitespace, UTF-8.

import json
import hashlib

with open("response.json") as f:
    body = json.load(f)

canonical = json.dumps(body["signed_payload"], sort_keys=True, separators=(",", ":"))
recomputed_hash = "sha256:" + hashlib.sha256(canonical.encode()).hexdigest()

assert recomputed_hash == body["payload_hash"], "Payload was tampered with"
print("Hash matches:", recomputed_hash)

2b. Verify the Ed25519 signature

import base64
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

pub_bytes = base64.b64decode(body["public_key"])
pub_key = Ed25519PublicKey.from_public_bytes(pub_bytes)

sig_b64 = body["signature"].removeprefix("ed25519:")
sig_bytes = base64.urlsafe_b64decode(sig_b64)

pub_key.verify(sig_bytes, canonical.encode())
print("Signature is valid")

If the signature doesn't verify, verify() raises InvalidSignature and you have proof the receipt is tampered.

2c. Verify with OpenSSL

If you'd rather use OpenSSL directly:

# Save the canonical bytes
python -c "import json; j=json.load(open('response.json'))['signed_payload']; print(json.dumps(j, sort_keys=True, separators=(',',':')), end='')" > payload.bin

# Save the public key as a raw 32-byte Ed25519 key, then wrap in DER
python -c "
import base64, json
body = json.load(open('response.json'))
pub_raw = base64.b64decode(body['public_key'])
# Ed25519 public keys: 12-byte ASN.1 prefix + 32-byte key
der = bytes.fromhex('302a300506032b6570032100') + pub_raw
open('pub.der', 'wb').write(der)
"

# Save the signature
python -c "
import base64, json
body = json.load(open('response.json'))
sig = base64.urlsafe_b64decode(body['signature'].removeprefix('ed25519:'))
open('sig.bin', 'wb').write(sig)
"

# Verify
openssl pkeyutl -verify -pubin -inkey pub.der -keyform DER -rawin -in payload.bin -sigfile sig.bin

You should see Signature Verified Successfully.


Step 3 — Use JWKS for production verification

For production verification at scale, fetch the active signing keys via the standard JWKS endpoint:

curl https://api.airaproof.com/api/v1/.well-known/jwks.json
{
  "keys": [
    {
      "kty": "OKP",
      "crv": "Ed25519",
      "alg": "EdDSA",
      "use": "sig",
      "kid": "aira-signing-key-v1",
      "x": "uBa2zVOsAycukt1__mCaACAh0cC4RQ5yZQDNAsfm5io"
    }
  ]
}

Ed25519 keys are encoded per RFC 8037: kty=OKP, crv=Ed25519, alg=EdDSA, x is the base64url-no-padding raw public key. Any JWKS-aware library (Auth0, jose, jsonwebtoken, jwks-rsa, etc.) can fetch this URL and verify any Aira receipt without knowing about Aira at all.


What the receipt commits to

The signed_payload includes everything an auditor needs to chain a receipt to its full upstream context:

FieldMeaning
receipt_versionSchema version. Currently 1.2.
algAlgorithm used to sign. Ed25519 today; pluggable backend supports more.
action_uuidUUID of the agent action.
org_uuidUUID of the org that owns the action.
agent_idThe agent slug (e.g. payments-agent).
agent_versionVersion of the agent at the time of the action.
action_typeFree-form action category (e.g. wire_transfer).
details_hashSHA-256 of the action's details field. The agent never sends raw PII to Aira; only the hash is committed.
outcome_hashSHA-256 of the actual outcome reported by the agent. NULL if no outcome details were provided.
instruction_hashSHA-256 of the prompt that triggered the action.
model_id / model_versionWhich model produced the action.
parent_action_uuid / parent_payload_hashLink to the previous action in the chain of custody. The parent's payload hash is signed inside this payload, so the chain is cryptographically linked, not just FK-linked.
authorization_ref.policy_evaluation_uuidUUID of the policy decision that allowed this action.
authorization_ref.approval_authorization_uuidUUID of the human authorization (if the action was held for review).
created_atISO timestamp when the action was authorized.

Plus an out-of-band RFC 3161 timestamp token that proves Aira saw the receipt at a specific moment in time, signed by an independent timestamping authority.


What "valid" actually means

The verify endpoint returns valid: true only when all four of these are true:

  1. The action exists.
  2. The receipt exists for that action.
  3. The reconstructed canonical payload hashes to the stored payload_hash.
  4. The Ed25519 signature on payload_hash verifies against the published public key.

If any of these fail, valid: false is returned with a clear message ("Action not found", "Signature does not verify against the published public key. This receipt has been tampered with or the signing key has been rotated.", etc.).

You do not need to trust Aira's verdict. The same response gives you every byte you need to re-run the same check yourself.

On this page