Skip to content
instantly-core-workflow-a logo

Instantly Core Workflow A

instantly-core-workflow-a

Build and launch an Instantly.ai cold email campaign end-to-end.

SKILL.md

Full skill instructions

Instantly Core Workflow A: Campaign Launch Pipeline

Overview

Build the core Instantly outreach pipeline: create a campaign with email sequences, add leads with personalization, assign sending accounts, and launch. This is the primary money-path workflow for cold email outreach via Instantly API v2.

Prerequisites

  • Completed instantly-install-auth setup
  • At least one warmed-up email account in Instantly
  • Lead data (CSV or programmatic) with email + first name at minimum
  • API key with campaigns:all and leads:all scopes

Instructions

Step 1: Create a Campaign with Sequences

import { instantly } from "./​src/​instantly";

interface CreateCampaignPayload {
  name: string;
  campaign_schedule: {
    start_date: string;
    end_date?: string;
    schedules: Array<{
      name: string;
      timing: { from: string; to: string };
      days: Record<string, boolean>;
      timezone: string;
    }>;
  };
  sequences: Array<{
    steps: Array<{
      type: "email";
      delay: number;
      delay_unit?: "minutes" | "hours" | "days";
      variants: Array<{ subject: string; body: string }>;
    }>;
  }>;
  daily_limit?: number;
  stop_on_reply?: boolean;
  stop_on_auto_reply?: boolean;
  email_gap?: number;
  link_tracking?: boolean;
  open_tracking?: boolean;
}

async function createCampaign() {
  const payload: CreateCampaignPayload = {
    name: "Q1 Outbound — Decision Makers",
    campaign_schedule: {
      start_date: "2026-04-01",
      schedules: [
        {
          name: "Business Hours",
          timing: { from: "09:00", to: "17:00" },
          days: { "1": true, "2": true, "3": true, "4": true, "5": true, "0": false, "6": false },
          timezone: "America/​New_York",
        },
      ],
    },
    // sequences array takes ONE element — add steps inside it
    sequences: [
      {
        steps: [
          {
            type: "email",
            delay: 0, // first email — no delay
            variants: [
              {
                subject: "{{firstName}}, quick question about {{companyName}}",
                body: `Hi {{firstName}},\n\nI noticed {{companyName}} is scaling its outbound — we help teams like yours book 3x more meetings without adding headcount.\n\nWorth a 15-min call this week?\n\nBest,\n{{senderName}}`,
              },
              {
                subject: "Idea for {{companyName}}",
                body: `Hey {{firstName}},\n\nSaw that {{companyName}} is growing fast. We helped [similar company] increase reply rates by 40%.\n\nOpen to a quick chat?\n\n{{senderName}}`,
              },
            ],
          },
          {
            type: "email",
            delay: 3,
            delay_unit: "days",
            variants: [
              {
                subject: "Re: {{firstName}}, quick question about {{companyName}}",
                body: `Hi {{firstName}},\n\nJust following up on my last note. Would love to share how we helped [company] with a similar challenge.\n\nHappy to work around your schedule.\n\n{{senderName}}`,
              },
            ],
          },
          {
            type: "email",
            delay: 4,
            delay_unit: "days",
            variants: [
              {
                subject: "Re: {{firstName}}, quick question about {{companyName}}",
                body: `Hi {{firstName}},\n\nI know you're busy — just wanted to check if improving outbound results is a priority right now.\n\nIf not, no worries at all. If so, I'd love 15 minutes.\n\nBest,\n{{senderName}}`,
              },
            ],
          },
        ],
      },
    ],
    daily_limit: 50,
    stop_on_reply: true,
    stop_on_auto_reply: false,
    email_gap: 120,        // seconds between emails
    link_tracking: false,  // disable for better deliverability
    open_tracking: true,
  };

  const campaign = await instantly<{ id: string; name: string; status: number }>(
    "/​campaigns",
    { method: "POST", body: JSON.stringify(payload) }
  );

  console.log(`Campaign created: ${campaign.name} (${campaign.id})`);
  return campaign;
}

Step 2: Add Leads to the Campaign

interface Lead {
  email: string;
  first_name?: string;
  last_name?: string;
  company_name?: string;
  website?: string;
  phone?: string;
  personalization?: string;
  custom_variables?: Record<string, string>;
}

async function addLeads(campaignId: string, leads: Lead[]) {
  // POST /​api/​v2/​leads — one at a time
  // For bulk: POST /​api/​v2/​leads with list_id or loop
  const results = [];

  for (const lead of leads) {
    const created = await instantly("/​leads", {
      method: "POST",
      body: JSON.stringify({
        campaign: campaignId,
        email: lead.email,
        first_name: lead.first_name,
        last_name: lead.last_name,
        company_name: lead.company_name,
        website: lead.website,
        personalization: lead.personalization,
        custom_variables: lead.custom_variables,
        skip_if_in_workspace: true,  // avoid duplicates
        verify_leads_on_import: true,
      }),
    });
    results.push(created);
  }

  console.log(`Added ${results.length} leads to campaign ${campaignId}`);
  return results;
}

// Example lead data
const sampleLeads: Lead[] = [
  {
    email: "[email protected]",
    first_name: "Jane",
    last_name: "Smith",
    company_name: "Acme Corp",
    custom_variables: { companyName: "Acme Corp", senderName: "Alex" },
  },
  {
    email: "[email protected]",
    first_name: "Bob",
    last_name: "Johnson",
    company_name: "TechStart",
    custom_variables: { companyName: "TechStart", senderName: "Alex" },
  },
];

