返回 Skill 列表
extension
分类: 营销与增长无需 API Key

apollo-core-workflow-b

实现Apollo.io的电子邮件序列和外展工作流程。在构建自动化电子邮件活动、创建序列或通过Apollo管理外展时使用。可以通过诸如“apollo email sequence”、“apollo outreach”、“apollo campaign”、“apollo sequences”、“apollo automated emails”等短语触发。

person作者: jakexiaohubgithub

Apollo Core Workflow B: Email Sequences & Outreach

Overview

Implement Apollo.io's email sequencing and outreach automation capabilities for B2B sales campaigns.

Prerequisites

  • Completed apollo-core-workflow-a (lead search)
  • Apollo account with Sequences feature enabled
  • Connected email account in Apollo

Workflow Components

1. List Existing Sequences

// src/services/apollo/sequences.ts
import { apollo } from '../../lib/apollo/client';

export async function listSequences() {
  const response = await apollo.request({
    method: 'GET',
    url: '/emailer_campaigns',
  });

  return response.emailer_campaigns.map((campaign: any) => ({
    id: campaign.id,
    name: campaign.name,
    status: campaign.active ? 'active' : 'paused',
    stepsCount: campaign.num_steps,
    contactsCount: campaign.contact_count,
    createdAt: campaign.created_at,
    stats: {
      sent: campaign.emails_sent_count,
      opened: campaign.emails_opened_count,
      replied: campaign.emails_replied_count,
      bounced: campaign.emails_bounced_count,
    },
  }));
}

2. Create Email Sequence

// src/services/apollo/create-sequence.ts
interface SequenceStep {
  type: 'auto_email' | 'manual_email' | 'call' | 'task';
  subject?: string;
  body?: string;
  waitDays: number;
}

interface CreateSequenceParams {
  name: string;
  steps: SequenceStep[];
  sendingSchedule?: {
    timezone: string;
    days: string[];
    startHour: number;
    endHour: number;
  };
}

export async function createSequence(params: CreateSequenceParams) {
  const sequence = await apollo.request({
    method: 'POST',
    url: '/emailer_campaigns',
    data: {
      name: params.name,
      permissions: 'team',
      active: false, // Start paused
      emailer_schedule: params.sendingSchedule ? {
        timezone: params.sendingSchedule.timezone,
        days_of_week: params.sendingSchedule.days,
        start_hour: params.sendingSchedule.startHour,
        end_hour: params.sendingSchedule.endHour,
      } : undefined,
    },
  });

  // Add steps to sequence
  for (const step of params.steps) {
    await addSequenceStep(sequence.emailer_campaign.id, step);
  }

  return sequence.emailer_campaign;
}

async function addSequenceStep(sequenceId: string, step: SequenceStep) {
  return apollo.request({
    method: 'POST',
    url: `/emailer_campaigns/${sequenceId}/emailer_steps`,
    data: {
      emailer_step: {
        type: step.type,
        wait_time: step.waitDays * 24 * 60, // Convert to minutes
        email_template: step.subject ? {
          subject: step.subject,
          body_html: step.body,
        } : undefined,
      },
    },
  });
}

3. Add Contacts to Sequence

// src/services/apollo/sequence-contacts.ts
interface AddToSequenceParams {
  sequenceId: string;
  contactIds: string[];
  sendEmailFromId?: string;
}

export async function addContactsToSequence(params: AddToSequenceParams) {
  const results = await Promise.allSettled(
    params.contactIds.map(async (contactId) => {
      return apollo.request({
        method: 'POST',
        url: '/emailer_campaigns/add_contact_ids',
        data: {
          emailer_campaign_id: params.sequenceId,
          contact_ids: [contactId],
          send_email_from_user_id: params.sendEmailFromId,
        },
      });
    })
  );

  const succeeded = results.filter(r => r.status === 'fulfilled').length;
  const failed = results.filter(r => r.status === 'rejected').length;

  return {
    added: succeeded,
    failed,
    total: params.contactIds.length,
  };
}

export async function removeContactFromSequence(
  sequenceId: string,
  contactId: string
) {
  return apollo.request({
    method: 'POST',
    url: '/emailer_campaigns/remove_contact_ids',
    data: {
      emailer_campaign_id: sequenceId,
      contact_ids: [contactId],
    },
  });
}

4. Sequence Analytics

