daimon.email
ExamplesAdvanced

SaaS Platform Integration

Building a SaaS product on daimon.email

Overview

This example shows how to build a SaaS platform that provides email-capable AI agents to your customers. Each customer gets their own agent with dedicated email infrastructure, while you manage billing and operations centrally.

Info

Multi-tenancy: Provision isolated inboxes per customer, route webhooks by customer ID, integrate billing into your product, and provide white-labeled email capabilities.

Architecture

Your SaaS Platform
  ├── Customer A
  │   ├── Agent inbox: customer-a-agent@daimon.email
  │   └── Webhook → your-platform.com/webhooks/customer-a
  ├── Customer B
  │   ├── Agent inbox: customer-b-agent@daimon.email
  │   └── Webhook → your-platform.com/webhooks/customer-b
  └── Customer C
      ├── Agent inbox: customer-c-agent@daimon.email
      └── Webhook → your-platform.com/webhooks/customer-c

Your Platform manages:
  - Customer provisioning
  - Usage tracking
  - Billing (pass-through or markup)
  - Support

Complete Implementation

import { DaimonClient } from 'daimon-email';
import Stripe from 'stripe';

interface Customer {
  id: string;
  email: string;
  plan: 'free' | 'pro' | 'enterprise';
  stripeCustomerId: string;
}

class AgentPlatform {
  private daimonClient: DaimonClient;
  private stripe: Stripe;
  private customers: Map<string, { customer: Customer; agentInbox: any }> = new Map();

  constructor(daimonApiKey: string, stripeSecretKey: string) {
    this.daimonClient = new DaimonClient({ apiKey: daimonApiKey });
    this.stripe = new Stripe(stripeSecretKey, { apiVersion: '2023-10-16' });
  }

  async onboardCustomer(customer: Customer) {
    console.log(`🎉 Onboarding customer: ${customer.email}`);

    // 1. Create Stripe customer
    const stripeCustomer = await this.stripe.customers.create({
      email: customer.email,
      metadata: {
        platformCustomerId: customer.id
      }
    });

    // 2. Provision agent inbox via daimon.email
    const agentInbox = await this.daimonClient.inboxes.create({
      username: `customer-${customer.id}-agent`,
      clientId: `platform-customer-${customer.id}`,
      metadata: {
        customerId: customer.id,
        customerEmail: customer.email,
        plan: customer.plan
      }
    });

    // 3. Set up customer-specific webhook
    await this.daimonClient.webhooks.create({
      endpointUrl: `https://your-platform.com/webhooks/${customer.id}`,
      events: ['message.received', 'message.sent'],
      inboxId: agentInbox.result.id,
      metadata: {
        customerId: customer.id
      }
    });

    // 4. Store customer data
    this.customers.set(customer.id, {
      customer: { ...customer, stripeCustomerId: stripeCustomer.id },
      agentInbox: agentInbox.result
    });

    // 5. Send welcome email with agent details
    await this.sendWelcomeEmail(customer, agentInbox.result);

    console.log(`✅ Customer ${customer.id} onboarded`);
    console.log(`   Agent inbox: ${agentInbox.result.address}`);
    console.log(`   API key: ${agentInbox.result.apiKey.slice(0, 20)}...`);

    return {
      agentEmail: agentInbox.result.address,
      agentApiKey: agentInbox.result.apiKey,
      webhookUrl: `https://your-platform.com/webhooks/${customer.id}`
    };
  }

  async sendWelcomeEmail(customer: Customer, agentInbox: any) {
    // Send via your email provider (SendGrid, etc.)
    console.log(`📧 Sending welcome email to ${customer.email}`);
  }

  async handleCustomerWebhook(customerId: string, event: any) {
    const customerData = this.customers.get(customerId);
    if (!customerData) {
      console.warn('Unknown customer:', customerId);
      return;
    }

    console.log(`📨 Webhook for customer ${customerId}: ${event.event}`);

    // Route to customer-specific processing
    switch (event.event) {
      case 'message.received':
        await this.processInboundEmail(customerId, event.message);
        break;

      case 'message.sent':
        await this.trackUsage(customerId, 'message_sent', event.message);
        break;

      default:
        console.log('Unhandled event:', event.event);
    }
  }

  async processInboundEmail(customerId: string, message: any) {
    console.log(`📬 Processing email for customer ${customerId}`);
    console.log(`   From: ${message.from}`);
    console.log(`   Subject: ${message.subject}`);

    // Customer-specific processing logic here
    // Could trigger their AI agent, update their database, etc.
  }

