ExamplesAdvanced
Human Approval Workflow
Agent requests operator approval before sensitive actions
Overview
Not all agent operations should be fully autonomous. For sensitive actions — like bulk email sends, account upgrades, or data deletion — you want human approval before the agent proceeds.
This example shows how to implement a human-in-the-loop approval workflow using the /v1/notify-operator endpoint.
Info
Best for: Agents that perform high-impact operations (bulk sends, payments, data deletion) where you want explicit operator confirmation before proceeding.
Architecture
Agent detects need for approval
↓
POST /v1/notify-operator (creates approval request)
↓
Operator receives magic link (email, Slack, Discord)
↓
Operator clicks link → instant approval
↓
Agent receives webhook: approval.granted
↓
Agent proceeds with approved actionComplete Implementation
import { DaimonClient } from 'daimon-email';
class ApprovalWorkflowAgent {
private client: DaimonClient;
private pendingApprovals: Map<string, any> = new Map();
constructor(apiKey: string) {
this.client = new DaimonClient({ apiKey });
}
async sendBulkEmail(recipients: string[], subject: string, body: string) {
// Check if this needs approval (e.g., > 100 recipients)
if (recipients.length > 100) {
console.log(`⚠️ Bulk send to ${recipients.length} recipients requires approval`);
const approval = await this.requestApproval({
action: 'bulk_send',
context: {
recipient_count: recipients.length,
subject: subject,
preview: body.slice(0, 200)
},
reason: `Sending to ${recipients.length} recipients exceeds safety threshold`,
urgency: 'medium'
});
console.log('⏳ Waiting for operator approval...');
console.log('📧 Approval request sent to operator');
// Wait for approval (via webhook)
return approval.requestId;
}
// Safe to send without approval
return this.executeBulkSend(recipients, subject, body);
}
async requestApproval(params: {
action: string;
context: any;
reason: string;
urgency?: 'low' | 'medium' | 'high';
}) {
const approval = await this.client.account.notifyOperator({
type: 'approval_request',
action: params.action,
context: params.context,
reason: params.reason,
urgency: params.urgency || 'medium',
callbackUrl: `https://my-agent.com/webhook/approval`,
expiresIn: 3600 // 1 hour
});
// Store pending approval
this.pendingApprovals.set(approval.requestId, {
action: params.action,
context: params.context,
timestamp: Date.now()
});
return approval;
}
async handleApprovalWebhook(event: any) {
const { requestId, status, approvedAt, approvedBy } = event;
const pending = this.pendingApprovals.get(requestId);
if (!pending) {
console.warn('Unknown approval request:', requestId);
return;
}
if (status === 'approved') {
console.log('✅ Approval granted by:', approvedBy);
console.log('⏱️ Approved in:', Date.now() - pending.timestamp, 'ms');
// Execute the approved action
await this.executeApprovedAction(pending);
this.pendingApprovals.delete(requestId);
} else if (status === 'denied') {
console.log('❌ Approval denied by:', approvedBy);
console.log('Reason:', event.denialReason);
this.pendingApprovals.delete(requestId);
} else if (status === 'expired') {
console.log('⏰ Approval request expired');
this.pendingApprovals.delete(requestId);
}
}
private async executeApprovedAction(approval: any) {
switch (approval.action) {
case 'bulk_send':
const { recipient_count, subject, preview } = approval.context;
console.log(`📧 Executing approved bulk send to ${recipient_count} recipients`);
// Execute the send
break;
case 'account_upgrade':
console.log('💳 Executing approved account upgrade');
// Process upgrade
break;
case 'data_deletion':
console.log('🗑️ Executing approved data deletion');
// Delete data
break;
default:
console.warn('Unknown action:', approval.action);
}
}
private async executeBulkSend(recipients: string[], subject: string, body: string) {
console.log(`📧 Sending to ${recipients.length} recipients...`);
for (const recipient of recipients) {
await this.client.inboxes.send('inb_xxx', {
to: recipient,
subject,
body
});
}
console.log('✅ Bulk send complete');
}
}
// Usage
const agent = new ApprovalWorkflowAgent('dm_live_xxx');
// This will trigger approval flow
await agent.sendBulkEmail(
['user1@example.com', 'user2@example.com', /* ... 500 more */],
'Product Update',
'We are excited to announce...'
);
// Webhook handler
app.post('/webhook/approval', async (req, res) => {
await agent.handleApprovalWebhook(req.body);
res.status(200).send('OK');
});import time
from daimon_email import DaimonClient
class ApprovalWorkflowAgent:
def __init__(self, api_key: str):
self.client = DaimonClient(api_key=api_key)
self.pending_approvals = {}
def send_bulk_email(self, recipients: list, subject: str, body: str):
# Check if this needs approval (e.g., > 100 recipients)
if len(recipients) > 100:
print(f'⚠️ Bulk send to {len(recipients)} recipients requires approval')
approval = self.request_approval({
'action': 'bulk_send',
'context': {
'recipient_count': len(recipients),
'subject': subject,
'preview': body[:200]
},
'reason': f'Sending to {len(recipients)} recipients exceeds safety threshold',
'urgency': 'medium'
})
print('⏳ Waiting for operator approval...')
print('📧 Approval request sent to operator')
# Wait for approval (via webhook)
return approval['request_id']
# Safe to send without approval
return self.execute_bulk_send(recipients, subject, body)
def request_approval(self, params: dict):
approval = self.client.account.notify_operator({
'type': 'approval_request',
'action': params['action'],
'context': params['context'],
'reason': params['reason'],
'urgency': params.get('urgency', 'medium'),
'callback_url': 'https://my-agent.com/webhook/approval',
'expires_in': 3600 # 1 hour
})
# Store pending approval
self.pending_approvals[approval['request_id']] = {
'action': params['action'],
'context': params['context'],
'timestamp': time.time()
}
return approval
def handle_approval_webhook(self, event: dict):
request_id = event['request_id']
status = event['status']
pending = self.pending_approvals.get(request_id)
if not pending:
print(f'Unknown approval request: {request_id}')
return
if status == 'approved':
print(f'✅ Approval granted by: {event["approved_by"]}')
print(f'⏱️ Approved in: {time.time() - pending["timestamp"]} seconds')
# Execute the approved action
self.execute_approved_action(pending)
del self.pending_approvals[request_id]
elif status == 'denied':
print(f'❌ Approval denied by: {event["approved_by"]}')
print(f'Reason: {event.get("denial_reason")}')
del self.pending_approvals[request_id]
elif status == 'expired':
print('⏰ Approval request expired')
del self.pending_approvals[request_id]
def execute_approved_action(self, approval: dict):
if approval['action'] == 'bulk_send':
context = approval['context']
print(f'📧 Executing approved bulk send to {context["recipient_count"]} recipients')
# Execute the send
elif approval['action'] == 'account_upgrade':
print('💳 Executing approved account upgrade')
# Process upgrade
elif approval['action'] == 'data_deletion':
print('🗑️ Executing approved data deletion')
# Delete data
else:
print(f'Unknown action: {approval["action"]}')
def execute_bulk_send(self, recipients: list, subject: str, body: str):
print(f'📧 Sending to {len(recipients)} recipients...')
for recipient in recipients:
self.client.inboxes.send('inb_xxx', {
'to': recipient,
'subject': subject,
'body': body
})
print('✅ Bulk send complete')
# Usage
agent = ApprovalWorkflowAgent('dm_live_xxx')
# This will trigger approval flow
agent.send_bulk_email(
['user1@example.com', 'user2@example.com'] + ['user{}@example.com'.format(i) for i in range(500)],
'Product Update',
'We are excited to announce...'
)Approval Request Formats
Standard Approval Request
{
"type": "approval_request",
"action": "bulk_send",
"context": {
"recipient_count": 500,
"subject": "Product Update",
"preview": "We are excited to announce..."
},
"reason": "Sending to 500 recipients exceeds safety threshold",
"urgency": "medium",
"callbackUrl": "https://my-agent.com/webhook/approval",
"expiresIn": 3600
}Upgrade Approval Request
{
"type": "approval_request",
"action": "account_upgrade",
"context": {
"currentTier": "free",
"requestedTier": "developer",
"monthlyCost": 9,
"reason": "Need sending capabilities"
},
"reason": "Agent requires sending access for email automation",
"urgency": "high"
}Operator Experience
When an approval is requested, the operator receives a notification with a magic link:
Subject: Agent Approval Required: Bulk Send
Your agent needs approval for the following action:
Action: Send bulk email
Recipients: 500
Subject: Product Update
Urgency: Medium
[Approve Action] [Deny Action]
This request expires in 1 hour.Clicking "Approve" instantly triggers the webhook to your agent.
Advanced Patterns
Multi-Step Approval
// Step 1: Request approval for upgrade
const upgradeApproval = await agent.requestApproval({
action: 'account_upgrade',
context: { tier: 'growth', cost: 29 },
reason: 'Need higher send limits'
});
// Step 2: After upgrade approved, request approval for bulk send
const sendApproval = await agent.requestApproval({
action: 'bulk_send',
context: { recipientCount: 1000 },
reason: 'Large campaign launch'
});Conditional Approval
// Only request approval for high-value operations
if (operation.estimatedCost > 100) {
await agent.requestApproval({
action: 'execute_campaign',
context: { estimatedCost: operation.estimatedCost },
reason: 'High-cost operation requires approval'
});
}Approval with Timeout Fallback
const approval = await agent.requestApproval({
action: 'data_export',
context: { recordCount: 10000 },
reason: 'Large data export',
expiresIn: 300 // 5 minutes
});
// If no response in 5 minutes, execute safe fallback
setTimeout(() => {
if (agent.pendingApprovals.has(approval.requestId)) {
console.log('Approval timeout - executing safe fallback');
agent.executeSafeDataExport();
}
}, 300000);