Aira

TypeScript SDK

Install, configure, and use the Aira TypeScript SDK. Two-step authorize and notarize flow, async client, framework integrations for Vercel AI, LangChain.js, and OpenAI Agents.

Installation

npm install aira-sdk

Gateway (zero-code integration)

import OpenAI from "openai";
import { gatewayOpenAIConfig } from "aira-sdk/gateway";

const client = new OpenAI({
  ...gatewayOpenAIConfig({ airaApiKey: "aira_live_..." }),
  apiKey: "sk-...",
});
import Anthropic from "@anthropic-ai/sdk";
import { gatewayAnthropicConfig } from "aira-sdk/gateway";

const client = new Anthropic({
  ...gatewayAnthropicConfig({ airaApiKey: "aira_live_..." }),
  apiKey: "sk-ant-...",
});

Quick Start

Aira uses a two-step flow. Call authorize() before the agent acts to get a policy decision. Call notarize() after the agent executes to mint the receipt.

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",
  });

  if (auth.status === "authorized") {
    // Execute the actual action
    const result = await sendWire(75000, "vendor");

    // Step 2: notarize the outcome and mint the receipt
    const receipt = await aira.notarize({
      actionId: auth.action_uuid,
      outcome: "completed",
      outcomeDetails: `Sent successfully. ref=${result.id}`,
    });
    console.log(receipt.signature); // Ed25519 signature
  } else if (auth.status === "pending_approval") {
    // Held for human review. Agent must wait.
    await queue.enqueue(auth.action_uuid);
  }
} catch (e) {
  if (e instanceof AiraError && e.code === "POLICY_DENIED") {
    console.error(`Blocked: ${e.message}`);
    return;
  }
  throw e;
}

See the Quickstart for more on the three branches (authorized, pending_approval, POLICY_DENIED).

Method Signatures

// Step 1: request authorization
const auth = await aira.authorize({
  actionType: string,
  details: string,
  agentId?: string,
  agentVersion?: string,
  instructionHash?: string,
  modelId?: string,
  modelVersion?: string,
  parentActionId?: string,
  endpointUrl?: string,
  storeDetails?: boolean,
  idempotencyKey?: string,
  requireApproval?: boolean,
  approvers?: string[],
});
// Returns Authorization:
//   auth.action_uuid        (UUID string)
//   auth.status           ("authorized" | "pending_approval")
//   auth.created_at
//   auth.request_id
//   auth.warnings         (string[] | null)
//
// Throws AiraError(code: "POLICY_DENIED") on policy deny.

// Step 2: notarize the outcome after execution
const receipt = await aira.notarize({
  actionId: string,
  outcome?: "completed" | "failed",   // default: "completed"
  outcomeDetails?: string,
});
// Returns ActionReceipt:
//   receipt.action_uuid
//   receipt.status        ("notarized" | "failed")
//   receipt.receipt_uuid    (null when failed)
//   receipt.payload_hash  (null when failed)
//   receipt.signature     (Ed25519, null when failed)
//   receipt.timestamp_token  (RFC 3161, may be null on TSA failure)
//   receipt.created_at
//   receipt.request_id
//   receipt.warnings

Error Handling

import { Aira, AiraError } from "aira-sdk";

try {
  const auth = await aira.authorize({ actionType: "wire_transfer", details: "..." });
} catch (e) {
  if (e instanceof AiraError) {
    if (e.code === "POLICY_DENIED") {
      // Action blocked by policy
      console.error(`Blocked: ${e.message}`);
      console.error(`Policy ID: ${e.details?.policy_uuid}`);
      console.error(`Audit row: ${e.details?.action_uuid}`);
      return;
    }
    if (e.code === "DUPLICATE_REQUEST") return;
  }
  throw e;
}

Agent Registry

// Register
const agent = await aira.registerAgent({
  agentSlug: "support-agent-v2",
  displayName: "Customer Support Agent",
  capabilities: ["email", "chat", "tickets"],
  public: true,
});

// Publish version
await aira.publishVersion({
  slug: "support-agent-v2",
  version: "1.0.0",
  modelId: "claude-sonnet-4-6",
  changelog: "Initial release",
});

