Documentation
SDKs, integrations, and API reference for the agent identity and trust layer.
Guides
Integrations
API reference
Quick Start
Get up and running with Vorim AI in 3 steps. Register an agent, check permissions, and emit audit events.
// Run as an ES module: a .mjs file, "type": "module" in package.json, or npx tsx file.ts
// Your API key needs agents:read, agents:write, permissions:read, audit:read and audit:write.
import createVorim from '@vorim/sdk';
// 1. Initialize with your API key
const vorim = createVorim({
apiKey: 'agid_sk_live_your_api_key_here',
// baseUrl defaults to the hosted API; set it only for self-hosted.
});
// 2. Register an agent
const agent = await vorim.register({
name: 'InvoiceBot',
description: 'Processes and sends invoices',
capabilities: ['api_access', 'email_send'],
scopes: ['agent:read', 'agent:write', 'agent:communicate'],
});
console.log('Agent ID:', agent.agent.agent_id);
console.log('Private Key:', agent.private_key); // Save this! Shown only once.
// 3. Check permissions before performing actions
const check = await vorim.check(agent.agent.agent_id, 'agent:write');
if (check.allowed) {
// Perform the action...
// 4. Emit an audit event
await vorim.emit({
agent_id: agent.agent.agent_id,
event_type: 'api_request',
action: 'POST /invoices',
resource: 'invoices',
result: 'success',
latency_ms: 42,
});
}typescriptInstallation
Install the SDK using npm, yarn, or pnpm. Zero dependencies — types are bundled.
# npm
npm install @vorim/sdk
# yarn
yarn add @vorim/sdk
# pnpm
pnpm add @vorim/sdkbashThe SDK requires Node.js 18+ or any modern browser with the Fetch API and Web Crypto API.
Full type definitions included. The SDK re-exports types from @vorim/shared-types for convenience.
Agent Onboarding (Device Flow)
Your AI coding agent can set up Vorim for you. Instead of pasting an API key, the agent runs the OAuth 2.0 Device Authorization Grant (RFC 8628): it starts the flow, shows you a short code and a link, you approve once in the browser, and the agent receives a scoped key. The agent never mints a credential on its own. A signed in, email verified human approves before any key exists, and the key is clamped to a strict scope ceiling (agent registration and audit only).
The one call helper runs the approve and poll loop and, with registerAgent, also registers a first agent identity with the new key. It fails closed: it raises on denial, expiry, or timeout, and never returns a partial credential.
import { deviceLogin } from '@vorim/sdk';
// No API key needed to start. The agent runs this; you approve in the browser.
const { api_key, agent } = await deviceLogin({
clientName: 'my-app',
registerAgent: true, // also register a first agent identity with the new key
onUserCode: ({ user_code, verification_uri }) => {
// Show the human this, verbatim:
console.log(`Approve at ${verification_uri} and enter code ${user_code}`);
},
});
console.log('API key:', api_key); // scopes: agents:read, agents:write, permissions:read, audit:write
console.log('Agent:', agent?.agent.agent_id); // first agent, ready to use
console.log('Private key:', agent?.private_key); // shown once, store ittypescriptThe Python SDK mirrors it with device_login(register_agent=True).
from vorim import device_login
result = device_login(
client_name="my-app",
register_agent=True,
on_user_code=lambda i: print(
f"Approve at {i['verification_uri']} and enter code {i['user_code']}"
),
)
api_key = result["api_key"]
agent = result["agent"] # first agent identity, with its one-time private_keypythonIn an MCP client (Claude Desktop, Cursor) the agent drives it natively with the vorim_onboard_start and vorim_onboard_check tools, with no key required to begin (server version 1.1.16 or later). Set VORIM_API_KEY afterwards for the other tools. Or point any agent at the machine readable guide:
# Paste one line into your coding agent:
Read https://vorim.ai/agents.md and follow the device-flow bootstrap to get an
API key, then integrate per https://vorim.ai/docsbashThe unauthenticated start endpoint creates a pending request, not a credential. Only the hashes of the device and user codes are stored. The code is single use and expires in 10 minutes. The minted key is returned exactly once and is revocable in Settings. It expires in 90 days by default, and you can request a shorter lifetime at start, down to one day, so a short-lived task agent gets a key that dies with the task. The server caps the request at 90 days, so a malformed one can never mint a longer-lived key than policy allows. The agent automates the plumbing. It does not replace your consent.
Framework Integrations
The SDK ships with first-class integrations for popular AI agent frameworks. Each integration provides permission-checked tool execution and automatic audit trail emission via subpath imports.
| Framework | Import | Key exports |
|---|---|---|
| LangChain / LangGraph | @vorim/sdk/integrations/langchain | wrapTool, wrapTools, VorimCallbackHandler, createVorimAgent |
| Coinbase AgentKit | @vorim/sdk/integrations/langchain | wrapTools over getLangChainTools(agentKit) — gate + sign each onchain action |
| OpenAI Function Calling | @vorim/sdk/integrations/openai | VorimToolRegistry, runAgentLoop, createVorimOpenAIAgent |
| CrewAI | @vorim/sdk/integrations/crewai | registerCrew, emitCrewTaskEvent, verifyCrewTrust |
| LlamaIndex | @vorim/sdk/integrations/llamaindex | wrapTool, wrapTools, createVorimAgent |
| Anthropic / Claude | @vorim/sdk/integrations/anthropic | VorimToolRegistry, runAgentLoop, createVorimClaudeAgent |
Connectors: card-issuing payment rails and regulated-industry systems of record. Each one gates and signs each mutating action an agent takes, with a spend ceiling or a capability cap and escalation to a named human. See the full integrations catalog for details.
| Connector | Import | What it guards |
|---|---|---|
| Anthropic Commerce Agents | @vorim/sdk/integrations/commerce-agents | Cart writes and checkout handoff by scope; the live apply_change escalates to a named human |
| Stripe Issuing | @vorim/sdk/integrations/stripe-issuing | Sign every real-time card authorization against a spend ceiling |
| Lithic | @vorim/sdk/integrations/lithic | Sign each Auth Stream Access approve / decline; single-use cards |
| Marqeta | @vorim/sdk/integrations/marqeta | Sign each JIT Funding decision (enterprise issuing) |
| Coinbase CDP Wallet | @vorim/sdk/integrations/coinbase-cdp | Value ceiling + network / destination allowlists per broadcast |
| Ramp | @vorim/sdk/integrations/ramp | Cap and sign each virtual-card limit provisioning action |
| Guidewire (insurance) | @vorim/sdk/integrations/guidewire | Dollar ceiling on claim payments / reserves; adjuster escalation |
| Benchling / Veeva (life sciences) | @vorim/sdk/integrations/life-sciences | Draft-not-release; Part 11 human-of-record signature |
| HL7 FHIR (healthcare) | @vorim/sdk/integrations/fhir | Read-only / resource caps; unsigned-order clinician escalation |
| iManage / NetDocuments (legal) | @vorim/sdk/integrations/legal | Narrow-not-widen access; supervising-lawyer escalation |
| Okta (Management API) | @vorim/sdk/integrations/okta | Gate + sign admin writes; role grants / token mints escalate to a named admin |
| Microsoft Entra ID (Graph) | @vorim/sdk/integrations/entra | Directory-role / app-secret / app-role grants escalate to a named admin |
| AWS IAM / STS | @vorim/sdk/integrations/aws-iam | Access-key mints and role/policy privilege changes escalate to a named admin |
LangChain / LangGraph
Wrap tools with permission checks, attach a callback handler for observability, or use the agent factory to register + wrap in one call.
import createVorim from "@vorim/sdk";
import { wrapTools, VorimCallbackHandler, createVorimAgent } from "@vorim/sdk/integrations/langchain";
const vorim = createVorim({ apiKey: "agid_sk_live_..." });
// Option 1: Wrap existing tools
const guardedTools = wrapTools([searchTool, analysisTool], {
vorim,
agentId: "agid_acme_a1b2c3d4",
permissionMap: { search_docs: "agent:read" },
});
// Option 2: Callback handler (observability only, non-blocking)
const handler = new VorimCallbackHandler(vorim, "agid_acme_a1b2c3d4");
await agent.invoke({ messages }, { callbacks: [handler] });
// Option 3: Full agent factory (register + wrap + observe)
const { agentId, tools, callbackHandler } = await createVorimAgent({
vorim,
name: "research-agent",
capabilities: ["web_search"],
scopes: ["agent:read", "agent:execute"],
tools: [searchTool, analysisTool],
});typescriptCoinbase AgentKit
Coinbase AgentKit gives an agent an onchain wallet and a set of action providers (ERC-20 transfer, swap, contract call, mint). It exposes those actions as LangChain tools, so you gate them with the same wrapTools you use for any LangChain agent: every onchain action is permission-checked and recorded as a signed audit event before it runs. Whichever action the model picks, Vorim is the one identity and signed-audit layer over it.
import createVorim from "@vorim/sdk";
import { wrapTools } from "@vorim/sdk/integrations/langchain";
import { getLangChainTools } from "@coinbase/agentkit-langchain";
const vorim = createVorim({ apiKey: "agid_sk_live_..." });
// AgentKit exposes its action providers as LangChain tools...
const agentKitTools = await getLangChainTools(agentKit);
// ...and Vorim gates + signs each one before it executes.
const guardedTools = wrapTools(agentKitTools, {
vorim,
agentId: "agid_acme_a1b2c3d4",
permissionMap: {
// an onchain transfer / swap moves value → agent:transact
native_transfer: "agent:transact",
erc20_transfer: "agent:transact",
// reads (balances, price feeds) → agent:read
get_balance: "agent:read",
},
// Growth+: link every action to a runtime allow/deny/escalate decision.
useRuntimeControl: true,
});typescriptAgentKit's Agentic Wallets already enforce their own session and per-transaction spend caps. Vorim adds what they don't: a cryptographic identity that stays the same across every tool the agent touches, and an independently verifiable, exportable record of every onchain action. For the wallet-broadcast layer underneath AgentKit, the @vorim/sdk/integrations/coinbase-cdp guard adds a value ceiling and network / destination allowlists per broadcast.
OpenAI Function Calling
Use VorimToolRegistry for permission-checked tool execution with OpenAI chat completions.
import OpenAI from "openai";
import createVorim from "@vorim/sdk";
import { VorimToolRegistry, runAgentLoop } from "@vorim/sdk/integrations/openai";
const vorim = createVorim({ apiKey: "agid_sk_live_..." });
const openai = new OpenAI();
const registry = new VorimToolRegistry({ vorim, agentId: "agid_acme_a1b2c3d4" });
registry.add({
name: "search_docs",
description: "Search internal documents",
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
execute: async ({ query }) => searchDocs(query),
permission: "agent:read",
});
// Use with chat completions
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages,
tools: registry.toOpenAITools(),
});
// Execute tool calls — permission checked + audited automatically
const toolMessages = await registry.executeToolCalls(
response.choices[0].message.tool_calls ?? []
);typescriptCrewAI
Register an entire crew with Vorim. Each member gets a unique identity, and delegation permissions are auto-granted.
import createVorim from "@vorim/sdk";
import { registerCrew, emitCrewTaskEvent, verifyCrewTrust } from "@vorim/sdk/integrations/crewai";
const vorim = createVorim({ apiKey: "agid_sk_live_..." });
const crew = await registerCrew(vorim, {
crewName: "content-pipeline",
members: [
{ role: "researcher", name: "crew-researcher", capabilities: ["web_search"], scopes: ["agent:read", "agent:execute"] },
{ role: "writer", name: "crew-writer", capabilities: ["file_write"], scopes: ["agent:read", "agent:write"] },
{ role: "editor", name: "crew-editor", capabilities: ["review"], scopes: ["agent:read", "agent:write"], allowDelegation: true },
],
});
// Verify trust before running
const trustReport = await verifyCrewTrust(vorim, crew);
// Audit each task
await emitCrewTaskEvent(vorim, {
role: "researcher",
agentId: crew.getMember("researcher").agentId,
task: "research_competitors",
tool: "web_search",
result: "success",
latencyMs: 3200,
});typescriptLlamaIndex
Wrap LlamaIndex tools with permission checks. Drop-in replacement for any BaseTool.
import createVorim from "@vorim/sdk";
import { wrapTool, createVorimAgent } from "@vorim/sdk/integrations/llamaindex";
const vorim = createVorim({ apiKey: "agid_sk_live_..." });
// Wrap a single tool
const guarded = wrapTool(searchTool, {
vorim,
agentId: "agid_acme_a1b2c3d4",
permissionMap: { search: "agent:read" },
});
// Or use the agent factory
const { agentId, tools } = await createVorimAgent({
vorim,
name: "research-agent",
capabilities: ["search", "write"],
scopes: ["agent:read", "agent:write", "agent:execute"],
tools: [searchTool, writeTool],
permissionMap: { search: "agent:read", write: "agent:write" },
});
// const agent = new OpenAIAgent({ tools });typescriptAnthropic / Claude
Use VorimToolRegistry for permission-checked tool execution with Claude's tool use API.
import Anthropic from "@anthropic-ai/sdk";
import createVorim from "@vorim/sdk";
import { VorimToolRegistry, runAgentLoop } from "@vorim/sdk/integrations/anthropic";
const vorim = createVorim({ apiKey: "agid_sk_live_..." });
const anthropic = new Anthropic();
const registry = new VorimToolRegistry({ vorim, agentId: "agid_acme_a1b2c3d4" });
registry.add({
name: "search_docs",
description: "Search internal documents",
input_schema: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
execute: async ({ query }) => searchDocs(query),
permission: "agent:read",
});
// Use with Claude messages API
const response = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages,
tools: registry.toAnthropicTools(),
});
// Execute tool_use blocks — permission checked + audited automatically
const toolResults = await registry.executeToolUseBlocks(
response.content.filter(b => b.type === "tool_use")
);
// Or use the full agent loop
const answer = await runAgentLoop({
vorim,
agentId: "agid_acme_a1b2c3d4",
anthropic,
model: "claude-sonnet-4-20250514",
systemPrompt: "You are a helpful assistant.",
registry,
userMessage: "Find docs about onboarding",
});typescriptMCP Server
The Vorim MCP server exposes Vorim operations as tools over stdio. Agents can register themselves, check permissions, log actions and verify trust in plain language. It works in any MCP client that runs local stdio servers, including Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Google Antigravity, Codex CLI and the OpenAI Agents SDK (through MCPServerStdio). Set VORIM_API_KEY to get all 19 tools; from version 1.1.16 it also starts without a key, with four tools including onboarding. ChatGPT connectors and the OpenAI Responses API's remote MCP tool need a remote HTTP server, so for those use the SDK's OpenAI integration instead (VorimToolRegistry from @vorim/sdk/integrations/openai, or vorim.integrations.openai_agents in Python).
Installation
npm install -g @vorim/mcp-serverbashConfiguration
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"vorim": {
"command": "npx",
"args": ["-y", "@vorim/mcp-server"],
"env": {
"VORIM_API_KEY": "agid_sk_live_..."
}
}
}
}typescriptFor Cursor, add to .cursor/mcp.json in your project root with the same format.
Berd & Goose (Block)
Berd is Block's desktop app for AI agents, running on the Goose engine — an MCP client. Add Vorim as a Standard IO extension and every agent you run in Berd (or Goose) gets identity, permission checks before actions, and a signed audit trail.
Berd / Goose Desktop: open the sidebar → Extensions → Add custom extension. Set Type to Standard IO, Command to npx -y @vorim/mcp-server, and add an environment variable VORIM_API_KEY = your key.
Goose CLI: run goose configure → Add Extension → Command-Line Extension, then enter the command npx -y @vorim/mcp-server.
Or add it directly to Goose's ~/.config/goose/config.yaml:
extensions:
vorim:
enabled: true
type: stdio
cmd: npx
args:
- "-y"
- "@vorim/mcp-server"
env_keys:
- VORIM_API_KEY
timeout: 300yamlStore the key with goose configure so it lands in Goose's secret store; env_keys tells Goose which secret to pass to the server at launch.
Cosine
Cosine ships autonomous coding agents (Lumen) that plan a change, write it, and open a pull request without a human in the loop. Its CLI is an MCP client, so adding Vorim gives every agent Cosine runs a cryptographic identity, a permission check before it acts, and a signed audit trail. When an agent acts unsupervised, that signed record of what it touched and under whose authority is what you hand an auditor afterward.
Fastest: add Vorim from the CLI:
cos mcp add --transport stdio -e VORIM_API_KEY=agid_sk_... vorim -- npx -y @vorim/mcp-serverbashOr edit ~/.cosine/mcp.json directly:
{
"mcpServers": {
"vorim": {
"transport": "stdio",
"command": "npx",
"args": ["-y", "@vorim/mcp-server"],
"env": {
"VORIM_API_KEY": "agid_sk_..."
}
}
}
}jsonPress Ctrl+J in the Cosine TUI and pick MCP servers to check that Vorim connected. On Windows the config lives at %USERPROFILE%\\.cosine\\mcp.json.
Available Tools (19)
| Tool | Description |
|---|---|
vorim_ping | Check API health and connectivity |
vorim_register_agent | Register a new agent with a cryptographic identity |
vorim_get_agent | Get agent details by ID |
vorim_list_agents | List all agents with pagination and filtering |
vorim_update_agent | Update agent metadata |
vorim_revoke_agent | Permanently revoke an agent |
vorim_check_permission | Check if agent has a permission scope (<5ms) |
vorim_grant_permission | Grant a permission scope with optional expiry/rate limits |
vorim_list_permissions | List all active permissions for an agent |
vorim_revoke_permission | Revoke a specific permission scope |
vorim_emit_event | Log an audit event for an agent action |
vorim_export_audit | Export signed audit bundle with SHA-256 manifest |
vorim_verify_trust | Verify agent trust score (public, no auth) |
vorim_register_ephemeral | Register an ephemeral did:key agent that auto-expires; returns agent_id, did:key, and keypair |
vorim_delegate_credential | Delegate a scoped credential to an agent, with optional rate limits and expiry |
vorim_request_token | Request a short-lived, scoped access token for an agent and provider |
vorim_list_delegations | List credential delegations, optionally filtered by agent |
vorim_onboard_start | Start device-authorization onboarding for a user with no API key; returns a user code and activation URL |
vorim_onboard_check | Check whether the user approved onboarding and retrieve the issued API key |
Example Usage
Once configured, you can use natural language in Claude, Cursor, or any MCP client:
// In Claude Desktop or Cursor, just ask:
"Register an agent called invoice-processor with read and execute permissions"
"Check if agent agid_acme_a1b2 has permission to execute"
"Log a tool_call event for agent agid_acme_a1b2: action=process_invoice, result=success"
"What's the trust score for agent agid_acme_a1b2?"
"Export the audit trail for the last 7 days"typescriptAgent Discovery
Vorim AI publishes a machine-readable Agent Card for automated discovery:
curl https://vorim.ai/.well-known/agent.jsonbashThis follows the A2A (Agent-to-Agent) discovery pattern, allowing other agents to programmatically discover Vorim's capabilities, endpoints, and authentication.
A2A Protocol Integration
The @vorim/a2a package adds identity and trust verification to Google's A2A (Agent-to-Agent) Protocol. Extend A2A Agent Cards with cryptographic identity and verify incoming agents before interacting.
Installation
# TypeScript
npm install @vorim/a2a
# Python (included in vorim >= 3.1.0)
pip install vorimbashExtend Your Agent Card
Add a vorimIdentity extension to your A2A Agent Card containing Ed25519 fingerprint, trust score, permission scopes, and a public verification URL.
import { createVorimA2A } from '@vorim/a2a';
const a2a = createVorimA2A({ apiKey: 'agid_sk_...' });
// Extend an existing Agent Card with Vorim identity
const card = await a2a.extendAgentCard(baseCard, 'agid_abc123');
// card.vorimIdentity = {
// agentId: "agid_abc123",
// publicKeyFingerprint: "a3f2...e91c",
// trustScore: 82,
// status: "active",
// scopes: ["agent:read", "agent:execute"],
// verifyUrl: "https://vorim.ai/v1/trust/verify/agid_abc123",
// badgeUrl: "https://vorim.ai/v1/trust/badge/agid_abc123.svg",
// verifiedAt: "2026-04-15T12:00:00Z"
// }typescriptVerify Incoming Agents
Before interacting with another agent, verify their trust score via Vorim's public API. The live score is checked independently, not from the self-reported value on the card.
// Verify an incoming agent's identity and trust
const result = await a2a.verifyAgent(incomingAgentCard);
if (result.trusted) {
console.log(`Verified: score ${result.score}, status ${result.status}`);
} else {
console.log(`Rejected: ${result.reason}`);
// "Trust score 32 is below minimum 50"
// "Agent is suspended"
// "Peer is missing required scope(s): agent:write"
}
// Verify with custom requirements
const strict = await a2a.verifyAgent(card, {
minTrustScore: 70,
requiredScopes: ['agent:read', 'agent:execute'],
});typescriptMiddleware
Wrap your A2A request handlers with automatic verification. Agents below your trust threshold are rejected before your code runs. By default the middleware fails closed on identity: it proves the caller controls the agent id (holder-of-key) via an extractProof / resolvePublicKey hook. Pass allowUnverifiedIdentity: true only if a trusted upstream already authenticated the caller.
// Auto-verify incoming A2A requests
const handler = a2a.middleware({
minTrustScore: 70,
requiredScopes: ['agent:read'],
auditLog: true, // log interactions as audit events
})(async (req) => {
// Only reached if sender agent passes all checks
const { vorimVerification } = req;
console.log(`Trusted agent: score ${vorimVerification.score}`);
return { status: 'ok' };
});typescriptCreate Agent Card from Scratch
Register a new Vorim agent and generate a complete A2A-compatible Agent Card in one call.
const card = await a2a.createAgentCard({
name: 'research-agent',
url: 'https://my-agent.example.com',
provider: { organization: 'Acme Corp' },
skills: [{ id: 'research', name: 'Web Research', tags: ['search'] }],
scopes: ['agent:read', 'agent:execute'],
});
// Returns a full A2A Agent Card with vorimIdentity extensiontypescriptPython
from vorim.a2a import VorimA2A
a2a = VorimA2A(api_key="agid_sk_...")
# Extend an Agent Card
card = a2a.extend_agent_card(base_card, agent_id="agid_abc123")
# Verify an incoming agent
result = a2a.verify_agent(incoming_card, min_trust_score=60)
if result.trusted:
print(f"Verified with score {result.score}")
# Create a new agent with A2A card
card = a2a.create_agent_card(
name="research-agent",
url="https://my-agent.example.com",
scopes=["agent:read", "agent:execute"],
)
# Decorator middleware
@a2a.middleware(min_trust_score=70, required_scopes=["agent:read"])
def handle_task(request):
return {"status": "ok"}typescriptDiscovery Endpoint
Serve your Vorim-extended Agent Card at /.well-known/agent.json for A2A discovery:
// Express example
app.get('/.well-known/agent.json', async (req, res) => {
const card = await a2a.discoveryEndpoint('agid_abc123', {
name: 'my-agent',
url: 'https://my-agent.example.com',
skills: [{ id: 'research', name: 'Web Research' }],
});
res.json(card);
});typescriptPydantic AI Integration
Type-safe agent identity for Pydantic AI agents. Uses dependency injection to provide identity, permission checking, and audit logging through RunContext.
Installation
pip install vorim pydantic-aibashVorimDeps Dependency
Pass VorimDeps as your agent's dependency type. It provides check(), emit(), grant(), and verify() methods on the context.
from vorim.pydantic_ai import VorimDeps
from pydantic_ai import Agent, RunContext
agent = Agent('openai:gpt-4o', deps_type=VorimDeps)
@agent.tool
async def fetch_data(ctx: RunContext[VorimDeps], query: str) -> str:
# Check permission before acting
check = ctx.deps.check('agent:read')
if not check.get('allowed'):
return 'Permission denied: agent:read not granted'
result = do_something(query)
# Log the action
ctx.deps.emit('data.fetch', outcome='success')
return result
# Run with Vorim identity
deps = VorimDeps(api_key='agid_sk_...', agent_id='agid_abc123')
result = await agent.run('Fetch the latest data', deps=deps)typescriptvorim_tool Decorator
Automatically checks permissions and logs audit events on every tool call. No manual check()/emit() needed.
from vorim.pydantic_ai import VorimDeps, vorim_tool
agent = Agent('openai:gpt-4o', deps_type=VorimDeps)
@agent.tool
@vorim_tool(scope='agent:read', action='data.fetch')
async def fetch_data(ctx: RunContext[VorimDeps], query: str) -> str:
# Permission checked automatically before this runs
# Audit event emitted automatically after this returns
return do_something(query)
# If permission is denied, tool returns 'Permission denied'
# and a denial audit event is loggedtypescriptQuick Start with create_vorim_agent
Register a new agent and create a Pydantic AI Agent in one call:
from vorim.pydantic_ai import create_vorim_agent
agent, deps = create_vorim_agent(
model='openai:gpt-4o',
api_key='agid_sk_...',
agent_name='research-agent',
scopes=['agent:read', 'agent:execute'],
system_prompt='You are a research agent.',
)
result = await agent.run('Find the latest papers', deps=deps)typescriptStripe ACP Integration
Agent identity verification for Stripe's Agentic Commerce Protocol. Ensures only authorized agents with the transact permission can initiate checkouts.
Authorize Before Checkout
Three checks run before any checkout: transact permission, trust score threshold, and active status.
# Python
from vorim.stripe_acp import VorimACP
acp = VorimACP(api_key='agid_sk_...', min_trust_score=70)
result = acp.authorize_checkout(
agent_id='agid_abc123',
seller='acme-store',
amount=4999,
currency='usd',
)
if result['authorized']:
# Proceed with Stripe ACP checkout
print(f'Trust score: {result["trust_score"]}')
else:
print(f'Blocked: {result["reason"]}')typescriptExpress Middleware (TypeScript)
import createVorim from '@vorim/sdk';
import { createVorimACP } from '@vorim/sdk/integrations/stripe-acp';
const vorim = createVorim({ apiKey: 'agid_sk_...' });
const acp = createVorimACP(vorim, { minTrustScore: 70 });
// Agent must send X-Vorim-Agent-Id header
app.post('/checkouts', acp.middleware(), (req, res) => {
// Only reached if agent has transact permission + trust score >= 70
const { trustScore, status } = req.vorimAuthorization;
// Create Stripe checkout session...
});typescriptFlask/FastAPI Decorator (Python)
@acp.require_transact(agent_id_header='X-Vorim-Agent-Id')
def create_checkout(request):
# Only reached if agent passes all checks
return process_checkout(request)typescriptAudit Trail
Every commerce interaction is logged automatically:
- checkout.authorize — permission + trust verification (success or denied with reason)
- checkout.created — checkout session initiated
- checkout.completed — payment processed
- checkout.canceled — checkout abandoned
OpenClaw Integration
Give your OpenClaw agent a cryptographic identity, enforce permissions before sensitive actions, and log a tamper-proof audit trail. Works as a skill via mcporter + MCP server.
Setup
Step 1: Create an account at vorim.ai and get your API key from Settings → API Keys.
Step 2: Add the Vorim MCP server to your OpenClaw instance:
# Add Vorim as an MCP server via mcporter
mcporter config add vorim --stdio "npx -y @vorim/mcp-server"
# Set your API key
export VORIM_API_KEY=agid_sk_live_...bashOr install the Vorim skill for automatic identity and audit behavior:
# Copy the skill to your OpenClaw skills directory
mkdir -p ~/.openclaw/skills/vorim
# Add SKILL.md from github.com/Vorim-AI-Labs/vorim-mcp-serverbashUsage
Once configured, your OpenClaw agent can use all 19 Vorim tools via mcporter:
# Register your agent (first run)
mcporter call vorim.vorim_register_agent name="my-openclaw" \
capabilities:='["browse","email","shell"]' \
scopes:='["agent:read","agent:write","agent:execute"]'
# Check permission before a sensitive action
mcporter call vorim.vorim_check_permission \
agent_id="agid_..." scope="agent:execute"
# Log an action to the audit trail
mcporter call vorim.vorim_emit_event \
agent_id="agid_..." event_type="tool_call" \
action="send_email" result="success"
# Verify trust score
mcporter call vorim.vorim_verify_trust agent_id="agid_..."bashWhat the Skill Does
- Permission checks — before shell commands, emails, payments, or any destructive action
- Audit logging — every action is recorded to an append-only audit trail, and can be cryptographically signed (Ed25519) for tamper-evidence
- Trust verification — external services can verify your agent before interacting
- Denied actions are blocked — if permission is denied, the agent stops and informs the user
Kiro
Kiro, the agentic IDE from AWS, loads MCP servers, steering files and agent hooks from your workspace's .kiro folder. Vorim uses all three. The MCP server lets the agent check its own permissions, the steering file tells it what to do when Vorim says no, and a PreToolUse hook checks every file write, shell command and MCP call against the agent's Vorim scopes before it runs. A call the agent isn't granted never runs.
Install
From your project root, run the installer. It writes four files into .kiro and merges Vorim into an existing mcp.json rather than replacing it.
curl -fsSL https://vorim.ai/hooks/kiro/install.sh | bashbashThen set two variables in the environment Kiro starts from, add VORIM_API_KEY to the Mcp Approved Env Vars setting so the MCP config can read it, and restart Kiro.
# key scopes: permissions:read, audit:write
export VORIM_API_KEY=agid_sk_live_...
export VORIM_AGENT_ID=agid_...bashWhat gets installed
.kiro/settings/mcp.jsonadds the vorim MCP server..kiro/hooks/vorim.jsona PreToolUse hook on write, shell and MCP tools that runs the Vorim check and blocks the call with exit code 2 when the agent lacks the scope, and a PostToolUse hook that records each completed call in the agent's audit trail..kiro/steering/vorim.mdtells the agent not to retry or work around a Vorim denial, and to check permission itself before actions the hooks can't see..kiro/vorim/vorim-hook.mjsthe hook itself, a single Node script with no dependencies.
{
"version": "v1",
"hooks": [
{
"name": "vorim-permission-gate",
"trigger": "PreToolUse",
"matcher": "write|shell|@mcp",
"action": { "type": "command", "command": "node .kiro/vorim/vorim-hook.mjs pre" },
"enabled": true
},
{
"name": "vorim-audit",
"trigger": "PostToolUse",
"matcher": "write|shell|@mcp",
"action": { "type": "command", "command": "node .kiro/vorim/vorim-hook.mjs post" },
"enabled": true
}
]
}jsonHow tools map to scopes
The hook picks the scope from the tool name. Reads and searches need agent:read, file writes need agent:write, shell commands need agent:execute, sending a message needs agent:communicate, and payments or refunds need agent:transact. Anything it can't classify needs agent:execute, and VORIM_SCOPE_MAP (a JSON object of tool-name patterns to scopes) overrides the mapping.
The hook fails closed, so if Vorim can't be reached the gated call is blocked; set VORIM_FAIL_OPEN=1 to let it through instead. Tool inputs are never sent whole. The audit event carries the tool name, the scope and a short summary such as a file path or a command, never file contents. Kiro runs hooks in the IDE and the CLI.
NanoClaw
NanoClaw runs Claude agents in containers and grows through skills that Claude Code applies to your checkout. Vorim ships as one of them, /add-vorim. It registers the Vorim MCP server for the agent groups you pick and adds a PreToolUse hook that checks every file write, shell command and MCP call against the agent's Vorim scopes, sending a message included. The API key stays in the OneCLI vault and never enters the container.
Install
In your NanoClaw checkout, fetch the skill and run it from Claude Code.
mkdir -p .claude/skills/add-vorim
curl -fsSL https://vorim.ai/hooks/nanoclaw/add-vorim/SKILL.md -o .claude/skills/add-vorim/SKILL.md
curl -fsSL https://vorim.ai/hooks/nanoclaw/add-vorim/REMOVE.md -o .claude/skills/add-vorim/REMOVE.md
# then, in Claude Code inside your NanoClaw checkout
/add-vorimbashThe skill asks which groups to cover and which Vorim agent each one acts as, has you store the API key with onecli secrets create, restarts the groups, and walks you through checking that a call the agent isn't granted gets blocked.
What it changes
data/v2-sessions/<group>/.claude-shared/settings.jsongains PreToolUse and PostToolUse command hooks that run the Vorim hook for Write, Edit, Bash and every MCP tool.ncl groups config add-mcp-serverregisters a vorim MCP server, with the key supplied by the OneCLI gateway.groups/<group>/instructions.prepend.mdgets a short block telling the agent not to work around a Vorim denial.
{
"hooks": {
"PreToolUse": [{
"matcher": "Write|Edit|NotebookEdit|Bash|mcp__.*",
"hooks": [{ "type": "command",
"command": "NODE_USE_ENV_PROXY=1 node /home/node/.claude/vorim/vorim-hook.mjs pre --agent agid_..." }]
}]
}
}jsonREMOVE.md, installed alongside the skill, undoes each step. The hook needs Node 22.21 or later in the container, the first release where fetch follows the gateway's HTTPS_PROXY once NODE_USE_ENV_PROXY=1 is set, which the skill does for you.
Marketplace Trust Widget
Embeddable trust badges for agent marketplaces, directories, and any page that lists AI agents. One script tag — shows a live trust score, verification status, and links to the full verification page. No backend work required.
Badge (Compact)
Best for agent listings, search results, and cards. Shows trust score and verification status inline.
<!-- Drop this on any page -->
<div data-vorim-agent="agid_acme_a1b2c3d4"></div>
<script src="https://vorim.ai/widget/vorim-trust.js"></script>htmlCard (Detailed)
Best for agent detail pages. Shows trust score, org name, scopes, creation date, and key fingerprint.
<div
data-vorim-agent="agid_acme_a1b2c3d4"
data-vorim-style="card"
data-vorim-theme="dark"
></div>
<script src="https://vorim.ai/widget/vorim-trust.js"></script>htmlInline (Minimal)
Best for tables, lists, and compact UIs. Shows a colored dot + score.
<div
data-vorim-agent="agid_acme_a1b2c3d4"
data-vorim-style="inline"
></div>htmlOptions
data-vorim-agentstringRequireddata-vorim-stylestringdata-vorim-themestringdata-vorim-sizestringdata-vorim-linkstringStatic SVG Badge
For README files, GitHub repos, and static pages. Live SVG that updates automatically:
markdownVerification API
For deeper integration. Public endpoint — no authentication required. Returns the live trust score plus a coarse band, an evidence-confidence indicator, the agent's positive trust signals, and a platform-signed attestation a counterparty can verify offline:
curl https://vorim.ai/v1/trust/verify/agid_acme_a1b2c3d4
# Returns (payload wrapped in a top-level "data" envelope):
{
"data": {
"agent_id": "agid_acme_a1b2c3d4",
"verified": true,
"trust_score": 85,
"trust_band": "trusted", // untrusted | building | established | trusted
"confidence": "established", // new | developing | established (evidence maturity)
"signals": ["identity_anchor", "reliability", "maturity"],
"status": "active",
"owner": { "org_name": "Acme Corp", "verified": true },
"attestation": {
"attestation_id": "f544370d-...",
"agent_id": "agid_acme_a1b2c3d4",
"trust_band": "trusted",
"confidence": "established",
"issued_at": "2026-06-19T15:41:01Z",
"expires_at": "2026-06-19T15:46:01Z",
"kid": "93da0741...", // matches a key from GET /v1/trust/keys
"signature": "ed25519:aRK3sIs0..."
}
}
}bashVerify the attestation offline. Fetch Vorim's public signing keys once, match the kid, and verify the Ed25519 signature over the signed fields — no live call back to Vorim, and tampering (e.g. upgrading the band) is rejected. The signed payload deliberately excludes the raw weights and gate thresholds.
curl https://vorim.ai/v1/trust/keys
# Returns the platform public key(s) for verifying attestations:
{
"data": {
"keys": [
{ "kid": "93da0741...", "alg": "ed25519",
"use": "trust-attestation",
"public_key_pem": "-----BEGIN PUBLIC KEY-----\n..." }
]
}
}bashWant the full breakdown of why an agent has a given score? The authenticated, owner-only GET /v1/agents/:id/trust endpoint returns ranked, plain-language reason codes for agents in your own org (also shown in the dashboard agent detail). The public surface stays coarse by design — bands and positive signals, never magnitudes or distance-to-threshold.
Public Agent Directory
Each agent has a List in public directory setting. When enabled, the agent's name, organization, and live trust score appear on the public Agent Directory, so anyone can look it up and verify it before interacting. It is off by default: nothing is listed until you explicitly opt an agent in. Toggle it from the agent's page in the dashboard, or via the API by setting public_listing:
# Opt an agent into the public directory
curl -X PATCH https://vorim.ai/v1/agents/agid_acme_a1b2c3d4 \
-H "Authorization: Bearer agid_sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "public_listing": true }' # set false to remove it againbashRead the directory from the public, unauthenticated endpoint. It returns only agents that opted in and are active (revoked, expired, and soft-deleted-org agents are excluded), newest first:
curl https://vorim.ai/v1/trust/directory
# Returns opted-in agents with their live trust score:
{
"data": {
"agents": [
{
"agent_id": "agid_acme_a1b2c3d4",
"name": "invoice-processor",
"org_name": "Acme Corp",
"status": "active",
"trust_score": 85,
"created_at": "2026-06-01T09:12:00Z"
}
],
"total": 1
}
}bashUse Cases
- Agent marketplaces — show trust badges on every listing, filter by minimum trust score
- Agent directories — sort and filter agents by verification status
- Multi-agent platforms — check trust score before allowing agent-to-agent interaction
- Enterprise procurement — require minimum trust score for agent onboarding
- GitHub READMEs — show live trust badge in your agent's README
CLI Tool
The @vorim/cli scaffolds projects, registers agents, and manages identity directly from the terminal. Get to "first agent secured" in 60 seconds.
Installation
# Run directly (no install needed)
npx @vorim/cli init
# Or install globally
npm install -g @vorim/clibashInitialize a Project
Scaffolds a Vorim-enabled project: tests connection, registers your first agent, emits a test event, and creates vorim.json + .env.
$ npx @vorim/cli init
╔══════════════════════════════╗
║ VORIM AI CLI ║
║ Agent Identity & Trust Layer ║
╚══════════════════════════════╝
→ Setting up Vorim AI in this project
✓ Found API key: agid_sk_live_abc...
→ Testing connection...
✓ Connected to Vorim AI (healthy)
API Key: agid_sk_live_abc...
Project name (my-project): my-project
First agent name (my-project-agent): my-project-agent
→ Registering agent "my-project-agent"...
✓ Agent registered: agid_org_a1b2c3d4
✓ Granted agent:read permission
✓ First audit event emitted
✓ Created vorim.json
✓ Added Vorim keys to .envbashCommands
| Command | Description |
|---|---|
vorim init | Scaffold a new project, register first agent, create config files |
vorim register [name] | Register a new agent with a cryptographic identity |
vorim verify <agent-id> | Check an agent's trust score and status |
vorim status | Check API health, connection, and project config |
vorim agents | List all agents with status and trust scores |
vorim grant <id> <scope> | Grant a permission scope to an agent |
vorim check <id> <scope> | Check if an agent has a specific permission |
vorim emit <id> <action> | Emit an audit event for an agent action |
Configuration
The CLI looks for your API key in this order:
# 1. Environment variable
export VORIM_API_KEY=agid_sk_live_...
# 2. .env file in current directory
VORIM_API_KEY=agid_sk_live_...
# 3. vorim.json in current directory (created by vorim init)
{ "apiKey": "agid_sk_live_..." }typescriptTrust Widget (Embeddable Badge)
Embed a live trust badge on your website, documentation, or agent marketplace listing. The badge shows the agent's current trust score and updates in real time.
SVG Badge
Embed directly in HTML. No JavaScript required. Cached for 60 seconds.
<!-- HTML embed -->
<img
src="https://vorim.ai/v1/trust/badge/YOUR_AGENT_ID.svg"
alt="Vorim Trust Score"
width="180"
height="32"
/>
<!-- Markdown -->
typescriptClickable Verification
Wrap the badge in a link so visitors can click to verify the agent's full trust profile:
<a href="https://vorim.ai/v1/trust/verify/YOUR_AGENT_ID" target="_blank">
<img
src="https://vorim.ai/v1/trust/badge/YOUR_AGENT_ID.svg"
alt="Vorim Trust Score"
/>
</a>typescriptTrust Verification API
Query the full trust profile programmatically. Public, no authentication required.
GET https://vorim.ai/v1/trust/verify/YOUR_AGENT_ID
Response (responses are wrapped in a "data" envelope):
{
"data": {
"agent_id": "agid_abc123",
"verified": true,
"trust_score": 82,
"trust_band": "trusted", // untrusted | building | established | trusted
"confidence": "established", // new | developing | established (evidence maturity)
"signals": ["identity_anchor", "reliability", "maturity"],
"status": "active",
"owner": { "org_name": "Acme Corp", "verified": true },
"attestation": {
"trust_band": "trusted",
"issued_at": "2026-06-19T15:41:01Z",
"expires_at": "2026-06-19T15:46:01Z",
"kid": "93da0741...", // matches a key from GET /v1/trust/keys
"signature": "ed25519:aRK3sIs0..."
}
}
}typescriptUse Cases
Common places to embed the trust badge:
- Agent marketplaces — show trust score next to agent listings
- Partner portals — verify agent identity before granting access
- Documentation — display trust badge in your API docs
- GitHub README — add badge to your agent's repository
- Compliance reports — include verification links in audit submissions
Agent Directory
The Public Agent Directory lists verified AI agents with their trust scores, making it easy to discover and verify agents before interacting with them.
How to Get Listed
Listing is opt-in and off by default. Nothing is listed until you explicitly enable public listing for a specific agent, so your agents stay private unless you choose otherwise. Once you opt an agent in, it will show:
- Agent name and ID
- Organization name
- Live trust score (0-100) with color-coded rating
- "Verified" badge for agents with trust score 70+
- Direct link to the public Trust API for independent verification
Directory API
Query the directory programmatically:
GET https://vorim.ai/v1/trust/directory
Response:
{
"data": {
"agents": [
{
"agent_id": "agid_abc123",
"name": "research-agent",
"org_name": "Acme Corp",
"status": "active",
"trust_score": 82,
"created_at": "2026-04-15T12:00:00Z"
}
],
"total": 1
}
}typescriptSearch and Filter
The directory page at vorim.ai/directory supports search by agent name, ID, or organization. Results are sorted by creation date with trust scores displayed as color-coded circles.
Python SDK
The official Python SDK provides sync and async clients with the same capabilities as the TypeScript SDK.
# Core SDK (requires Python 3.10+)
pip install vorim
# With framework integrations (quote the extras so zsh doesn't expand the brackets)
pip install "vorim[langchain]" # LangChain / LangGraph
pip install "vorim[crewai]" # CrewAI
pip install "vorim[openai]" # OpenAI Agents SDK
pip install "vorim[anthropic]" # Anthropic / Claude
pip install "vorim[all]" # All integrationsbashQuick Start
from vorim import Vorim
vorim = Vorim(api_key="agid_sk_live_...")
# Register an agent
result = vorim.register(
name="invoice-processor",
capabilities=["read_documents", "extract_data"],
scopes=["agent:read", "agent:execute"],
)
# Check permissions (<5ms via Redis)
check = vorim.check(result.agent.agent_id, "agent:execute")
if check.allowed:
# Emit audit event
vorim.emit(
agent_id=result.agent.agent_id,
event_type="tool_call",
action="process_invoice",
result="success",
latency_ms=142,
)
# Verify any agent's trust (public, no auth required)
trust = vorim.verify(result.agent.agent_id)
print(f"Trust score: {trust.trust_score}/100")pythonAsync Client
import asyncio
from vorim import AsyncVorim
async def main():
async with AsyncVorim(api_key="agid_sk_live_...") as vorim:
result = await vorim.register(
name="async-agent",
capabilities=["search"],
scopes=["agent:read"],
)
print(result.agent.agent_id)
asyncio.run(main())pythonLangChain Integration
from vorim import Vorim
from vorim.integrations.langchain import vorim_tool, VorimCallbackHandler
vorim = Vorim(api_key="agid_sk_live_...")
@vorim_tool(vorim, agent_id="agid_acme_...", permission="agent:execute")
def search(query: str) -> str:
"""Search documents."""
return f"Results for {query}"
# search() is now a standard LangChain tool with Vorim permission checks + auditpythonCrewAI Integration
from vorim import Vorim
from vorim.integrations.crewai import register_crew
vorim = Vorim(api_key="agid_sk_live_...")
crew = register_crew(vorim, {
"crew_name": "content-pipeline",
"members": [
{
"role": "researcher",
"name": "crew-researcher",
"capabilities": ["web_search"],
"scopes": ["agent:read", "agent:execute"],
},
],
})pythonOpenAI Integration
from openai import OpenAI
from vorim import Vorim
from vorim.integrations.openai_agents import VorimToolRegistry
vorim = Vorim(api_key="agid_sk_live_...")
client = OpenAI()
registry = VorimToolRegistry(vorim=vorim, agent_id="agid_acme_...")
registry.add(
name="search",
description="Search documents",
parameters={"type": "object", "properties": {"query": {"type": "string"}}},
execute=lambda args: f"Results for {args['query']}",
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Search for AI papers"}],
tools=registry.to_openai_tools(),
)
tool_messages = registry.execute_tool_calls(
response.choices[0].message.tool_calls or []
)pythonAuthentication
All SDK requests are authenticated with an API key. Create one from the Settings page in the Vorim AI dashboard.
import createVorim from '@vorim/sdk';
const vorim = createVorim({
apiKey: 'agid_sk_live_xxxxxxxxxxxxxxxx',
baseUrl: 'https://api.vorim.ai', // default
timeout: 10000, // default: 10 seconds
});typescriptConfiguration options:
apiKeystringRequiredagid_sk_.baseUrlstringhttps://api.vorim.ai.timeoutnumberNever expose your API key in client-side code. The SDK is designed for server-to-server use. Store your key in environment variables.
Agent Identity
Every agent gets a unique cryptographic identity upon registration. The private key is returned once and never stored by Vorim AI.
vorim.register(input)
Register a new agent with a cryptographic keypair.
namestringRequireddescriptionstringcapabilitiesstring[]Required['api_access', 'file_read']).scopesPermissionScope[]Requiredconst agent = await vorim.register({
name: 'InvoiceBot',
description: 'Automated invoice processing agent',
capabilities: ['api_access', 'email_send', 'file_read'],
scopes: ['agent:read', 'agent:write', 'agent:communicate'],
});
// Response
{
agent: {
agent_id: 'agid_acme_a1b2c3d4',
name: 'InvoiceBot',
status: 'active',
trust_score: 50,
// ... more fields
},
private_key: '-----BEGIN PRIVATE KEY-----\n...', // Save this!
public_key: '-----BEGIN PUBLIC KEY-----\n...',
key_fingerprint: 'a1b2c3d4e5f6...',
}typescriptvorim.getAgent(agentId)
Retrieve details for a specific agent.
const agent = await vorim.getAgent('agid_acme_a1b2c3d4');
console.log(agent.name, agent.status, agent.trust_score);typescriptvorim.listAgents(params?)
List all agents in your organization with optional filtering.
const { agents, meta } = await vorim.listAgents({
status: 'active',
page: 1,
per_page: 25,
});
console.log(`${meta.total} agents total, showing page ${meta.page}`);typescriptvorim.revoke(agentId)
Permanently revoke an agent. This cannot be undone.
await vorim.revoke('agid_acme_a1b2c3d4');
// Agent status is now 'revoked', all permissions deniedtypescriptPermissions
Vorim has seven permission scopes. They are independent of one another, not a ladder.
Holding agent:write does not imply agent:read. Every scope an agent needs is granted explicitly, which is why each demo agent lists the full set it uses. Read the seven as a checklist rather than a hierarchy.
agent:readagent:writeagent:executeagent:transactagent:communicateagent:delegateagent:elevatevorim.check(agentId, scope)
Check if an agent has a specific permission. Use this before every action.
Check before the action, not after it. A check that runs once the side effect has already happened records what occurred but cannot prevent it, and preventing it is the whole point of holding a scope.
Decisions are cached in Redis, and an allow read from that cache is re-confirmed against the database before it is honored. A grant that has since been revoked, expired, or belongs to a deactivated agent is therefore denied on the very next check rather than lingering until the cache lapses.
const result = await vorim.check('agid_acme_a1b2c3d4', 'agent:write');
if (result.allowed) {
// Agent is authorized — proceed with the action
await performWrite();
} else {
console.log('Permission denied:', result.reason);
}
// Response shape
{
allowed: true,
agent_id: 'agid_acme_a1b2c3d4',
scope: 'agent:write',
reason: undefined, // populated with the denial reason when allowed is false
}typescriptvorim.grant(agentId, scope, options?)
Grant a permission scope to an agent with optional constraints.
// Grant with expiration
await vorim.grant('agid_acme_a1b2c3d4', 'agent:transact', {
valid_until: '2027-06-01T00:00:00Z',
});
// Grant with rate limiting
await vorim.grant('agid_acme_a1b2c3d4', 'agent:communicate', {
rate_limit: { max: 100, window: '1h' },
});typescriptAudit Events
Every agent action should be logged for compliance and debugging. Events are stored in TimescaleDB with ULID ordering.
Retries are safe when you send an Idempotency-Key header. The first request records its response, and a repeat returns that same response carrying the original event ids instead of writing the batch twice. Send no key and you get exactly today's behavior.
What you send is what we store. If prompts cannot leave your network, send a digest or a keyed commitment in place of the payload; payload privacy covers how that works and what a third party can still check.
vorim.emit(event)
Emit a single audit event.
agent_idstringRequiredevent_typestringRequiredtool_call, api_request, message_sent, permission_change, status_change, key_rotation, login, or export.actionstringRequiredPOST /invoices).resourcestringresultstringRequiredsuccess, denied, or error.latency_msnumbermetadataobjectawait vorim.emit({
agent_id: 'agid_acme_a1b2c3d4',
event_type: 'api_request',
action: 'POST /api/invoices/create',
resource: 'invoices',
result: 'success',
latency_ms: 127,
metadata: {
invoice_id: 'inv_9876',
amount: 1500.00,
currency: 'USD',
},
});typescriptvorim.emitBatch(events)
Emit up to 1,000 audit events in a single request. Ideal for high-throughput scenarios.
const events = actions.map(action => ({
agent_id: 'agid_acme_a1b2c3d4',
event_type: 'tool_call',
action: action.name,
result: action.success ? 'success' : 'error',
latency_ms: action.duration,
}));
const { ingested } = await vorim.emitBatch(events);
console.log(`${ingested} events recorded`);typescriptTrust Verification
Verify an agent's identity and trust score via the public Trust API. No authentication required for verification.
The endpoint is public. A counterparty can check an agent before dealing with it without an account, an API key, or any involvement from you, which is rather the point of publishing it. A score only you can read proves nothing to anybody else.
It scores the agent's own record rather than your company. Every agent starts at 50 and earns the rest, so a low score on a new agent means untested rather than untrustworthy.
vorim.verify(agentId)
const trust = await vorim.verify('agid_acme_a1b2c3d4');
console.log(trust.trust_score); // 87
console.log(trust.status); // 'active'
console.log(trust.owner.verified); // true — is the owning org verified
console.log(trust.active_scopes); // ['agent:read', 'agent:execute']typescriptPython
trust = vorim.verify("agid_acme_a1b2c3d4")
print(trust.trust_score) # 87
print(trust.status) # 'active'
print(trust.owner["verified"]) # True — is the owning org verified
# Async (await needs an async function; run it with asyncio.run(main()))
async def main():
async with AsyncVorim(api_key="agid_sk_live_...") as client:
trust = await client.verify("agid_acme_a1b2c3d4")pythonTrust score factors (0-100):
Payload Signing
Sign payloads with the agent's private key for tamper-proof verification.
Signing is on by default, and that default only does anything when the SDK holds the agent's private key. register() puts the key in an in-process keyring, and useAgentKey() restores it after a restart. Emit an event for an agent whose key is missing and it goes out unsigned, with nothing raised to tell you.
Design against that one. A pipeline can look healthy while writing unsigned rows for months, and the only check that exercises the whole path is exporting a bundle and running @vorim/verify over it.
What gets signed is a canonical serialization of the event rather than the JSON you happened to send, so two services that order their fields differently still produce identical bytes. v1 follows RFC 8785 and is the default. v0 stays readable for anything signed before that change, since re-signing an old event would defeat the point of having signed it.
Vorim never holds the private key. It is returned once, when the agent is registered, and never again, so a lost key means rotating the agent rather than recovering it.
vorim.sign(payload, privateKeyPem)
// Sign a payload with the agent's private key
const payload = JSON.stringify({ action: 'transfer', amount: 500 });
const signature = await vorim.sign(payload, agent.private_key);
console.log(signature);
// 'ed25519:base64encodedSignature...'
// Include signature in audit events for verification
await vorim.emit({
agent_id: agent.agent_id,
event_type: 'api_request',
action: 'POST /transfers',
result: 'success',
signature, // Stored with the audit event
});typescriptError Handling
The SDK throws VorimError for all API errors with structured details.
import { VorimError } from '@vorim/sdk';
try {
await vorim.check('invalid_agent_id', 'agent:read');
} catch (err) {
if (err instanceof VorimError) {
console.log(err.status); // 404
console.log(err.code); // 'AGENT_NOT_FOUND'
console.log(err.message); // 'Agent not found'
console.log(err.details); // { agent_id: 'invalid_agent_id' }
}
}typescriptCommon error codes:
| Status | Code | Description |
|---|---|---|
400 | VALIDATION_ERROR | Invalid request body or parameters |
401 | UNAUTHORIZED | Missing or invalid API key |
403 | FORBIDDEN | Insufficient permissions |
404 | AGENT_NOT_FOUND | Resource not found (entity-specific: AGENT_NOT_FOUND, PERMISSION_NOT_FOUND, CONNECTION_NOT_FOUND, …) |
409 | CONFLICT | Resource already exists |
429 | RATE_LIMITED | Too many requests |
500 | INTERNAL_ERROR | Server error |
Payload Privacy
Your prompts and model outputs never have to reach Vorim. The audit event carries a digest of them, computed in your process. This section is about making that a guarantee rather than a habit, and about the difference between a digest and a commitment.
Commit, don’t just hash
A bare SHA-256 of a prompt is not a commitment, it is a lookup key. Prompts are low-entropy and usually templated, so anyone holding the digest and the template recovers the content by trying candidates until a hash matches. That includes us, and anyone who obtains an export.
Use a keyed commitment for anything a person or a model wrote. The key stays in your process and is never sent. Keep hashPayload only for content that is already high-entropy, such as a file digest or a random identifier.
import { commitPayload, hashPayload } from '@vorim/sdk';
// The key lives in your secret store and is never sent to Vorim.
const key = process.env.VORIM_COMMITMENT_KEY!;
await vorim.emit({
agent_id: agent.agent_id,
event_type: 'tool_call',
action: 'invoice.refund',
result: 'success',
input_hash: await commitPayload(prompt, key), // hmac-sha256:...
output_hash: await commitPayload(completion, key),
});typescriptDisclose later, to whoever needs it
To prove that a specific prompt produced a specific audit record, hand the verifier the content and the key. They recompute and compare. Until then the record shows that an action happened and nothing about what was in it.
import { commitPayload } from '@vorim/sdk';
// The auditor has the event, the prompt, and the key. They recompute.
const recomputed = await commitPayload(disclosedPrompt, disclosedKey);
console.log(recomputed === event.input_hash); // truetypescriptRefuse to send content at all
The hash fields were always the right place for content, but nothing enforced it, and metadata is a free-form object that a whole prompt fits into by accident. Turn the guard on and the SDK throws before anything leaves your process.
It rejects non-scalar metadata, metadata strings over the limit (256 characters by default), and any hash field that is not a well-formed digest, which is what catches a raw prompt passed to input_hash.
const vorim = createVorim({
apiKey: process.env.VORIM_API_KEY!,
noPayload: true, // or { maxMetadataChars: 1024 }
});
await vorim.emit({
agent_id: agent.agent_id,
event_type: 'tool_call',
action: 'invoice.refund',
result: 'success',
metadata: { tool: 'stripe.refunds.create', attempt: 2 }, // ok
});
await vorim.emit({
agent_id: agent.agent_id,
event_type: 'tool_call',
action: 'invoice.refund',
result: 'success',
metadata: { prompt: fullPrompt }, // throws
});
// VorimError PAYLOAD_BLOCKED — nothing was senttypescriptReferencing an event
Every emit returns a stable content digest alongside the event id. The digest is computed over exactly the bytes the signature covers, so you can recompute it yourself and get the same value, and hand it to a downstream evidence layer as a reference that does not depend on trusting either of us to compute it.
const { events } = await vorim.emit({ /* ... */ });
// events: [{ event_id: 'evt_...', digest: 'sha256:...' }]
// Recompute it yourself, offline, from the event you sent.
import { eventDigest } from '@vorim/sdk';
console.log(await eventDigest(sentEvent) === events[0].digest); // truetypescriptThe same value is what prev_event_hash references on the next event, so a hash-chained emitter is already producing these. It is a content digest rather than an identity: two byte-identical events share one, which is why the response carries the event id beside it.
Credential Delegation
Credential delegation lets agents safely access third-party OAuth services (Google, GitHub, Slack, etc.) without ever seeing refresh tokens. The platform acts as a proxy — agents receive short-lived access tokens through scoped, time-limited delegations.
How It Works
1. Register an OAuth provider with your client credentials (encrypted at rest with AES-256-GCM)
2. Store a connection — user authorizes OAuth access, refresh token is encrypted in the vault
3. Delegate to an agent — bind an agent to a connection with attenuated scopes
4. Agent requests a token — platform exchanges the refresh token and returns a short-lived access token
5. Everything is audited — every delegation, revocation, and token use is logged
Register an OAuth Provider
// Register a Google OAuth provider
await vorim.registerProvider({
provider_key: 'google',
display_name: 'Google Workspace',
client_id: 'your-google-client-id',
client_secret: 'your-google-client-secret',
auth_url: 'https://accounts.google.com/o/oauth2/v2/auth',
token_url: 'https://oauth2.googleapis.com/token',
scopes_available: ['drive.readonly', 'gmail.send', 'calendar.events'],
});typescriptStore an OAuth Connection
// After user completes OAuth consent flow
await vorim.storeConnection({
provider_id: 'provider-uuid',
refresh_token: 'ya29.a0AfH6SM...', // encrypted at rest
scopes_granted: ['drive.readonly', 'gmail.send'],
external_account_id: 'user@gmail.com',
});typescriptDelegate to an Agent
// Give an agent access to a subset of the connection's scopes
await vorim.delegateCredential({
connection_id: 'connection-uuid',
agent_id: 'agid_acme_a1b2c3d4',
scopes_delegated: ['drive.readonly'], // must be ⊆ connection scopes
max_requests_per_hr: 100,
valid_until: '2027-04-30T00:00:00Z',
});typescriptAgent Requests a Token
// Agent requests a short-lived access token
const token = await vorim.requestToken({
agent_id: 'agid_acme_a1b2c3d4',
scope: 'drive.readonly',
});
// Use the token (expires in ~1 hour)
const response = await fetch('https://www.googleapis.com/drive/v3/files', {
headers: { Authorization: `${token.token_type} ${token.access_token}` },
});typescriptRevoke a Delegation
Revoking a delegation cascades to all downstream delegation chains. Revocation is immediate.
// Revoke agent's access (cascades to all chains)
await vorim.revokeDelegation('delegation-uuid');
// Or revoke the entire connection (revokes ALL delegations)
// DELETE /v1/credentials/connections/:idtypescriptPython SDK
from vorim import Vorim
client = Vorim(api_key="agid_sk_live_...")
# Register provider
client.register_provider(
provider_key="github",
client_id="your-github-client-id",
client_secret="your-github-client-secret",
auth_url="https://github.com/login/oauth/authorize",
token_url="https://github.com/login/oauth/access_token",
)
# Delegate to an agent
client.delegate_credential(
connection_id="connection-uuid",
agent_id="agid_acme_a1b2c3d4",
scopes_delegated=["repo:read"],
)
# Agent requests a token
token = client.request_token(
agent_id="agid_acme_a1b2c3d4",
scope="repo:read",
)pythonSecuritynoteEphemeral Agents (did:key)
Ephemeral agents are short-lived agents that bootstrap identity on instantiation using the W3C did:key format. They auto-expire after a configurable TTL without manual cleanup.
When to Use Ephemeral Agents
Use ephemeral agents for temporary tasks, one-off workflows, CI/CD pipelines, testing, or any scenario where an agent doesn't need a persistent identity. They still get full permission checks and audit trail coverage.
Register an Ephemeral Agent
// Register an ephemeral agent (auto-expires in 1 hour)
const result = await vorim.registerEphemeral({
capabilities: ['data-processing', 'report-generation'],
scopes: ['agent:read', 'agent:write'],
ttl_seconds: 3600, // 1 hour (min: 1, max: 86400)
});
console.log(result.did_key); // did:key:z6Mkp...
console.log(result.ttl_seconds); // 3600
console.log(result.expires_at); // ISO timestamp
console.log(result.private_key); // Ed25519 key (returned once)typescriptPython SDK
from vorim import Vorim
client = Vorim(api_key="agid_sk_live_...")
# Register ephemeral agent (5-minute lifetime)
result = client.register_ephemeral(
capabilities=["temp-task"],
scopes=["agent:read", "agent:execute"],
ttl_seconds=300,
)
print(result["did_key"]) # did:key:z6Mkp...
print(result["expires_at"]) # auto-expires in 5 minutespythonEphemeral vs. Persistent Agents
| Property | Persistent | Ephemeral |
|---|---|---|
| ID format | agid_{org}_{uuid} | did:key:z6Mk... |
| Lifetime | Permanent (until revoked) | TTL-based (1s to 24h) |
| Registration | POST /agents | POST /agents/ephemeral |
| Permissions | Updatable after creation | Fixed at creation, expire with agent |
| Cleanup | Manual revocation | Automatic on TTL expiry |
| Audit trail | Full | Full (attributable to did:key) |
| Use case | Production services | Temporary tasks, testing, CI/CD |
Auto-cleanupnoteWebhooks
Vorim POSTs to your endpoint when an audit event matches an alert rule you have configured. The body carries the rule that matched and the event that matched it, so you can route on either without a second lookup.
What arrives
Four headers travel with every delivery. Content-Type is application/json, X-Vorim-Event repeats the event type so you can route before parsing, X-Vorim-Timestamp is the ISO 8601 time the body was signed, and X-Vorim-Signature carries the HMAC. The signature header is present only when the deployment has a signing secret configured.
POST https://your-endpoint.example.com/vorim
Content-Type: application/json
X-Vorim-Event: tool_call
X-Vorim-Timestamp: 2026-08-31T09:14:02.118Z
X-Vorim-Signature: sha256=9f2b...c41d
{
"rule_id": "rule_7f3a2b",
"rule_name": "Denied transactions",
"event": {
"event_type": "tool_call",
"agent_id": "agid_acme_b5f35578",
"action": "invoice.refund",
"result": "denied"
},
"timestamp": "2026-08-31T09:14:02.118Z"
}typescriptVerify the signature
Recompute the HMAC and compare it in constant time. Verify against the raw request body, never against JSON you have parsed and re-serialised, because a round trip through your JSON library will reorder keys or change number formatting and the MAC will not match.
Reject anything whose timestamp is more than 300 seconds from now. That window is wide enough for ordinary clock skew and narrow enough to make a replayed delivery useless.
import { createHmac, timingSafeEqual } from 'node:crypto';
// You need the RAW body: a JSON round trip changes the bytes the MAC covers.
app.post('/vorim', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.get('X-Vorim-Timestamp') ?? '';
const received = req.get('X-Vorim-Signature') ?? '';
const age = Math.abs(Date.now() - Date.parse(timestamp)) / 1000;
if (!timestamp || Number.isNaN(age) || age > 300) return res.sendStatus(400);
const expected = 'sha256=' + createHmac('sha256', process.env.VORIM_WEBHOOK_SECRET)
.update(timestamp + '.' + req.body.toString('utf8'))
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(received);
if (a.length !== b.length || !timingSafeEqual(a, b)) return res.sendStatus(401);
const event = JSON.parse(req.body.toString('utf8'));
res.sendStatus(200); // acknowledge fast, process out of band
});typescriptWhat we do not promise
Rate Limits
Limits are per API key and per minute unless stated otherwise. Exceeding one returns HTTP 429 with the code RATE_LIMITED and a Retry-After header in seconds. Honour that header rather than retrying on a fixed interval.
POST /v1/audit/events 600 requests / minute
GET /v1/audit/events 120 requests / minute
POST /v1/audit/export 10 requests / minute
POST /v1/runtime/decisions 5000 requests / minute
Webhook rule create/update 20 requests / minute
Auth endpoints are limited per IP, not per key:
login 10 attempts / 15 minutes
register 3 / hour
password reset 3 / hourtypescriptIngest is deliberately the loosest of these. Emitting audit events is the thing an agent under load does most, and a limit that throttles the record but not the action would leave you with a gap in the log rather than a slowed-down agent.
If you are batching, remember that one request carrying 1,000 events costs one unit against the ingest limit, not a thousand. Batching is the answer to a rate limit far more often than a higher limit is.
Go Live
Everything below is off or optional by default, because each one changes behaviour in a way that would break an existing integration if we turned it on for you. Work through them before you depend on the record in front of an auditor.
Turn on signing, then enforce it
The SDK signs at source by default from 3.1 onward, but the server accepts unsigned events so an older client is never locked out. Once every client you run is signing, set VORIM_VERIFY_AUDIT_SIGNATURES so a present-but-invalid signature is rejected rather than stored. Check your own traffic first: enabling it while an old SDK is still emitting will drop those events.
Decide what leaves your process
If your agents handle anything you would not want in an export, turn on no-payload mode and switch your hash fields to keyed commitments. Both are described under Payload Privacy. This is the setting enterprise security review asks about, and it is much easier to enable before you have traffic than after.
Chain your events
Hash chaining is off by default. With it on, deleting a single event from the middle of an agent history becomes detectable rather than invisible, which is most of the value of having a ledger at all.
const vorim = createVorim({
apiKey: process.env.VORIM_API_KEY,
chainEvents: true, // prev_event_hash on every event
canonicalForm: 'v1', // signature covers metadata and delegation too
noPayload: true, // refuse to transmit content at all
});typescriptCheck an export before you need one
Export a bundle and run it through the open-source verifier once, on a normal week, rather than discovering the shape of the output on the day somebody asks for it. The verifier is dependency-free and runs offline, so a counterparty can repeat exactly what you just did.
npx @vorim/verify bundle.jsontypescriptSign your webhooks
Set a webhook signing secret before you point a production system at a Vorim webhook. Without one the deliveries carry no signature and your receiver cannot tell our POST from anyone else who has learned the URL.
How Pricing Works
Vorim is usage-based. You pay for what your agents do, not how many you create.
The meter is the audit event: every action your agent emits through the SDK is one unit. That includes permission checks, agent actions, and the audit records the SDK writes on your behalf. Agent registration, identity issuance, and trust score reads do not count against the meter.
Each tier comes with a monthly included volume. Agents and seats are capped per tier (a separate constraint that exists because they map to product surface area, not infrastructure cost):
- Starter, Custom: 100,000 audit events / month, unlimited agents, 90-day retention.
- Growth, Custom: 1,000,000 audit events / month, unlimited agents, unlimited retention.
- Enterprise, Custom: unlimited audit events, unlimited agents, commit-and-burst pricing, self-hosted option, SSO/SAML, custom MSA.
Why events and not agents? Agent count taxes the behavior the product is built to encourage (creating more agents, smaller-scoped agents, ephemeral agents). Event count tracks the actual unit of value: signed records that a regulator, customer, or counterparty can verify. Pricing on agents would make the right architectural choice expensive; pricing on events keeps the incentives aligned.
Overage and cap behavior. If you exceed your included monthly volume, events keep recording and your dashboard prompts an upgrade. There’s no automatic per-event overage charge or hard rejection at the cap. For per-event overage rates and commit-and-burst commercial terms, contact us in conversation.
For Starter or Growth, book a demo or email team@vorim.ai. For Enterprise, book a call to discuss commit-and-burst, self-hosted deployment, and SSO/SAML.
API Reference
All REST API endpoints are prefixed with /v1. Authenticate via Authorization: Bearer <jwt> or Bearer agid_sk_*.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /auth/register | None | Create organization + user |
POST | /auth/login | None | Email/password login |
POST | /auth/refresh | None | Refresh JWT tokens |
GET | /agents | JWT | List agents |
POST | /agents | JWT | Register new agent |
GET | /agents/:id | JWT | Get agent details |
PATCH | /agents/:id | JWT | Update agent |
DELETE | /agents/:id | JWT | Revoke agent |
POST | /agents/:id/permissions | JWT | Grant permission |
GET | /agents/:id/permissions | JWT | List permissions |
DELETE | /agents/:id/permissions/:scope | JWT | Revoke permission |
POST | /agents/:id/permissions/verify | JWT | Check permission |
POST | /audit/events | API Key | Submit audit events |
GET | /audit/events | API Key | Query audit events |
POST | /audit/export | API Key | Export audit bundle |
GET | /trust/verify/:agentId | None | Public trust check |
GET | /trust/badge/:agentId.svg | None | Embeddable SVG badge |
Book a demo for a walkthrough, or contact us for support. For enterprise needs, reach out at sales@vorim.ai.