// src/services/apollo/sequence-analytics.ts
export async function getSequenceAnalytics(sequenceId: string) {
  const response = await apollo.request({
    method: 'GET',
    url: `/emailer_campaigns/${sequenceId}`,
  });

  const campaign = response.emailer_campaign;

  return {
    id: campaign.id,
    name: campaign.name,
    metrics: {
      totalContacts: campaign.contact_count,
      activeContacts: campaign.active_contact_count,
      completedContacts: campaign.finished_contact_count,
    },
    emailMetrics: {
      sent: campaign.emails_sent_count,
      delivered: campaign.emails_sent_count - campaign.emails_bounced_count,
      opened: campaign.emails_opened_count,
      clicked: campaign.emails_clicked_count,
      replied: campaign.emails_replied_count,
      bounced: campaign.emails_bounced_count,
    },
    rates: {
      deliveryRate: calculateRate(
        campaign.emails_sent_count - campaign.emails_bounced_count,
        campaign.emails_sent_count
      ),
      openRate: calculateRate(
        campaign.emails_opened_count,
        campaign.emails_sent_count
      ),
      clickRate: calculateRate(
        campaign.emails_clicked_count,
        campaign.emails_opened_count
      ),
      replyRate: calculateRate(
        campaign.emails_replied_count,
        campaign.emails_sent_count
      ),
    },
  };
}

function calculateRate(numerator: number, denominator: number): number {
  if (denominator === 0) return 0;
  return Math.round((numerator / denominator) * 100 * 10) / 10;
}

5. Complete Outreach Pipeline

// src/services/apollo/outreach-pipeline.ts
import { searchPeople } from './people-search';
import { createSequence } from './create-sequence';
import { addContactsToSequence } from './sequence-contacts';

interface OutreachCampaign {
  name: string;
  targetCriteria: {
    domains: string[];
    titles: string[];
  };
  emailSteps: Array<{
    subject: string;
    body: string;
    waitDays: number;
  }>;
}

export async function launchOutreachCampaign(campaign: OutreachCampaign) {
  // Step 1: Find matching leads
  const leads = await searchPeople({
    domains: campaign.targetCriteria.domains,
    titles: campaign.targetCriteria.titles,
    perPage: 100,
  });

  console.log(`Found ${leads.contacts.length} matching contacts`);

  // Step 2: Create sequence
  const sequence = await createSequence({
    name: campaign.name,
    steps: campaign.emailSteps.map((step, index) => ({
      type: 'auto_email' as const,
      subject: step.subject,
      body: step.body,
      waitDays: index === 0 ? 0 : step.waitDays,
    })),
    sendingSchedule: {
      timezone: 'America/New_York',
      days: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
      startHour: 9,
      endHour: 17,
    },
  });

  console.log(`Created sequence: ${sequence.id}`);

  // Step 3: Add contacts to sequence
  const contactIds = leads.contacts.map(c => c.id);
  const result = await addContactsToSequence({
    sequenceId: sequence.id,
    contactIds,
  });

  console.log(`Added ${result.added} contacts to sequence`);

  return {
    sequenceId: sequence.id,
    contactsAdded: result.added,
    contactsFailed: result.failed,
  };
}

Usage Example

// Launch a cold outreach campaign
const result = await launchOutreachCampaign({
  name: 'Q1 2025 Engineering Leaders Outreach',
  targetCriteria: {
    domains: ['stripe.com', 'plaid.com', 'square.com'],
    titles: ['VP Engineering', 'Director of Engineering'],
  },
  emailSteps: [
    {
      subject: 'Quick question about {{company}}',
      body: 'Hi {{first_name}}, ...',
      waitDays: 0,
    },
    {
      subject: 'Following up',
      body: 'Hi {{first_name}}, wanted to follow up...',
      waitDays: 3,
    },
    {
      subject: 'Last attempt',
      body: 'Hi {{first_name}}, one last note...',
      waitDays: 5,
    },
  ],
});

Output

  • List of available sequences with stats
  • New sequence creation with steps
  • Contacts added to sequences
  • Campaign analytics and metrics

Error Handling

| Error | Cause | Solution | |-------|-------|----------| | Email Not Connected | No sending account | Connect email in Apollo UI | | Contact Already in Sequence | Duplicate enrollment | Check before adding | | Invalid Email Template | Missing variables | Validate template syntax | | Sequence Limit Reached | Plan limits | Upgrade plan or archive sequences |

Resources

Next Steps

Proceed to apollo-common-errors for error handling patterns.