Skip to main content

Approvals

The approval system provides human oversight for sensitive operations, ensuring critical actions require explicit authorization.

Why Approvals?

Some operations are too important to be fully automated:

  • High-risk actions: Deleting users, bulk operations
  • Compliance requirements: Regulated data access
  • Cost control: Expensive operations need sign-off
  • Quality assurance: Review AI-generated content before sending

How Approvals Work

Operation Requested → Risk Assessment → Approval Queue → Review → Execute/Reject
  1. Agent or system attempts sensitive operation
  2. System evaluates risk level
  3. Approval request created
  4. Admin reviews in dashboard or API
  5. Approved: Operation executes
  6. Rejected: Operation blocked

Approval Configuration

Enable for Operations

await client.settings.update({
approvals: {
enabled: true,

// Operations that require approval
requiredFor: [
'user.delete',
'agent.delete',
'bulk_operation',
'data_export',
'integration.connect'
],

// Risk-based rules
riskRules: [
{
condition: 'tool.cost > 10',
action: 'require_approval',
riskLevel: 'medium'
},
{
condition: 'tool.name matches "admin_*"',
action: 'require_approval',
riskLevel: 'high'
}
],

// Timeout settings
defaultTimeout: 86400000, // 24 hours
autoRejectOnTimeout: true
}
});

Per-Tool Approval

Require approval for specific tools:

await client.tools.update('tool-123', {
permissions: {
requireApproval: true,
approvers: ['admin@company.com'],
riskLevel: 'high'
}
});

Managing Approvals

View Pending Approvals

const pending = await client.approvals.listPending();

for (const request of pending.data) {
console.log({
id: request.id,
operationType: request.operationType,
resourceType: request.resourceType,
resourceId: request.resourceId,
riskLevel: request.riskLevel,
riskReason: request.riskReason,
requestedBy: request.requestedBy,
requestedAt: request.requestedAt,
expiresAt: request.expiresAt
});
}

Approve Request

await client.approvals.approve('request-123', {
reason: 'Verified with user, operation is legitimate'
});

Reject Request

await client.approvals.reject('request-123', {
reason: 'Operation not authorized for this resource'
});

View History

const history = await client.approvals.listHistory({
status: 'approved', // approved, rejected, expired, executed
startDate: '2024-01-01',
limit: 100
});

Risk Levels

LevelDescriptionDefault Timeout
lowMinor operations24 hours
mediumStandard sensitive ops12 hours
highCritical operations4 hours
criticalEmergency-level risk1 hour

Custom Risk Assessment

// Agent can request approval with risk details
await client.approvals.request({
operationType: 'bulk_delete',
resourceType: 'conversations',
resourceId: 'batch-123',
riskLevel: 'high',
riskReason: 'Deleting 500 conversations containing customer data',
context: {
conversationCount: 500,
dateRange: '2024-01-01 to 2024-01-31',
requestedBy: 'cleanup-agent'
}
});

Approval Workflow States

┌──────────┐    ┌──────────┐    ┌──────────┐
│ pending │───▶│ approved │───▶│ executed │
└──────────┘ └──────────┘ └──────────┘
│ │
│ ▼
│ ┌──────────┐
│ │ failed │ (execution failed)
│ └──────────┘

├─────────────────────────────────────▶┌──────────┐
│ │ rejected │
│ └──────────┘

└─────────────────────────────────────▶┌──────────┐
(timeout) │ expired │
└──────────┘

Dashboard Integration

Approval Queue

Access pending approvals in the Dashboard:

  1. Go to SecurityApprovals
  2. View pending requests with risk details
  3. Click to expand and see full context
  4. Approve or reject with optional reason

Notifications

Configure notifications for pending approvals:

await client.settings.update({
approvals: {
notifications: {
email: true,
slack: true,
inApp: true,
urgentOnly: false // Only high/critical if true
}
}
});

Trusted Patterns

