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)
- SupportComplete 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.comBranded 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
});
});