  async trackUsage(customerId: string, action: string, data: any) {
    const customerData = this.customers.get(customerId);
    if (!customerData) return;

    // Track usage for billing
    console.log(`📊 Tracking usage for ${customerId}: ${action}`);

    // Report to Stripe
    if (customerData.customer.plan !== 'free') {
      await this.stripe.billing.meterEvents.create({
        event_name: action,
        payload: {
          stripe_customer_id: customerData.customer.stripeCustomerId,
          value: '1'
        }
      });
    }
  }

  async upgradeCustomer(customerId: string, newPlan: 'free' | 'pro' | 'enterprise') {
    const customerData = this.customers.get(customerId);
    if (!customerData) throw new Error('Customer not found');

    console.log(`⬆️  Upgrading customer ${customerId} to ${newPlan}`);

    // 1. Update Stripe subscription
    const subscription = await this.stripe.subscriptions.create({
      customer: customerData.customer.stripeCustomerId,
      items: [{ price: this.getPriceId(newPlan) }]
    });

    // 2. Update daimon.email inbox metadata
    await this.daimonClient.inboxes.update(customerData.agentInbox.id, {
      metadata: {
        ...customerData.agentInbox.metadata,
        plan: newPlan
      }
    });

    // 3. Update local state
    customerData.customer.plan = newPlan;

    console.log(`✅ Customer ${customerId} upgraded to ${newPlan}`);

    return subscription;
  }

  private getPriceId(plan: string): string {
    const prices = {
      pro: 'price_pro_monthly',
      enterprise: 'price_enterprise_monthly'
    };
    return prices[plan] || '';
  }

  async getCustomerDashboard(customerId: string) {
    const customerData = this.customers.get(customerId);
    if (!customerData) throw new Error('Customer not found');

    // Fetch threads and messages for this customer's inbox
    const threads = await this.daimonClient.threads.list({
      inboxId: customerData.agentInbox.id
    });

    const messages = await this.daimonClient.messages.list({
      inboxId: customerData.agentInbox.id,
      limit: 100
    });

    return {
      customer: customerData.customer,
      agentInbox: {
        address: customerData.agentInbox.address,
        status: customerData.agentInbox.status
      },
      stats: {
        totalThreads: threads.length,
        totalMessages: messages.length,
        messagesThisMonth: this.countMessagesThisMonth(messages)
      },
      recentActivity: threads.slice(0, 10)
    };
  }

  private countMessagesThisMonth(messages: any[]): number {
    const startOfMonth = new Date();
    startOfMonth.setDate(1);
    startOfMonth.setHours(0, 0, 0, 0);

    return messages.filter(m => new Date(m.receivedAt) >= startOfMonth).length;
  }

  async offboardCustomer(customerId: string) {
    const customerData = this.customers.get(customerId);
    if (!customerData) throw new Error('Customer not found');

    console.log(`👋 Offboarding customer: ${customerId}`);

    // 1. Cancel Stripe subscription
    const subscriptions = await this.stripe.subscriptions.list({
      customer: customerData.customer.stripeCustomerId
    });

    for (const sub of subscriptions.data) {
      await this.stripe.subscriptions.cancel(sub.id);
    }

    // 2. Delete agent inbox (optional - could just deactivate)
    await this.daimonClient.inboxes.delete(customerData.agentInbox.id);

    // 3. Remove from local state
    this.customers.delete(customerId);

    console.log(`✅ Customer ${customerId} offboarded`);
  }
}

// Usage
const platform = new AgentPlatform(
  'dm_live_platform_xxx',
  'sk_live_stripe_xxx'
);

// Onboard new customer
const onboarding = await platform.onboardCustomer({
  id: 'cust_abc123',
  email: 'customer@example.com',
  plan: 'pro',
  stripeCustomerId: ''
});

console.log('Customer agent email:', onboarding.agentEmail);
console.log('Customer API key:', onboarding.agentApiKey);

// Handle webhooks
app.post('/webhooks/:customerId', async (req, res) => {
  const { customerId } = req.params;
  await platform.handleCustomerWebhook(customerId, req.body);
  res.status(200).send('OK');
});

// Get customer dashboard
app.get('/api/customers/:customerId/dashboard', async (req, res) => {
  const dashboard = await platform.getCustomerDashboard(req.params.customerId);
  res.json(dashboard);
});
from daimon_email import DaimonClient
import stripe

