Veto/docs

Admin SDK

Full API reference for the VetoAdmin management client — programmatic control over policies, decisions, approvals, tools, API keys, and more.

Overview

VetoAdmin is a management client for controlling your entire Veto setup programmatically. While the core Veto class handles runtime validation (wrapping tools, guarding calls), VetoAdmin talks to the Veto management API to create policies, review decisions, resolve approvals, manage MCP upstreams, rotate API keys, and stream events.

All requests are authenticated with your API key via the X-Veto-API-Key header.

Installation

npm install veto-sdk

Constructor

import { VetoAdmin } from 'veto-sdk';

const admin = new VetoAdmin({
  apiKey: 'veto_abc123...',
});

Options

OptionTypeDefaultDescription
apiKeystringRequired. Your Veto API key
baseUrlstring"https://api.veto.so"API base URL (for self-hosted deployments)
timeoutnumber30000Request timeout in milliseconds
const admin = new VetoAdmin({
  apiKey: 'veto_abc123...',
  baseUrl: 'https://veto.internal.corp.com',
  timeout: 10_000,
});

Policies

Full CRUD over tool policies, plus activation control and bulk export.

listPolicies(opts?)

const policies = await admin.listPolicies();

// Filter by project
const projectPolicies = await admin.listPolicies({ projectId: 'proj_abc' });

Returns Policy[].

getPolicy(toolName)

const policy = await admin.getPolicy('send_email');

Returns a Policy:

interface Policy {
  _id: string;
  toolName: string;
  mode: 'deterministic' | 'llm';
  version: number;
  isActive: boolean;
  constraints?: Constraint[];
  outputRules?: OutputRule[];
  llmConfig?: LlmConfig;
  sessionConstraints?: SessionConstraints;
  projectId?: string;
  createdAt: string;
}

createPolicy(input)

const policy = await admin.createPolicy({
  toolName: 'wire_transfer',
  mode: 'deterministic',
  constraints: [
    { argumentName: 'amount', enabled: true, maximum: 10_000, action: 'deny' },
    { argumentName: 'recipient', enabled: true, enum: ['vendor-a', 'vendor-b'] },
  ],
});

updatePolicy(toolName, input)

const updated = await admin.updatePolicy('wire_transfer', {
  mode: 'deterministic',
  constraints: [
    { argumentName: 'amount', enabled: true, maximum: 50_000, action: 'require_approval' },
  ],
});

deletePolicy(toolName)

await admin.deletePolicy('wire_transfer');

activatePolicy(toolName) / deactivatePolicy(toolName)

Toggle a policy on or off without deleting it.

await admin.deactivatePolicy('send_email');
// ... later
await admin.activatePolicy('send_email');

exportPolicies(opts?)

Export all policies as a string (JSON or YAML).

const json = await admin.exportPolicies();
const yaml = await admin.exportPolicies({ format: 'yaml' });
const projectExport = await admin.exportPolicies({ projectId: 'proj_abc', format: 'json' });

Decisions

Query the audit log of validation decisions. Supports pagination, filtering, aggregation, and export.

listDecisions(query?)

const result = await admin.listDecisions({
  limit: 50,
  offset: 0,
  toolName: 'send_email',
  decision: 'deny',
  startDate: '2026-01-01',
  endDate: '2026-03-31',
});

console.log(result.data);       // Decision[]
console.log(result.pagination); // { total, limit, offset, hasMore }

getDecision(id)

const decision = await admin.getDecision('dec_abc123');

Returns a Decision:

interface Decision {
  _id: string;
  toolName: string;
  arguments: Record<string, unknown>;
  decision: 'allow' | 'deny' | 'require_approval';
  mode: string;
  reason?: string;
  latencyMs: number;
  createdAt: string;
}

getDecisionStats(opts?)

const stats = await admin.getDecisionStats({
  projectId: 'proj_abc',
  startDate: '2026-01-01',
  endDate: '2026-03-31',
});
// { total: 1200, allowed: 1050, denied: 130, requireApproval: 20 }

exportDecisions(opts?)

