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-sdkGateway (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.warningsError 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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
createEvidencePackage() | Seal actions into bundle |
listEvidencePackages() | List packages |
getEvidencePackage(id) | Get detail |
timeTravel(pointInTime) | Point-in-time query |
liabilityChain(actionId) | Multi-hop chain |
Estate
| Method | Description |
|---|---|
setAgentWill(slug) | Set succession plan |
getAgentWill(slug) | Get will |
issueDeathCertificate(slug) | Issue death cert |
getDeathCertificate(slug) | Get cert |
createComplianceSnapshot() | Attestation |
listComplianceSnapshots() | List snapshots |
Escrow
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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)
});