class AgentPlatform:
    def __init__(self, daimon_api_key: str, stripe_secret_key: str):
        self.daimon_client = DaimonClient(api_key=daimon_api_key)
        stripe.api_key = stripe_secret_key
        self.customers = {}

    def onboard_customer(self, customer: dict):
        print(f'🎉 Onboarding customer: {customer["email"]}')

        # 1. Create Stripe customer
        stripe_customer = stripe.Customer.create(
            email=customer['email'],
            metadata={'platform_customer_id': customer['id']}
        )

        # 2. Provision agent inbox via daimon.email
        agent_inbox = self.daimon_client.inboxes.create({
            'username': f'customer-{customer["id"]}-agent',
            'client_id': f'platform-customer-{customer["id"]}',
            'metadata': {
                'customer_id': customer['id'],
                'customer_email': customer['email'],
                'plan': customer['plan']
            }
        })

        # 3. Set up customer-specific webhook
        self.daimon_client.webhooks.create({
            'endpoint_url': f'https://your-platform.com/webhooks/{customer["id"]}',
            'events': ['message.received', 'message.sent'],
            'inbox_id': agent_inbox['result']['id'],
            'metadata': {'customer_id': customer['id']}
        })

        # 4. Store customer data
        self.customers[customer['id']] = {
            'customer': {**customer, 'stripe_customer_id': stripe_customer.id},
            'agent_inbox': agent_inbox['result']
        }

        print(f'✅ Customer {customer["id"]} onboarded')
        print(f'   Agent inbox: {agent_inbox["result"]["address"]}')

        return {
            'agent_email': agent_inbox['result']['address'],
            'agent_api_key': agent_inbox['result']['api_key']
        }

    def handle_customer_webhook(self, customer_id: str, event: dict):
        customer_data = self.customers.get(customer_id)
        if not customer_data:
            print(f'Unknown customer: {customer_id}')
            return

        print(f'📨 Webhook for customer {customer_id}: {event["event"]}')

        if event['event'] == 'message.received':
            self.process_inbound_email(customer_id, event['message'])

        elif event['event'] == 'message.sent':
            self.track_usage(customer_id, 'message_sent', event['message'])

    def process_inbound_email(self, customer_id: str, message: dict):
        print(f'📬 Processing email for customer {customer_id}')
        print(f'   From: {message["from"]}')
        print(f'   Subject: {message["subject"]}')

# Usage
platform = AgentPlatform('dm_live_platform_xxx', 'sk_live_stripe_xxx')

onboarding = platform.onboard_customer({
    'id': 'cust_abc123',
    'email': 'customer@example.com',
    'plan': 'pro'
})

Pricing Models

Pass-Through Pricing

Charge customers exactly what daimon.email costs:

const tiers = {
  free: { price: 0, daimonTier: 'free' },
  pro: { price: 9, daimonTier: 'developer' },
  enterprise: { price: 29, daimonTier: 'growth' }
};

Markup Pricing

Add your margin on top of daimon.email costs:

const tiers = {
  starter: { price: 19, daimonTier: 'developer', margin: 10 },
  business: { price: 49, daimonTier: 'growth', margin: 20 },
  enterprise: { price: 199, daimonTier: 'enterprise', margin: 100 }
};

Usage-Based Pricing

Charge per message or per thread:

const pricing = {
  perMessage: 0.01, // $0.01 per message
  perThread: 0.05,  // $0.05 per thread
  perAgent: 5       // $5 per agent per month
};

White-Label Configuration

Custom Email Domains (Sprint 3)

// Customer gets emails from their own domain
await platform.provisionCustomDomain(customerId, 'agent.customer-domain.com');

// Inbox created: support@agent.customer-domain.com

Branded Upgrade Flows

const upgradeLink = await daimonClient.account.createUpgradeLink({
  reason: 'Upgrade to Pro for unlimited agents',
  returnUrl: `https://your-platform.com/customers/${customerId}/upgraded`,
  brandingColor: '#0066cc',
  brandingLogo: 'https://your-platform.com/logo.png'
});

Customer Self-Service Portal

app.get('/portal/:customerId', async (req, res) => {
  const { customerId } = req.params;
  const dashboard = await platform.getCustomerDashboard(customerId);

  res.render('customer-portal', {
    customer: dashboard.customer,
    agentEmail: dashboard.agentInbox.address,
    stats: dashboard.stats,
    recentActivity: dashboard.recentActivity,
    upgradeUrl: dashboard.customer.plan === 'free' ? '/upgrade' : null
  });
});

Next Steps