// Update
await aira.updateAgent("support-agent-v2", { description: "Updated description" });

// List versions
const versions = await aira.listVersions("support-agent-v2");

// Decommission
await aira.decommissionAgent("old-agent");

// Transfer ownership
await aira.transferAgent("my-agent", { toOrgId: "org-uuid", reason: "M&A" });

Evidence & Discovery

// Create sealed evidence package
const pkg = await aira.createEvidencePackage({
  title: "Q1 2026 Lending Audit Trail",
  actionIds: ["act-uuid-1", "act-uuid-2", "act-uuid-3"],
  description: "All lending decisions for BaFin review",
});

// Time-travel query
const result = await aira.timeTravel({
  pointInTime: "2026-03-20T00:00:00Z",
  agentSlug: "lending-agent",
});

// Liability chain
const chain = await aira.liabilityChain("act-uuid", { maxDepth: 10 });

Agent Estate

// Set succession plan
await aira.setAgentWill({
  slug: "support-agent",
  successorSlug: "support-agent-v3",
  successionPolicy: "transfer_to_successor",
  dataRetentionDays: 2555,
  notifyEmails: ["compliance@acme.com"],
  instructions: "Transfer conversation history. Delete PII after 7 years.",
});

// Get will
const will = await aira.getAgentWill("support-agent");

// Issue death certificate (agent must be decommissioned first)
await aira.decommissionAgent("old-agent");
const cert = await aira.issueDeathCertificate("old-agent", { reason: "Replaced by v3" });

// Compliance snapshot
const snapshot = await aira.createComplianceSnapshot({
  framework: "eu-ai-act",
  agentSlug: "lending-agent",
  findings: { art_12_logging: "pass", art_14_oversight: "pass" },
});

Escrow

// Create account
const account = await aira.createEscrowAccount({
  purpose: "Vendor contract #4521",
  currency: "EUR",
});

// Record commitment
await aira.escrowDeposit(account.id, { amount: 5000.0, description: "Liability commitment" });

// Release
await aira.escrowRelease(account.id, { amount: 5000.0 });

// Dispute
await aira.escrowDispute(account.id, {
  amount: 5000.0,
  description: "Agent error in contract terms",
});

Ask Aira (Chat)

const response = await aira.ask("How many email actions were notarized this week?");
console.log(response.content);
console.log(response.toolsUsed);  // ["count_actions"]

Public Verification

No authentication required:

const result = await aira.verifyAction("action-uuid");
console.log(result.valid);    // true
console.log(result.message);  // "Action receipt exists and signing key is valid."

Trust Layer

Standards-based identity and trust for agents: W3C DIDs, Verifiable Credentials, mutual notarization, and reputation scoring. See the full Trust Layer guide for architecture details.

DID Identity

Every registered agent gets a W3C-compliant DID (did:web):

// DID is assigned on registration
const agent = await aira.registerAgent({
  agentSlug: "my-agent",
  displayName: "My Agent",
  capabilities: ["email", "chat"],
});
// agent.did → "did:web:airaproof.com:agents:my-agent"

// Rotate keys
await aira.rotateAgentKeys("my-agent");

Verifiable Credentials

// Get the agent's W3C Verifiable Credential
const vc = await aira.getAgentCredential("my-agent");

// Verify it
const result = await aira.verifyCredential(vc);
console.log(result.valid);  // true

// Revoke
await aira.revokeCredential("my-agent", { reason: "Agent deprecated" });

Mutual Notarization

For high-stakes actions, both parties co-sign:

// Agent A initiates
const request = await aira.requestMutualSign({
  actionId: "act-uuid",
  counterpartyDid: "did:web:partner.com:agents:their-agent",
});

// Agent B completes
const receipt = await aira.completeMutualSign({
  actionId: "act-uuid",
  did: "did:web:partner.com:agents:their-agent",
  signature: "z...",
  signedPayloadHash: "sha256:...",
});

Reputation Score

const rep = await aira.getReputation("my-agent");
console.log(rep.score);  // 84
console.log(rep.tier);   // "Verified"

Endpoint Verification

await aira.setEndpointPolicy({
  mode: "strict",
  whitelist: ["https://api.stripe.com", "https://api.openai.com"],
});

Framework Integrations