Step 3: Map Sending Accounts to Campaign

async function assignAccounts(campaignId: string) {
  // Get available warmed-up accounts
  const accounts = await instantly<{ email: string; warmup_status: string }[]>(
    "/​accounts?limit=50"
  );

  const warmedUp = accounts.filter((a) => a.warmup_status === "active");
  console.log(`Found ${warmedUp.length} warmed-up accounts`);

  // Check current account-campaign mappings
  for (const account of warmedUp.slice(0, 3)) {
    const mappings = await instantly(
      `/​account-campaign-mappings/​${encodeURIComponent(account.email)}?limit=10`
    );
    console.log(`${account.email} mapped to ${Array.isArray(mappings) ? mappings.length : 0} campaigns`);
  }

  // Accounts are assigned to campaigns in the Instantly dashboard or
  // via the PATCH campaign endpoint with email_list
  await instantly(`/​campaigns/​${campaignId}`, {
    method: "PATCH",
    body: JSON.stringify({
      email_list: warmedUp.slice(0, 3).map((a) => a.email),
    }),
  });

  console.log(`Assigned ${Math.min(3, warmedUp.length)} accounts to campaign`);
}

Step 4: Launch the Campaign

async function launchCampaign(campaignId: string) {
  // Activate (start) the campaign
  await instantly(`/​campaigns/​${campaignId}/​activate`, { method: "POST" });
  console.log(`Campaign ${campaignId} is now ACTIVE`);

  // Verify sending status
  const status = await instantly<{ sending: boolean; reason?: string }>(
    `/​campaigns/​${campaignId}/​sending-status`
  );
  console.log(`Sending status:`, status);
}

// Full pipeline
async function main() {
  const campaign = await createCampaign();
  await addLeads(campaign.id, sampleLeads);
  await assignAccounts(campaign.id);
  await launchCampaign(campaign.id);
  console.log("\nCampaign launched successfully!");
}

main().catch(console.error);

Key API Endpoints Used

MethodPathPurpose
POST/​campaignsCreate campaign with sequences
PATCH/​campaigns/​{id}Update campaign settings
POST/​campaigns/​{id}/​activateStart sending
POST/​campaigns/​{id}/​pauseStop sending
GET/​campaigns/​{id}/​sending-statusCheck if actively sending
POST/​leadsAdd a lead to campaign
GET/​accountsList email accounts
GET/​account-campaign-mappings/​{email}Check account assignments

Error Handling

ErrorCauseSolution
400 Bad Request on createInvalid schedule or sequence formatEnsure days keys are strings "0"-"6", timing is HH:MM
Campaign stuck in DraftNo sending accounts assignedAssign via PATCH /​campaigns/​{id} with email_list
Leads not receiving emailsAccounts not warmed upEnable warmup first (see instantly-core-workflow-b)
422 on lead addDuplicate email in workspaceSet skip_if_in_workspace: true
Low open ratesPoor subject lines or spam folderDisable link tracking, test with inbox placement

Resources

Next Steps

For account warmup and analytics, see instantly-core-workflow-b.

More skills from jeremylongshore

openrouter-compliance-review logo
jeremylongshore/claude-code-plugins-plus-skills

openrouter-compliance-review

Review OpenRouter integration for regulatory compliance (SOC2, GDPR,

2.8K 0
View
maintainx-data-handling logo
jeremylongshore/claude-code-plugins-plus-skills

maintainx-data-handling

Data synchronization, ETL patterns, and data management for MaintainX.

2.8K 0
View
groq-core-workflow-a logo
jeremylongshore/claude-code-plugins-plus-skills

groq-core-workflow-a

Execute Groq primary workflow: chat completions with tool use and JSON

2.8K 0
View
validating-csrf-protection logo
jeremylongshore/claude-code-plugins-plus-skills

validating-csrf-protection

Validate CSRF protection implementations for security gaps. Use when

2.8K 0
View
api-contract logo
jeremylongshore/claude-code-plugins-plus-skills

api-contract

Configure this skill should be used when the user asks about "API contract",

2.8K 0
View
ideogram-incident-runbook logo
jeremylongshore/claude-code-plugins-plus-skills

ideogram-incident-runbook

Execute Ideogram incident response with triage, mitigation, and postmortem.

2.8K 0
View
gamma-sdk-patterns logo
jeremylongshore/claude-code-plugins-plus-skills

gamma-sdk-patterns

Reusable patterns for the Gamma REST API (no SDK exists).

2.8K 0
View
deepgram-sdk-patterns logo
jeremylongshore/claude-code-plugins-plus-skills

deepgram-sdk-patterns

Apply production-ready Deepgram SDK patterns for TypeScript and Python.

2.8K 0
View
openrouter-upgrade-migration logo
jeremylongshore/claude-code-plugins-plus-skills

openrouter-upgrade-migration

Migrate to OpenRouter from direct provider APIs or upgrade between SDK/model

2.8K 0
View
openrouter-common-errors logo
jeremylongshore/claude-code-plugins-plus-skills

openrouter-common-errors

Diagnose and fix common OpenRouter API errors. Use when encountering

2.8K 0
View
linear-cost-tuning logo
jeremylongshore/claude-code-plugins-plus-skills

linear-cost-tuning

Optimize Linear API usage, reduce unnecessary calls, and maximize

2.8K 0
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View