Create patterns for operations that don't need approval:

// Trust certain operations from specific agents
await client.approvals.createTrustedPattern({
name: 'IT Agent - User Lookup',
description: 'IT agent reading user info is safe',
conditions: {
agentId: 'it-agent-123',
operationType: 'user.read',
resourceType: 'user'
},
enabled: true
});

// Trust operations under certain thresholds
await client.approvals.createTrustedPattern({
name: 'Small Data Export',
description: 'Exports under 100 records are auto-approved',
conditions: {
operationType: 'data_export',
contextMatch: {
'recordCount': { lt: 100 }
}
}
});

List Trusted Patterns

const patterns = await client.approvals.listTrustedPatterns();

for (const pattern of patterns.data) {
console.log({
name: pattern.name,
conditions: pattern.conditions,
enabled: pattern.enabled,
matchCount: pattern.matchCount
});
}

Approval API

Request Approval

interface ApprovalRequest {
operationType: string;
resourceType: string;
resourceId: string;
riskLevel: 'low' | 'medium' | 'high' | 'critical';
riskReason: string;
context?: Record<string, any>;
expiresIn?: number; // ms
}

const request = await client.approvals.request({
operationType: 'bulk_update',
resourceType: 'agents',
resourceId: 'batch-456',
riskLevel: 'medium',
riskReason: 'Updating configuration for 50 agents',
context: {
agentCount: 50,
changedFields: ['systemPrompt', 'model']
}
});

// Wait for approval
const status = await client.approvals.waitForDecision(request.id, {
timeout: 3600000, // 1 hour
pollInterval: 5000
});

if (status.approved) {
// Proceed with operation
} else {
// Handle rejection
}

Check Approval Status

const status = await client.approvals.get('request-123');

console.log({
status: status.status,
decidedBy: status.decisionInfo?.decidedBy,
decidedAt: status.decisionInfo?.decidedAt,
reason: status.decisionInfo?.reason
});

Webhooks

Get notified of approval events:

await client.webhooks.create({
url: 'https://your-app.com/webhooks',
events: [
'approval.requested',
'approval.approved',
'approval.rejected',
'approval.expired'
]
});

Webhook Payload

{
"event": "approval.requested",
"timestamp": "2024-01-15T10:00:00Z",
"data": {
"id": "request-123",
"operationType": "bulk_delete",
"resourceType": "conversations",
"riskLevel": "high",
"riskReason": "Deleting 500 conversations",
"requestedBy": {
"type": "agent",
"id": "agent-456",
"name": "Cleanup Agent"
},
"expiresAt": "2024-01-15T14:00:00Z"
}
}

Best Practices

1. Don't Over-Approve

Require approval only for truly sensitive operations:

// ✅ Good - specific sensitive operations
requiredFor: ['user.delete', 'data_export', 'bulk_operation']

// ❌ Bad - everything needs approval (creates fatigue)
requiredFor: ['*']

2. Set Reasonable Timeouts

// High-risk: shorter timeout forces prompt decision
{ riskLevel: 'critical', timeout: 3600000 } // 1 hour

// Low-risk: longer timeout is okay
{ riskLevel: 'low', timeout: 86400000 } // 24 hours

3. Provide Context

// ✅ Good - helpful context
await client.approvals.request({
riskReason: 'Deleting 500 conversations from Jan 2024',
context: {
conversationCount: 500,
dateRange: '2024-01-01 to 2024-01-31',
sampleIds: ['conv-1', 'conv-2', 'conv-3']
}
});

// ❌ Bad - no context
await client.approvals.request({
riskReason: 'Bulk delete'
});

4. Use Trusted Patterns for Common Operations

// Reduce approval fatigue for safe, frequent operations
await client.approvals.createTrustedPattern({
name: 'Support Agent - Read User',
conditions: {
agentId: 'support-agent',
operationType: 'user.read'
}
});

Next: Learn about Audit Logs for tracking all activity.