daimon.email
ExamplesOutbound

Sales Outreach Agent

Automated sales email sequences

Overview

A sales outreach agent automates personalized email campaigns, tracks engagement, and follows up intelligently based on recipient behavior. This example demonstrates sending, tracking, and multi-step sequences.

Note

Sending emails requires a paid tier. See the Magic Upgrade Flow for autonomous tier upgrades.

How It Works

  1. Create an inbox on a paid tier
  2. Build a prospect list with personalization data
  3. Send personalized emails via the API
  4. Track replies and engagement via webhooks
  5. Follow up automatically based on response behavior
  6. Maintain conversation context via threads

Complete Implementation

import { DaimonClient } from 'daimon-email';
import express from 'express';

const client = new DaimonClient({ apiKey: process.env.DAIMON_API_KEY });
const app = express();

app.use(express.json());

interface Prospect {
  email: string;
  name: string;
  company: string;
  title: string;
  painPoint?: string;
}

interface CampaignState {
  prospectEmail: string;
  stepNumber: number;
  lastSent: Date;
  replied: boolean;
  threadId?: string;
}

const campaignStates = new Map<string, CampaignState>();

// Step 1: Set up sales inbox with paid tier
async function setupSalesInbox() {
  const inbox = await client.inboxes.create({
    username: 'sales-outreach',
    clientId: 'sales-agent-v1'
  });

  console.log(`Sales inbox: ${inbox.address}`);

  // Check capabilities
  const authedClient = new DaimonClient({ apiKey: inbox.apiKey });
  const capabilities = await authedClient.account.getCapabilities();

  const canSend = capabilities.can.some(c => c.action === 'send_messages');

  if (!canSend) {
    console.log('⚠️  Sending requires paid tier');
    console.log('Generate upgrade link:');

    const upgradeLink = await authedClient.account.createUpgradeLink();
    console.log(upgradeLink.url);

    throw new Error('SEND_REQUIRES_PAID');
  }

  // Register webhook for replies
  const webhook = await authedClient.webhooks.create({
    endpointUrl: 'https://your-sales-agent.com/webhook',
    events: ['message.received'],
    inboxId: inbox.id
  });

  console.log('Webhook registered for reply tracking');

  return { inbox, authedClient };
}

// Step 2: Build prospect list
const prospects: Prospect[] = [
  {
    email: 'john@techstartup.com',
    name: 'John',
    company: 'TechStartup Inc',
    title: 'CTO',
    painPoint: 'scaling infrastructure'
  },
  {
    email: 'sarah@saascorp.io',
    name: 'Sarah',
    company: 'SaaS Corp',
    title: 'VP Engineering',
    painPoint: 'reducing deployment time'
  }
];

// Step 3: Send personalized outreach
async function sendInitialOutreach(inboxId: string, prospect: Prospect) {
  const subject = `Quick question about ${prospect.company}'s ${prospect.painPoint}`;

  const body = `Hi ${prospect.name},

I noticed ${prospect.company} is growing quickly and thought you might be interested in how we've helped similar companies solve ${prospect.painPoint}.

We recently helped a company like yours reduce deployment time by 60% using our platform.

Would you be open to a quick 15-minute call next week to explore if we could help ${prospect.company} achieve similar results?

Best regards,
Sales Agent`;

  try {
    const message = await client.inboxes.send(inboxId, {
      to: prospect.email,
      subject,
      body,
      clientId: `outreach-${prospect.email}-step-1` // Idempotency
    });

    console.log(`Sent initial outreach to ${prospect.email}`);

    // Track campaign state
    campaignStates.set(prospect.email, {
      prospectEmail: prospect.email,
      stepNumber: 1,
      lastSent: new Date(),
      replied: false,
      threadId: message.threadId
    });

    return message;

  } catch (error) {
    if (error.code === 'SEND_LIMIT_EXCEEDED') {
      console.log('Send limit exceeded. Waiting before retry...');
      // Handle rate limiting
    }
    throw error;
  }
}