const csv = await admin.exportDecisions({ format: 'csv', startDate: '2026-01-01' });
const json = await admin.exportDecisions({ format: 'json', projectId: 'proj_abc' });

Approvals

List, inspect, and resolve human-in-the-loop approval requests.

listApprovals(opts?)

const all = await admin.listApprovals();
const denied = await admin.listApprovals({ status: 'denied' });

listPendingApprovals()

Convenience method that returns only pending approvals.

const pending = await admin.listPendingApprovals();

getApproval(id)

const approval = await admin.getApproval('apr_abc123');

Returns an Approval:

interface Approval {
  _id: string;
  toolName: string;
  arguments?: Record<string, unknown>;
  status: 'pending' | 'approved' | 'denied' | 'expired';
  expiresAt: string;
  resolvedBy?: string;
  resolvedAt?: string;
  createdAt: string;
}

resolveApproval(id, action, resolvedBy)

const resolved = await admin.resolveApproval('apr_abc123', 'approve', 'user@company.com');

batchResolveApprovals(approvals)

Resolve multiple approvals in a single request.

const result = await admin.batchResolveApprovals([
  { id: 'apr_abc123', action: 'approve', resolvedBy: 'user@company.com' },
  { id: 'apr_def456', action: 'deny', resolvedBy: 'user@company.com' },
]);

for (const item of result.data) {
  if (item.error) {
    console.error(`Failed to resolve ${item.id}: ${item.error}`);
  }
}

Tools

Registered tool definitions. Tools are auto-registered when the SDK calls registerTools() during validation.

listTools()

const tools = await admin.listTools();

Returns Tool[]:

interface Tool {
  _id: string;
  name: string;
  description?: string;
  arguments: Record<string, unknown>[];
}

deleteTool(name)

await admin.deleteTool('deprecated_tool');

Policy Drafts

AI-generated or manually created policy drafts that go through a review workflow before becoming active policies.

listPolicyDrafts(opts?)

const drafts = await admin.listPolicyDrafts();
const pending = await admin.listPolicyDrafts({ status: 'pending_review' });
const projectDrafts = await admin.listPolicyDrafts({ projectId: 'proj_abc' });

createPolicyDraft(input)

const draft = await admin.createPolicyDraft({
  name: 'email-sending-policy',
  description: 'Restrict outbound email to verified domains',
  rules: [
    { tool: 'send_email', constraint: 'regex', argument: 'to', value: '.*@company\\.com$' },
  ],
  status: 'pending_review',
  createdByAgentId: 'agent-7',
});

getPolicyDraft(id)

const draft = await admin.getPolicyDraft('draft_abc123');

Returns a PolicyDraft:

interface PolicyDraft {
  _id: string;
  name: string;
  description?: string;
  status: 'draft' | 'pending_review' | 'approved' | 'rejected';
  createdByAgentId?: string;
  rules: Record<string, unknown>[];
  createdAt: string;
}

approvePolicyDraft(id)

const approved = await admin.approvePolicyDraft('draft_abc123');

rejectPolicyDraft(id, reason?)

const rejected = await admin.rejectPolicyDraft('draft_abc123', 'Too permissive on file access');

MCP Gateway

Manage upstream MCP servers connected through the Veto gateway.

listUpstreams()

const upstreams = await admin.listUpstreams();

Returns McpUpstream[]:

interface McpUpstream {
  _id: string;
  slug: string;
  name: string;
  transport: 'mcp-sse' | 'mcp-stdio';
  url?: string;
  command?: string;
  enabled: boolean;
}

createUpstream(input)

// SSE transport
const upstream = await admin.createUpstream({
  name: 'Internal Tools Server',
  transport: 'mcp-sse',
  url: 'https://tools.internal.corp.com/sse',
  timeoutMs: 15_000,
});

// Stdio transport
const local = await admin.createUpstream({
  name: 'Local MCP Server',
  transport: 'mcp-stdio',
  command: 'npx',
  args: ['-y', '@company/mcp-tools'],
});

deleteUpstream(id)

await admin.deleteUpstream('ups_abc123');

