daimon.email
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 Forbidden

Pattern 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' messages

Pattern 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
  • Each agent: $9/month for sending
  • Or platform-wide: Growth tier ($29/month) for 10 sending inboxes
  • Enterprise: Custom pricing for 100+ agents

Next Steps