// Step 4: Handle replies via webhook
app.post('/webhook', async (req, res) => {
  const { event, message } = req.body;

  if (event === 'message.received') {
    await handleReply(message);
  }

  res.status(200).send('OK');
});

async function handleReply(message: any) {
  const prospectEmail = message.from;

  console.log(`Reply received from ${prospectEmail}`);
  console.log(`Subject: ${message.subject}`);
  console.log(`Body: ${message.replyBody}`);

  // Update campaign state
  const state = campaignStates.get(prospectEmail);
  if (state) {
    state.replied = true;
    campaignStates.set(prospectEmail, state);
  }

  // Analyze sentiment/intent
  const isPositive = await analyzeReply(message.replyBody);

  if (isPositive) {
    // Send calendar link or next step
    await sendCalendarLink(message.inboxId, message.id, prospectEmail);
  } else {
    // Graceful disengagement
    await sendDisengagementReply(message.inboxId, message.id);
  }
}

// Step 5: Follow-up sequence
async function runFollowUpSequence(inboxId: string) {
  for (const [prospectEmail, state] of campaignStates.entries()) {
    // Skip if already replied
    if (state.replied) continue;

    const daysSinceLastSent = (Date.now() - state.lastSent.getTime()) / (1000 * 60 * 60 * 24);

    // Send follow-up after 3 days
    if (state.stepNumber === 1 && daysSinceLastSent >= 3) {
      await sendFollowUp1(inboxId, prospectEmail, state);
    }

    // Send second follow-up after 6 days
    if (state.stepNumber === 2 && daysSinceLastSent >= 6) {
      await sendFollowUp2(inboxId, prospectEmail, state);
    }

    // Final follow-up after 10 days
    if (state.stepNumber === 3 && daysSinceLastSent >= 10) {
      await sendFinalFollowUp(inboxId, prospectEmail, state);
    }
  }
}

async function sendFollowUp1(inboxId: string, prospectEmail: string, state: CampaignState) {
  const prospect = prospects.find(p => p.email === prospectEmail);

  const body = `Hi ${prospect.name},

Following up on my previous email about ${prospect.company}'s ${prospect.painPoint}.

I wanted to share a quick case study from a similar company that saw 3x improvement in their deployment pipeline.

Is this something you'd be interested in exploring?

Best,
Sales Agent`;

  await client.inboxes.messages.reply(inboxId, state.threadId, {
    body,
    clientId: `outreach-${prospectEmail}-step-2`
  });

  state.stepNumber = 2;
  state.lastSent = new Date();
  campaignStates.set(prospectEmail, state);

  console.log(`Sent follow-up 1 to ${prospectEmail}`);
}

async function sendFollowUp2(inboxId: string, prospectEmail: string, state: CampaignState) {
  const prospect = prospects.find(p => p.email === prospectEmail);

  const body = `Hi ${prospect.name},

I know you're busy, so I'll keep this brief.

We have a limited number of spots for our Q2 onboarding cohort, and I thought ${prospect.company} would be a great fit.

If you're interested in learning more, I'd love to schedule a quick call.

Otherwise, I'll assume the timing isn't right and won't follow up again.

Best,
Sales Agent`;

  await client.inboxes.messages.reply(inboxId, state.threadId, {
    body,
    clientId: `outreach-${prospectEmail}-step-3`
  });

  state.stepNumber = 3;
  state.lastSent = new Date();
  campaignStates.set(prospectEmail, state);

  console.log(`Sent follow-up 2 to ${prospectEmail}`);
}

async function sendFinalFollowUp(inboxId: string, prospectEmail: string, state: CampaignState) {
  const body = `No worries! I'll close this out on my end.

If anything changes or you'd like to revisit this in the future, feel free to reach out.

Best of luck!
Sales Agent`;

  await client.inboxes.messages.reply(inboxId, state.threadId, {
    body,
    clientId: `outreach-${prospectEmail}-final`
  });

  console.log(`Sent final follow-up to ${prospectEmail}`);
}