Vercel AI SDK

Use the Aira middleware with the Vercel AI SDK. For each tool step, call authorize() before the tool runs and notarize() after it returns.

import { Aira, AiraError } from "aira-sdk";
import { generateText } from "ai";

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

const result = await generateText({
  model: yourModel,
  tools: yourTools,
  prompt: "Analyze Q1 revenue",
  experimental_prepareStep: async (step) => {
    // authorize the intended tool call before Vercel runs it
    const auth = await aira.authorize({
      actionType: "tool_call",
      agentId: "vercel-agent",
      details: `Invoke ${step.toolCall?.toolName}`,
    });
    if (auth.status !== "authorized") {
      throw new Error(`Tool call not authorized: ${auth.status}`);
    }
    step.meta = { actionId: auth.action_uuid };
  },
  onStepFinish: async (step) => {
    // notarize after the tool returns
    await aira.notarize({
      actionId: step.meta.actionId,
      outcome: "completed",
      outcomeDetails: JSON.stringify(step.toolResults),
    });
  },
});

LangChain.js

import { Aira } from "aira-sdk";
import { AiraCallbackHandler } from "aira-sdk/extras/langchain";

const aira = new Aira({ apiKey: "aira_live_xxx" });
const handler = new AiraCallbackHandler({
  client: aira,
  agentId: "research-agent",
  modelId: "gpt-5.2",
});

// Every tool call and chain completion gets a signed receipt
const result = await chain.invoke(
  { input: "Analyze Q1 revenue" },
  { callbacks: [handler] },
);

OpenAI Agents SDK

import { Aira } from "aira-sdk";
import { AiraGuardrail } from "aira-sdk/extras/openai-agents";

const aira = new Aira({ apiKey: "aira_live_xxx" });
const guardrail = new AiraGuardrail({
  client: aira,
  agentId: "assistant-agent",
});

// Wrap tools — every call and result gets a signed receipt
const search = guardrail.wrapTool(searchTool, { toolName: "web_search" });
const execute = guardrail.wrapTool(codeExecutor, { toolName: "code_exec" });

MCP Server

import { AiraMCPServer } from "aira-sdk/extras/mcp";

const server = new AiraMCPServer({ apiKey: "aira_live_xxx" });
await server.start();

The server exposes four tools: authorize_action, notarize_action, verify_action, and get_receipt. The MCP-connected agent calls authorize_action before acting and notarize_action after.

Add to your MCP client config:

{
  "mcpServers": {
    "aira": {
      "command": "npx",
      "args": ["aira-sdk", "mcp"],
      "env": { "AIRA_API_KEY": "aira_live_xxx" }
    }
  }
}

Offline Mode

Offline mode is for post-hoc audit actions where authorize has already run (or is not required). It queues pending records locally and flushes them when connectivity returns.

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

// Queue a two-step record locally. authorize and notarize are serialized.
const auth = await aira.authorize({
  actionType: "scan_completed",
  details: "Scanned document batch #77",
  agentId: "scanner-agent",
});

// Execute the real work offline...
await aira.notarize({
  actionId: auth.action_uuid,
  outcome: "completed",
  outcomeDetails: "Batch 77 scanned, 412 pages",
});

console.log(aira.queue.pendingCount);

// Flush to API when back online. Policies are evaluated server-side at flush time.
const results = await aira.sync();

Offline mode means policies are not checked until sync() runs. Use it for audit-only workflows, not for gated actions.


Session

Pre-fill defaults for a block of related actions. Every authorize() call within the session inherits the agent identity and model.

const session = aira.session({ agentId: "onboarding-agent", modelId: "claude-sonnet-4-6" });

const auth = await session.authorize({
  actionType: "identity_verified",
  details: "Verified customer ID #4521",
});
if (auth.status === "authorized") {
  await verifyIdentityInDb(4521);
  await session.notarize({
    actionId: auth.action_uuid,
    outcome: "completed",
    outcomeDetails: "KYC check passed",
  });
}

Webhook Verification

Verify that incoming webhooks are authentic Aira events. HMAC-SHA256 signature verification ensures tamper-proof delivery.

import { verifyWebhook, parseEvent } from "aira-sdk/webhooks";

