ExamplesAdvanced
Multi-Agent Fleet
Managing email for a fleet of AI agents
Overview
When you're running dozens or hundreds of AI agents, each needs its own email identity and inbox. This example shows how to provision and manage a fleet of agents at scale using daimon.email.
Info
Fleet management: Provision 100+ agent inboxes, centralized monitoring, shared webhook routing, and unified billing.
Architecture
Fleet Manager (Platform)
├── Agent 1 (inb_001) → customer-support@daimon.email
├── Agent 2 (inb_002) → sales-outreach@daimon.email
├── Agent 3 (inb_003) → lead-qualifier@daimon.email
├── ...
└── Agent N (inb_NNN) → agent-n@daimon.email
Central Webhook → Routes to specific agents by inbox_id
Central Monitoring → GET /v1/threads (all agents)Complete Implementation
import { DaimonClient } from 'daimon-email';
interface AgentConfig {
id: string;
role: 'support' | 'sales' | 'research';
username: string;
capabilities: string[];
}
class AgentFleetManager {
private platformClient: DaimonClient;
private agents: Map<string, { inbox: any; apiKey: string }> = new Map();
constructor(platformApiKey: string) {
this.platformClient = new DaimonClient({ apiKey: platformApiKey });
}
async provisionFleet(configs: AgentConfig[]) {
console.log(`🚀 Provisioning fleet of ${configs.length} agents...`);
const results = await Promise.all(
configs.map(config => this.provisionAgent(config))
);
console.log(`✅ Fleet provisioned: ${results.length} agents ready`);
return results;
}
async provisionAgent(config: AgentConfig) {
console.log(`📦 Provisioning agent: ${config.id} (${config.role})`);
// Create inbox for this agent
const inbox = await this.platformClient.inboxes.create({
username: config.username,
clientId: `agent-${config.id}`,
metadata: {
agentId: config.id,
role: config.role,
provisionedAt: new Date().toISOString()
}
});
// Store agent info
this.agents.set(config.id, {
inbox: inbox.result,
apiKey: inbox.result.apiKey
});
console.log(`✅ Agent ${config.id}: ${inbox.result.address}`);
return {
agentId: config.id,
inbox: inbox.result,
apiKey: inbox.result.apiKey
};
}
async setupCentralWebhook() {
console.log('🔗 Setting up central webhook...');
const webhook = await this.platformClient.webhooks.create({
endpointUrl: 'https://platform.example.com/webhook/fleet',
events: ['message.received', 'message.sent', 'thread.created'],
// No inbox_id = receives events for all inboxes on this account
metadata: {
type: 'fleet_central_webhook'
}
});
console.log(`✅ Central webhook: ${webhook.id}`);
return webhook;
}
async handleWebhook(event: any) {
const { inboxId, event: eventType, message, thread } = event;
// Find which agent this event belongs to
const agentEntry = Array.from(this.agents.entries()).find(
([_, data]) => data.inbox.id === inboxId
);
if (!agentEntry) {
console.warn('Unknown inbox:', inboxId);
return;
}
const [agentId, agentData] = agentEntry;
console.log(`📨 Event for agent ${agentId}: ${eventType}`);
// Route to specific agent handler
await this.routeToAgent(agentId, event);
}
async routeToAgent(agentId: string, event: any) {
// Route event to specific agent's processing logic
switch (event.event) {
case 'message.received':
console.log(`Agent ${agentId} received: ${event.message.subject}`);
// Trigger agent-specific processing
break;
case 'thread.created':
console.log(`Agent ${agentId} started new thread: ${event.thread.id}`);
break;
default:
console.log(`Agent ${agentId} event: ${event.event}`);
}
}
async getFleetStatus() {
console.log('📊 Fetching fleet status...');
const inboxes = await this.platformClient.inboxes.list();
const threads = await this.platformClient.threads.list();
const status = {
totalAgents: this.agents.size,
activeInboxes: inboxes.filter(i => i.status === 'active').length,
totalThreads: threads.length,
messageVolume: threads.reduce((sum, t) => sum + t.messageCount, 0),
agentBreakdown: this.getAgentBreakdown()
};
console.log('Fleet Status:', status);
return status;
}
private getAgentBreakdown() {
const breakdown: Record<string, number> = {};
this.agents.forEach((data, agentId) => {
const role = data.inbox.metadata?.role || 'unknown';
breakdown[role] = (breakdown[role] || 0) + 1;
});
return breakdown;
}
async scaleFleet(targetCount: number) {
const currentCount = this.agents.size;
if (targetCount > currentCount) {
// Scale up
const toAdd = targetCount - currentCount;
console.log(`⬆️ Scaling up: adding ${toAdd} agents`);
const newConfigs = Array.from({ length: toAdd }, (_, i) => ({
id: `agent-${currentCount + i + 1}`,
role: 'support' as const,
username: `agent-${currentCount + i + 1}`,
capabilities: ['receive', 'send']
}));
await this.provisionFleet(newConfigs);
} else if (targetCount < currentCount) {
// Scale down
const toRemove = currentCount - targetCount;
console.log(`⬇️ Scaling down: removing ${toRemove} agents`);
// Remove least active agents
const agentIds = Array.from(this.agents.keys()).slice(-toRemove);
await this.deprovisionAgents(agentIds);
}
console.log(`✅ Fleet scaled to ${targetCount} agents`);
}
async deprovisionAgents(agentIds: string[]) {
for (const agentId of agentIds) {
const agent = this.agents.get(agentId);
if (!agent) continue;
console.log(`🗑️ Deprovisioning agent: ${agentId}`);
// Delete inbox
await this.platformClient.inboxes.delete(agent.inbox.id);
// Remove from map
this.agents.delete(agentId);
}
}
async broadcastMessage(subject: string, body: string) {
console.log(`📢 Broadcasting to ${this.agents.size} agents...`);
const results = await Promise.all(
Array.from(this.agents.values()).map(async (agent) => {
const client = new DaimonClient({ apiKey: agent.apiKey });
return client.inboxes.send(agent.inbox.id, {
to: 'internal@example.com',
subject,
body
});
})
);
console.log(`✅ Broadcast complete: ${results.length} sent`);
return results;
}
}
// Usage
const fleetManager = new AgentFleetManager('dm_live_platform_key');
// Provision initial fleet
const agentConfigs = [
{ id: 'agent-1', role: 'support', username: 'support-1', capabilities: ['receive', 'send'] },
{ id: 'agent-2', role: 'support', username: 'support-2', capabilities: ['receive', 'send'] },
{ id: 'agent-3', role: 'sales', username: 'sales-1', capabilities: ['receive', 'send'] },
{ id: 'agent-4', role: 'sales', username: 'sales-2', capabilities: ['receive', 'send'] },
{ id: 'agent-5', role: 'research', username: 'research-1', capabilities: ['receive'] }
];
await fleetManager.provisionFleet(agentConfigs);
// Set up central webhook
await fleetManager.setupCentralWebhook();
// Monitor fleet
setInterval(async () => {
await fleetManager.getFleetStatus();
}, 60000); // Every minute
// Scale fleet based on load
await fleetManager.scaleFleet(10); // Scale to 10 agents
// Webhook handler
app.post('/webhook/fleet', async (req, res) => {
await fleetManager.handleWebhook(req.body);
res.status(200).send('OK');
});from daimon_email import DaimonClient
from typing import List, Dict
class AgentFleetManager:
def __init__(self, platform_api_key: str):
self.platform_client = DaimonClient(api_key=platform_api_key)
self.agents = {}
def provision_fleet(self, configs: List[Dict]):
print(f'🚀 Provisioning fleet of {len(configs)} agents...')
results = []
for config in configs:
result = self.provision_agent(config)
results.append(result)
print(f'✅ Fleet provisioned: {len(results)} agents ready')
return results
def provision_agent(self, config: Dict):
print(f'📦 Provisioning agent: {config["id"]} ({config["role"]})')
# Create inbox for this agent
inbox = self.platform_client.inboxes.create({
'username': config['username'],
'client_id': f'agent-{config["id"]}',
'metadata': {
'agent_id': config['id'],
'role': config['role'],
'provisioned_at': datetime.now().isoformat()
}
})
# Store agent info
self.agents[config['id']] = {
'inbox': inbox['result'],
'api_key': inbox['result']['api_key']
}
print(f'✅ Agent {config["id"]}: {inbox["result"]["address"]}')
return {
'agent_id': config['id'],
'inbox': inbox['result'],
'api_key': inbox['result']['api_key']
}
def setup_central_webhook(self):
print('🔗 Setting up central webhook...')
webhook = self.platform_client.webhooks.create({
'endpoint_url': 'https://platform.example.com/webhook/fleet',
'events': ['message.received', 'message.sent', 'thread.created'],
'metadata': {
'type': 'fleet_central_webhook'
}
})
print(f'✅ Central webhook: {webhook["id"]}')
return webhook
def handle_webhook(self, event: Dict):
inbox_id = event['inbox_id']
event_type = event['event']
# Find which agent this event belongs to
agent_id = None
for aid, data in self.agents.items():
if data['inbox']['id'] == inbox_id:
agent_id = aid
break
if not agent_id:
print(f'Unknown inbox: {inbox_id}')
return
print(f'📨 Event for agent {agent_id}: {event_type}')
# Route to specific agent handler
self.route_to_agent(agent_id, event)
def route_to_agent(self, agent_id: str, event: Dict):
event_type = event['event']
if event_type == 'message.received':
print(f'Agent {agent_id} received: {event["message"]["subject"]}')
elif event_type == 'thread.created':
print(f'Agent {agent_id} started new thread: {event["thread"]["id"]}')
else:
print(f'Agent {agent_id} event: {event_type}')
def get_fleet_status(self):
print('📊 Fetching fleet status...')
inboxes = self.platform_client.inboxes.list()
threads = self.platform_client.threads.list()
status = {
'total_agents': len(self.agents),
'active_inboxes': len([i for i in inboxes if i['status'] == 'active']),
'total_threads': len(threads),
'message_volume': sum(t['message_count'] for t in threads),
'agent_breakdown': self.get_agent_breakdown()
}
print(f'Fleet Status: {status}')
return status
def get_agent_breakdown(self):
breakdown = {}
for agent_id, data in self.agents.items():
role = data['inbox'].get('metadata', {}).get('role', 'unknown')
breakdown[role] = breakdown.get(role, 0) + 1
return breakdown
# Usage
fleet_manager = AgentFleetManager('dm_live_platform_key')
# Provision initial fleet
agent_configs = [
{'id': 'agent-1', 'role': 'support', 'username': 'support-1'},
{'id': 'agent-2', 'role': 'support', 'username': 'support-2'},
{'id': 'agent-3', 'role': 'sales', 'username': 'sales-1'},
]
fleet_manager.provision_fleet(agent_configs)
fleet_manager.setup_central_webhook()Key Patterns
Pattern 1: Scoped API Keys
Each agent gets its own API key, scoped to only its inbox:
// Agent 1 can only access its own inbox
const agent1Client = new DaimonClient({ apiKey: agent1ApiKey });
await agent1Client.inboxes.messages.list(agent1InboxId); // ✅ Works
await agent1Client.inboxes.messages.list(agent2InboxId); // ❌ 403 ForbiddenPattern 2: Centralized Monitoring
Platform monitors all agents from one dashboard:
// Platform API key can access all agent inboxes
const allThreads = await platformClient.threads.list(); // All agents' threads
const allMessages = await platformClient.messages.list(); // All agents' messagesPattern 3: Load-Based Scaling
// Monitor queue depth and scale fleet
if (queueDepth > 100 && fleetSize < 50) {
await fleetManager.scaleFleet(fleetSize + 10);
}Fleet Metrics Dashboard
interface FleetMetrics {
totalAgents: number;
activeAgents: number;
messagesPerHour: number;
averageResponseTime: number;
errorRate: number;
agentUtilization: Record<string, number>;
}
async function getFleetMetrics(): Promise<FleetMetrics> {
const threads = await platformClient.threads.list();
const messages = await platformClient.messages.list({ limit: 1000 });
return {
totalAgents: fleetManager.agents.size,
activeAgents: threads.filter(t => t.status === 'active').length,
messagesPerHour: calculateMessagesPerHour(messages),
averageResponseTime: calculateAvgResponseTime(threads),
errorRate: calculateErrorRate(messages),
agentUtilization: calculateUtilization(threads)
};
}Pricing Considerations
Free Tier
- Each agent: Free inbox (receive-only)
- Platform: 1 account, unlimited free inboxes
- Webhooks: Unlimited
Paid Tier
- Each agent: $9/month for sending
- Or platform-wide: Growth tier ($29/month) for 10 sending inboxes
- Enterprise: Custom pricing for 100+ agents