async function sendCalendarLink(inboxId: string, messageId: string, prospectEmail: string) {
  const body = `Great! Here's a link to book a time that works for you:

https://calendly.com/sales-agent/demo

Looking forward to connecting!

Best,
Sales Agent`;

  await client.inboxes.messages.reply(inboxId, messageId, {
    body,
    clientId: `calendar-${prospectEmail}`
  });

  console.log(`Sent calendar link to ${prospectEmail}`);
}

async function sendDisengagementReply(inboxId: string, messageId: string) {
  const body = `Thanks for letting me know. I appreciate your time.

Best of luck!
Sales Agent`;

  await client.inboxes.messages.reply(inboxId, messageId, {
    body,
    clientId: `disengage-${messageId}`
  });
}

async function analyzeReply(replyBody: string): Promise<boolean> {
  // Simple sentiment analysis (could use LLM)
  const positiveKeywords = ['interested', 'yes', 'sure', 'sounds good', 'tell me more'];
  const negativeKeywords = ['not interested', 'no thanks', 'remove', 'unsubscribe'];

  const lowerBody = replyBody.toLowerCase();

  if (negativeKeywords.some(kw => lowerBody.includes(kw))) {
    return false;
  }

  if (positiveKeywords.some(kw => lowerBody.includes(kw))) {
    return true;
  }

  return false; // Default to negative if unclear
}

// Run the sales campaign
async function runSalesCampaign() {
  const { inbox, authedClient } = await setupSalesInbox();

  // Send initial outreach to all prospects
  for (const prospect of prospects) {
    await sendInitialOutreach(inbox.id, prospect);
    await new Promise(resolve => setTimeout(resolve, 2000)); // Rate limiting
  }

  // Run follow-up sequence daily
  setInterval(async () => {
    await runFollowUpSequence(inbox.id);
  }, 24 * 60 * 60 * 1000); // Daily

  // Start webhook server
  app.listen(3000, () => {
    console.log('Sales agent running on port 3000');
  });
}

runSalesCampaign();
from daimon_email import DaimonClient
from flask import Flask, request
import os
import time
from datetime import datetime, timedelta
from typing import Dict, List

client = DaimonClient(api_key=os.environ.get('DAIMON_API_KEY'))
app = Flask(__name__)

campaign_states = {}

# Step 1: Set up sales inbox with paid tier
def setup_sales_inbox():
    inbox = client.inboxes.create(
        username='sales-outreach',
        client_id='sales-agent-v1'
    )

    print(f"Sales inbox: {inbox.address}")

    # Check capabilities
    authed_client = DaimonClient(api_key=inbox.api_key)
    capabilities = authed_client.account.get_capabilities()

    can_send = any(c['action'] == 'send_messages' for c in capabilities['can'])

    if not can_send:
        print('⚠️  Sending requires paid tier')
        print('Generate upgrade link:')

        upgrade_link = authed_client.account.create_upgrade_link()
        print(upgrade_link['url'])

        raise Exception('SEND_REQUIRES_PAID')

    # Register webhook for replies
    webhook = authed_client.webhooks.create(
        endpoint_url='https://your-sales-agent.com/webhook',
        events=['message.received'],
        inbox_id=inbox.id
    )

    print('Webhook registered for reply tracking')

    return inbox, authed_client

# Step 2: Build prospect list
prospects = [
    {
        'email': 'john@techstartup.com',
        'name': 'John',
        'company': 'TechStartup Inc',
        'title': 'CTO',
        'pain_point': 'scaling infrastructure'
    },
    {
        'email': 'sarah@saascorp.io',
        'name': 'Sarah',
        'company': 'SaaS Corp',
        'title': 'VP Engineering',
        'pain_point': 'reducing deployment time'
    }
]