const isValid = verifyWebhook({
  payload: req.body,
  signature: req.headers["x-aira-signature"],
  secret: "whsec_xxx",
});

if (isValid) {
  const event = parseEvent(req.body);
  console.log(event.eventType);  // "action.notarized"
  console.log(event.data);       // Action data with cryptographic receipt
}

Supported event types: action.authorized, action.approval_requested, action.approved, action.denied, action.notarized, action.failed, action.cosigned, agent.registered, agent.decommissioned, evidence.sealed, escrow.deposited, escrow.released, escrow.disputed, compliance.snapshot_created, case.complete, case.requires_human_review.


All Methods

Actions

MethodDescription
authorize()Step 1. Request permission before the agent acts.
notarize()Step 2. Report the outcome and mint the receipt.
getAction(id)Get action detail with policy evaluation and receipt.
listActions()List actions with filters.
approveAction(id)Approve a held action (JWT auth).
denyAction(id)Deny a held action (JWT auth).
cosignAction(id)Human co-signature on a notarized action (JWT auth).
setLegalHold(id)Prevent deletion.
releaseLegalHold(id)Remove legal hold.
getActionChain(id)Chain of custody.
verifyAction(id)Public verification.

Agents

MethodDescription
registerAgent()Register identity
getAgent(slug)Get detail + versions
listAgents()List with status filter
updateAgent(slug)Update metadata
publishVersion(slug)Publish new version
listVersions(slug)List all versions
decommissionAgent(slug)Decommission
transferAgent(slug)Transfer ownership
getAgentActions(slug)Actions by this agent

Evidence

MethodDescription
createEvidencePackage()Seal actions into bundle
listEvidencePackages()List packages
getEvidencePackage(id)Get detail
timeTravel(pointInTime)Point-in-time query
liabilityChain(actionId)Multi-hop chain

Estate

MethodDescription
setAgentWill(slug)Set succession plan
getAgentWill(slug)Get will
issueDeathCertificate(slug)Issue death cert
getDeathCertificate(slug)Get cert
createComplianceSnapshot()Attestation
listComplianceSnapshots()List snapshots

Escrow

MethodDescription
createEscrowAccount()Create account
listEscrowAccounts()List accounts
getEscrowAccount(id)Get detail
escrowDeposit(id, amount)Record commitment
escrowRelease(id, amount)Release commitment
escrowDispute(id, amount, desc)File dispute

Trust Layer

MethodDescription
getAgentDid(slug)Retrieve agent's W3C DID
rotateAgentKeys(slug)Rotate Ed25519 signing keys
getAgentCredential(slug)Get W3C Verifiable Credential
verifyCredential(vc)Verify a Verifiable Credential
revokeCredential(slug)Revoke a credential
requestMutualSign(actionId, did)Initiate mutual notarization
completeMutualSign(...)Complete mutual notarization
getMutualSignStatus(actionId)Check mutual sign status
getReputation(slug)Get reputation score and tier
listReputationHistory(slug)List reputation history
setEndpointPolicy(...)Set endpoint verification policy
getEndpointPolicy()Get current endpoint policy
resolveDid(did)Resolve any DID to DID Document
checkTrust(slug)Run full trust check
listCredentials(slug)List all credentials
getTrustBundle(slug)Get DID + VC + reputation

Chat

MethodDescription
ask(message)Natural language query

Error Handling

import { AiraError } from "aira-sdk";

try {
  const auth = await aira.authorize({
    actionType: "email_sent",
    details: "Send onboarding email",
    agentId: "support-agent",
  });
} catch (e) {
  if (e instanceof AiraError) {
    if (e.code === "POLICY_DENIED") {
      console.log(`Blocked: ${e.message}`);
    } else if (e.code === "PLAN_LIMIT_EXCEEDED") {
      console.log("Monthly operation limit reached");
    } else {
      console.log(`[${e.code}] ${e.message} (status=${e.status})`);
    }
  }
}

See Error Handling for the full list of error codes.

Configuration

const aira = new Aira({
  apiKey: "aira_live_xxx",
  baseUrl: "https://your-self-hosted.com",  // Self-hosted
  timeout: 60000,                            // Request timeout (ms)
});

On this page