testUpstream(id)

Verify connectivity to an upstream server.

const result = await admin.testUpstream('ups_abc123');

if (result.status === 'ok') {
  console.log(`Connected in ${result.latencyMs}ms`);
} else {
  console.error(`Connection failed: ${result.error}`);
}

Returns an UpstreamTestResult:

interface UpstreamTestResult {
  status: 'ok' | 'error';
  latencyMs?: number;
  error?: string;
}

API Keys

Manage API keys for your organization.

listApiKeys()

const keys = await admin.listApiKeys();

Returns ApiKeyInfo[]:

interface ApiKeyInfo {
  _id: string;
  name: string;
  keyPrefix: string;
  isRevoked: boolean;
  projectId?: string;
  lastUsedAt?: string;
  createdAt: string;
}

createApiKey(input)

The full key is only returned once at creation time. Store it securely.

const created = await admin.createApiKey({
  name: 'CI Pipeline Key',
  projectId: 'proj_abc',
});

console.log(created.key); // "veto_abc123..." — save this, it won't be shown again

Returns ApiKeyCreated:

interface ApiKeyCreated {
  _id: string;
  name: string;
  key: string;
  keyPrefix: string;
}

revokeApiKey(id)

await admin.revokeApiKey('key_abc123');

Events

Real-time event streaming over SSE. Two interfaces: callback-based and async iterable.

onEvent(type, callback)

Subscribe to events with a callback. Returns an EventSubscription with an unsubscribe() method.

const subscription = admin.onEvent('decision.created', (event) => {
  console.log(`New decision: ${event.data.toolName} → ${event.data.decision}`);
});

// Listen for multiple event types
const sub = admin.onEvent(['decision.created', 'approval.resolved'], (event) => {
  console.log(`${event.type}: ${JSON.stringify(event.data)}`);
});

// Stop listening
subscription.unsubscribe();

subscribeEvents(opts?)

Async iterable interface for structured consumption. Works with for await...of.

for await (const event of admin.subscribeEvents({ types: ['decision.created'] })) {
  console.log(event.type, event.data);
}

Event shape:

interface VetoAdminEvent {
  type: string;
  data: Record<string, unknown>;
}

Organizations and Projects

Read-only access to organization and project metadata scoped to your API key.

listOrganizations()

const orgs = await admin.listOrganizations();

listProjects(opts?)

const projects = await admin.listProjects();
const filtered = await admin.listProjects({ organizationId: 'org_abc' });

Error handling

All API errors throw a VetoAdminError with the HTTP status code attached.

import { VetoAdminError } from 'veto-sdk';

try {
  await admin.getPolicy('nonexistent_tool');
} catch (error) {
  if (error instanceof VetoAdminError) {
    console.error(error.message);    // "GET /policies/nonexistent_tool failed (404): ..."
    console.error(error.statusCode); // 404
  }
}

Timeouts throw a VetoAdminError with statusCode: 0:

try {
  await admin.listDecisions();
} catch (error) {
  if (error instanceof VetoAdminError && error.statusCode === 0) {
    console.error('Request timed out');
  }
}

Full example

Putting it together — a script that audits recent denials and auto-approves low-risk pending approvals:

import { VetoAdmin, VetoAdminError } from 'veto-sdk';

const admin = new VetoAdmin({ apiKey: process.env.VETO_API_KEY! });

// Pull denied decisions from the last 7 days
const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
const denied = await admin.listDecisions({
  decision: 'deny',
  startDate: weekAgo,
  limit: 100,
});
console.log(`${denied.pagination.total} denials in the last 7 days`);

// Auto-approve pending read_file approvals
const pending = await admin.listPendingApprovals();
const readFileApprovals = pending.filter((a) => a.toolName === 'read_file');

if (readFileApprovals.length > 0) {
  const result = await admin.batchResolveApprovals(
    readFileApprovals.map((a) => ({
      id: a._id,
      action: 'approve' as const,
      resolvedBy: 'ops-bot',
    }))
  );
  console.log(`Resolved ${result.data.length} read_file approvals`);
}