# Step 3: Send personalized outreach
def send_initial_outreach(inbox_id: str, prospect: Dict):
    subject = f"Quick question about {prospect['company']}'s {prospect['pain_point']}"

    body = f"""Hi {prospect['name']},

I noticed {prospect['company']} is growing quickly and thought you might be interested in how we've helped similar companies solve {prospect['pain_point']}.

We recently helped a company like yours reduce deployment time by 60% using our platform.

Would you be open to a quick 15-minute call next week to explore if we could help {prospect['company']} achieve similar results?

Best regards,
Sales Agent"""

    try:
        message = client.inboxes.send(inbox_id, {
            'to': prospect['email'],
            'subject': subject,
            'body': body,
            'client_id': f"outreach-{prospect['email']}-step-1"
        })

        print(f"Sent initial outreach to {prospect['email']}")

        # Track campaign state
        campaign_states[prospect['email']] = {
            'prospect_email': prospect['email'],
            'step_number': 1,
            'last_sent': datetime.now(),
            'replied': False,
            'thread_id': message['thread_id']
        }

        return message

    except Exception as e:
        if hasattr(e, 'code') and e.code == 'SEND_LIMIT_EXCEEDED':
            print('Send limit exceeded. Waiting before retry...')
        raise

# Step 4: Handle replies via webhook
@app.post('/webhook')
def handle_webhook():
    data = request.json
    event = data.get('event')
    message = data.get('message')

    if event == 'message.received':
        handle_reply(message)

    return 'OK', 200

def handle_reply(message: Dict):
    prospect_email = message['from']

    print(f"Reply received from {prospect_email}")
    print(f"Subject: {message['subject']}")
    print(f"Body: {message.get('reply_body')}")

    # Update campaign state
    if prospect_email in campaign_states:
        campaign_states[prospect_email]['replied'] = True

    # Analyze sentiment/intent
    is_positive = analyze_reply(message.get('reply_body', ''))

    if is_positive:
        send_calendar_link(message['inbox_id'], message['id'], prospect_email)
    else:
        send_disengagement_reply(message['inbox_id'], message['id'])

def analyze_reply(reply_body: str) -> bool:
    positive_keywords = ['interested', 'yes', 'sure', 'sounds good', 'tell me more']
    negative_keywords = ['not interested', 'no thanks', 'remove', 'unsubscribe']

    lower_body = reply_body.lower()

    if any(kw in lower_body for kw in negative_keywords):
        return False

    if any(kw in lower_body for kw in positive_keywords):
        return True

    return False

def send_calendar_link(inbox_id: str, message_id: str, prospect_email: str):
    body = """Great! Here's a link to book a time that works for you:

https://calendly.com/sales-agent/demo

Looking forward to connecting!

Best,
Sales Agent"""

    client.inboxes.messages.reply(inbox_id, message_id, {
        'body': body,
        'client_id': f"calendar-{prospect_email}"
    })

    print(f"Sent calendar link to {prospect_email}")

def send_disengagement_reply(inbox_id: str, message_id: str):
    body = """Thanks for letting me know. I appreciate your time.

Best of luck!
Sales Agent"""

    client.inboxes.messages.reply(inbox_id, message_id, {
        'body': body,
        'client_id': f"disengage-{message_id}"
    })

# Run the sales campaign
def run_sales_campaign():
    inbox, authed_client = setup_sales_inbox()

    # Send initial outreach to all prospects
    for prospect in prospects:
        send_initial_outreach(inbox['id'], prospect)
        time.sleep(2)  # Rate limiting

    # Start webhook server
    app.run(port=3000)

if __name__ == '__main__':
    run_sales_campaign()

Key Features

  • Personalization: Dynamic content based on prospect data
  • Thread continuity: All follow-ups stay in the same conversation
  • Reply tracking: Webhooks for instant engagement detection
  • Multi-step sequences: Automated follow-ups based on timing
  • Idempotency: Prevents duplicate sends with clientId

Next Steps