Aira

Compliance Reports — EU AI Act Article 12 / 9 / 6

Same data as a bundle, rendered as a regulator-ready PDF — what your compliance officer hands the regulator.

Report vs Bundle. A report is the human-readable PDF a regulator opens. A Compliance Bundle is the machine-readable JSON evidence behind it. Most teams produce both. See The Five Aira Sub-Products for the full taxonomy.

What it is

A compliance report is a regulator-facing PDF generated by Aira from your action receipts, signed with the same Ed25519 key that signs every receipt and optionally timestamped by an RFC 3161 trusted authority. It complements compliance bundles: bundles are the cryptographic evidence; reports are the human-readable document a regulator can open in any PDF viewer.

Three frameworks are supported in this release:

FrameworkIdentifierOutput
EU AI Act, Article 12eu_ai_act_art12Annex VII-style technical file: every action grouped by agent, mapped to the Article 12 fields the regulation requires (period of use, input/output hashes, system + model identifiers, instruction hash, natural persons in the loop, policy decision chain, tamper evidence).
EU AI Act, Article 9eu_ai_act_art9Risk management register: actions classified into the eight Annex III high-risk categories (biometric, critical infrastructure, education, employment, essential services, law enforcement, migration, justice + a fallback "other"), with sample IDs and the control statements Aira enforces for each category.
EU AI Act, Article 6eu_ai_act_art6Right-to-explanation document for a single action: the policy decision chain (rules, AI, consensus modes), the human approval chain, and the cryptographic receipt.

Generating a report

Article 12 — period-based

curl -X POST https://api.airaproof.com/api/v1/compliance/reports \
  -H "Authorization: Bearer aira_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "framework": "eu_ai_act_art12",
    "period_start": "2026-04-01T00:00:00Z",
    "period_end": "2026-04-30T23:59:59Z"
  }'

The endpoint is synchronous. For periods with thousands of actions the call returns in a few seconds; if you need to generate against year-long periods, queue the work via the dashboard.

The response is the report metadata (status will be ready once generation completes). The PDF is not in the response — fetch it from the download endpoint.

Article 9 — risk register

Same payload as Article 12, just change the framework:

curl -X POST https://api.airaproof.com/api/v1/compliance/reports \
  -H "Authorization: Bearer aira_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "framework": "eu_ai_act_art9",
    "period_start": "2026-04-01T00:00:00Z",
    "period_end": "2026-04-30T23:59:59Z"
  }'

Article 6 — single action

curl -X POST https://api.airaproof.com/api/v1/compliance/reports \
  -H "Authorization: Bearer aira_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "framework": "eu_ai_act_art6",
    "action_uuid": "00000000-0000-0000-0000-000000000001"
  }'

A read-only JSON view of the same explanation is exposed at GET /api/v1/actions/{action_uuid}/explanation. The explanation can also be streamed directly as a PDF via GET /api/v1/actions/{action_uuid}/explanation/pdf.

Downloading the PDF

curl https://api.airaproof.com/api/v1/compliance/reports/{report_uuid}/download \
  -H "Authorization: Bearer aira_live_..." \
  -o aira-report.pdf

The download response carries three custom headers that mirror the row metadata:

  • X-Aira-Content-Hash — SHA-256 of the PDF bytes
  • X-Aira-Signature — Ed25519 signature over the signed descriptor
  • X-Aira-Signing-Key — key identifier (look up in /.well-known/jwks.json)

Article 6 evidence quality (linked vs inferred)

Each entry in the policy_chain of an Article 6 explanation carries an evidence_quality flag so a reader can tell signed evidence apart from lifecycle-derived inferences:

  • linked — the entry was loaded from a signed PolicyEvaluation row whose action_uuid directly references this action. Cryptographic evidence; independently verifiable.
  • inferred — the entry was derived from the action's lifecycle (the status column and any HumanAuthorization rows). Used when no PolicyEvaluation row is linked to the action. The action's outcome is recoverable from the signed receipt + status — but the specific policy that produced the outcome can't be pinpointed.

The Article 6 service derives one of three lifecycle outcomes:

Lifecycle signalInferred decision
status == 'denied_by_policy'deny
Any signed HumanAuthorization existsrequire_approval
Otherwise (notarized / authorized cleanly)allow

This mirrors what the dashboard shows in the action lifecycle timeline, so the PDF and the dashboard never disagree.

In the PDF, inferred entries are tagged Evidence: derived from action lifecycle. The receipt and (when present) the approval chain remain the cryptographic record.

Verifying a report

curl https://api.airaproof.com/api/v1/compliance/reports/{report_uuid}/verify \
  -H "Authorization: Bearer aira_live_..."

Returns:

{
  "report_uuid": "...",
  "valid": true,
  "checks": {
    "content_hash_matches": true,
    "signature_valid": true
  },
  "descriptor": { ... },
  "request_id": "req_..."
}

The verifier recomputes the SHA-256 of the stored PDF bytes and re-derives the canonical descriptor that was signed at generation time, then verifies the signature against the active public key. Both checks must succeed for valid to be true.

SDK usage

Python

from aira import Aira

aira = Aira(api_key="aira_live_...")

report = aira.create_compliance_report(
    framework="eu_ai_act_art12",
    period_start="2026-04-01T00:00:00Z",
    period_end="2026-04-30T23:59:59Z",
)

with open("article12.pdf", "wb") as fh:
    fh.write(aira.download_compliance_report(report.id))

verification = aira.verify_compliance_report(report.id)
assert verification.valid

TypeScript

import { Aira } from "aira-sdk";
import { writeFileSync } from "node:fs";

const aira = new Aira({ apiKey: "aira_live_..." });

const report = await aira.createComplianceReport({
  framework: "eu_ai_act_art12",
  periodStart: "2026-04-01T00:00:00Z",
  periodEnd: "2026-04-30T23:59:59Z",
});

writeFileSync("article12.pdf", await aira.downloadComplianceReport(report.id));

const verification = await aira.verifyComplianceReport(report.id);
console.log("valid:", verification.valid);

Disabling the endpoints

The route group sits behind a feature flag, ENABLE_COMPLIANCE_REPORTS, which defaults to True. Set the env var to false to make every endpoint return 404. Useful for self-hosted operators who don't need PDF reports.

On this page