Aira

Quickstart

Authorize an agent action, execute it, and mint a cryptographic receipt in under five minutes.

Two ways to integrate

SDK integration — call authorize() before your agent acts, notarize() after. Full control over the policy + receipt lifecycle.

Gateway (zero code) — change one URL in your OpenAI/Anthropic config. Aira sits in the middle as a transparent proxy, authorizing and signing every call automatically. Gateway guide →

The model in one paragraph

Aira is a gate, not a logger. Before the agent does the real-world thing (send a wire, ship a contract, write to a database), it calls authorize(). Aira evaluates every active policy and returns a decision: proceed, hold for human approval, or blocked. Only after the agent actually executes the action does it call notarize() with the outcome. That second call mints a cryptographic receipt that commits to both the original intent and the actual outcome.

1. Create an account

Register your organization and get your first API key:

curl -X POST https://api.airaproof.com/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "dev@acme.com",
    "password": "your-secure-password"
  }'
Response
{
  "org_uuid": "org_01J8X...",
  "user_uuid": "usr_01J8X...",
  "message": "Account created. Check your email to verify.",
  "request_id": "req_a1b2c3d4e5f6"
}

After verifying your email, create an API key from the dashboard or via the API.

Save your API key immediately. It is shown once and cannot be retrieved again.

2. Authorize an action

Before the agent executes, ask Aira for permission. Aira evaluates every active policy and returns an Authorization object.

from aira import Aira, AiraError

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

try:
    auth = aira.authorize(
        action_type="wire_transfer",
        details="Send 75,000 EUR to vendor X",
        agent_id="payments-agent",
    )
except AiraError as e:
    if e.code == "POLICY_DENIED":
        # A policy blocked this action. Do not execute.
        log.error(f"Blocked: {e.message}")
        return
    raise
import { Aira, AiraError } from "aira-sdk";

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

try {
  const auth = await aira.authorize({
    actionType: "wire_transfer",
    details: "Send 75,000 EUR to vendor X",
    agentId: "payments-agent",
  });
} catch (e) {
  if (e instanceof AiraError && e.code === "POLICY_DENIED") {
    console.error(`Blocked: ${e.message}`);
    return;
  }
  throw e;
}

The authorization response carries a status. There are three branches.

3. Handle the three branches

Branch A: authorized

Policies passed. The agent is cleared to execute. After the real-world action completes, call notarize() to mint the receipt.

if auth.status == "authorized":
    # Execute the actual action
    result = send_wire(75000, to="vendor")

    # Step 2: notarize the outcome and mint the receipt
    receipt = aira.notarize(
        action_uuid=auth.action_uuid,
        outcome="completed",
        outcome_details=f"Sent successfully. ref={result.id}",
    )
    print(receipt.signature)  # Ed25519 signature
if (auth.status === "authorized") {
  const result = await sendWire(75000, "vendor");

  const receipt = await aira.notarize({
    actionId: auth.action_uuid,
    outcome: "completed",
    outcomeDetails: `Sent successfully. ref=${result.id}`,
  });
  console.log(receipt.signature); // Ed25519 signature
}

If the execution fails, pass outcome="failed" instead. No receipt is minted, but the failure is recorded for the audit trail.

Branch B: pending_approval

A policy decided a human must approve. The action is held server-side and an email goes to your configured approvers. The agent must not execute until the human approves. Once approved, status moves to approved and the agent (or a webhook handler) can call notarize().

elif auth.status == "pending_approval":
    # Held for human review. Agent must wait.
    # When the human approves, the action moves to status="approved".
    # A webhook handler (or a worker) then calls notarize() to mint the receipt.
    queue.enqueue(auth.action_uuid)
else if (auth.status === "pending_approval") {
  // Held for human review. Agent must wait.
  // When the human approves, the action moves to status="approved".
  // A webhook handler (or a worker) then calls notarize() to mint the receipt.
  await queue.enqueue(auth.action_uuid);
}

See Human Approval for the full held-action flow.

Branch C: POLICY_DENIED

A deny policy matched. The SDK raises AiraError with code POLICY_DENIED. The agent must not execute and there is nothing to notarize. Log and move on.

Handled in the try/except block at the top of step 2.

4. Verify a receipt

Anyone can verify a minted receipt. No authentication required.

curl https://api.airaproof.com/api/v1/verify/action/act_01J8X...
Response
{
  "valid": true,
  "receipt_uuid": "rct_01J8X...",
  "verified_at": "2026-04-07T14:35:00Z",
  "public_key_id": "aira-signing-key-v1",
  "message": "Action receipt exists and signing key is valid."
}

Share the verify URL with your auditor, regulator, or compliance team.

5. Register an agent (optional)

Give your agent a verifiable identity. Once registered, Aira validates the agent on every authorize() call. Decommissioned agents are automatically blocked.

curl -X POST https://api.airaproof.com/api/v1/agents \
  -H "Authorization: Bearer aira_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_slug": "payments-agent",
    "display_name": "Payments Agent",
    "capabilities": ["wire_transfer", "ach", "refund"],
    "public": true
  }'

6. Run a consensus case (separate API)

Cases are a separate API for multi-model decision evaluation. They are not the same as the policy consensus mode, even though they share the underlying scoring machinery.

curl -X POST https://api.airaproof.com/api/v1/cases \
  -H "Authorization: Bearer aira_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "details": "Should we approve a credit application for a customer with credit score 742?",
    "models": ["gpt-5.4", "claude-sonnet-4-6", "gemini-3.1-flash-lite"],
    "options": { "human_review_threshold": 0.4 }
  }'

See Cases for the full reference.

What's next

On this page