Skill: zero2ai-hub/skill-cold-email-outreach (version 1.0.0)

# skill-cold-email-outreach Apollo → Hunter → Instantly automated cold email pipeline. Scrape leads, verify emails, upload to campaign — fully automated B2B outreach in one command. ## What it does 1. **Source leads** — Apollo CSV export (free tier) or Apollo API scrape (paid) 2. **Verify emails** — Hunter.io filters invalid/risky addresses 3. **Upload & personalize** — pushes verified leads to Instantly v2 with dynamic first lines 4. **Sequence** — 3-email drip: D0 (ROI pitch) → D3 (pain question) → D8 (soft close) ## Requirements - Node.js 18+ - Apollo.io account (free = CSV export, paid = API) - Hunter.io API key (free = 25/mo, paid for scale) - Instantly.ai account + campaign pre-created - Instantly v2 Bearer token ## Setup ```bash # 1. Edit config.js — add your keys cp scripts/config.example.js scripts/config.js # Fill in: instantly.apiKey, hunter.apiKey, apollo.apiKey, target ICP # 2. Run node scripts/import-csv.js your-apollo-export.csv ``` ## Scripts | File | Purpose | |------|---------| | `import-csv.js` | Apollo CSV → Hunter verify → Instantly upload | | `pipeline.js` | Apollo API scrape → Hunter verify → Instantly upload | | `config.example.js` | Config template (copy to config.js) | | `emails.js` | 3-email sequence content (customize subject/body) | ## Target Config ```js target: { industries: ["ecommerce", "retail"], countries: ["AE", "SA", "EG"], // ISO-2 codes titles: ["Founder", "CEO", "Owner"], perPage: 25, maxLeads: 200, } ``` ## Output ``` 📋 Parsed 18 rows from CSV 🔍 Verifying emails with Hunter... ✓ [REDACTED_SECRET] — John @ Acme Store ✗ [REDACTED_SECRET] — invalid ✅ Verified: 17 / 18 leads 📤 Uploading to Instantly... ╔══════════════════════════════════╗ ║ IMPORT COMPLETE ⚡ ║ ║ Uploaded: 17 ║ ║ Failed: 0 ║ ╚══════════════════════════════════╝ ``` ## After running 1. Open app.instantly.ai → your campaign 2. Connect sending inboxes (warm them first) 3. Set daily limit: 40/inbox 4. Hit Launch 🚀 ## Tips - Warm inboxes 2–3 weeks before launching cold campaigns - Apollo free: 50 contacts/month via manual CSV export - Keep sequences short (3 emails max for cold) - Personalize first line by industry/location for higher reply rates

path: README.md
content: # skill-cold-email-outreach
Apollo → Hunter → Instantly automated cold email pipeline for B2B outreach


path: scripts/config.example.js
content: // Copy this to config.js and fill in your keys
module.exports = {
  instantly: {
    [REDACTED_SECRET]",
  },
  hunter: {
    [REDACTED_SECRET]",
  },
  apollo: {
    [REDACTED_SECRET]",  // Only needed for pipeline.js (API scrape mode)
  },
  target: {
    industries: ["ecommerce", "retail", "online retail"],
    countries: ["AE", "SA", "EG", "KW", "QA", "BH", "OM"],  // ISO-2 codes
    titles: [
      "Founder", "Co-Founder", "CEO", "Owner", "Director",
      "Head of E-commerce", "E-commerce Manager", "Operations Manager"
    ],
    perPage: 25,
    maxLeads: 200,
  },
  sequence: {
    name: "Your Campaign Name",
    campaignId: "YOUR_INSTANTLY_CAMPAIGN_ID",
    dailyLimit: 40,  // per inbox
    delayBetweenEmails: { min: 5, max: 15 },  // minutes
  }
};


path: scripts/emails.js
content: // Cold email sequence — customize for your ICP
// Lead variables available: {{firstName}}, {{companyName}}

module.exports = {

  email1: {
    subject: "your store is leaving hours on the table every week",
    body: (lead) => `Hi {{firstName}},

I looked at {{companyName}} — you're running a real store, which means someone on your team is probably spending 3-5 hours a day on things that should be automatic: order routing, supplier submissions, tracking updates, stock alerts.

We fix that. [Your Company] builds e-commerce automation systems that take those exact workflows off your plate in under 30 days.

Worth a 30-min call to see if your store has the same wins available?

→ [YOUR BOOKING LINK]

— [Your Name]
[Your Company]
`,
  },

  email2: {
    subject: "Re: your store is leaving hours on the table every week",
    delayDays: 3,
    body: (lead) => `Hi {{firstName}},

Just following up — wanted to make sure this didn't get buried.

Quick question: what's the most manual, repetitive task your team deals with right now? Order processing? Inventory? Supplier coordination?

Happy to share how we've handled it in a 30-min call.

→ [YOUR BOOKING LINK]

— [Your Name]
`,
  },

  email3: {
    subject: "last one from me",
    delayDays: 5,
    body: (lead) => `Hi {{firstName}},

Won't keep following up after this — I know your inbox is busy.

But if {{companyName}} ever hits a point where the manual ops are slowing growth, we're the team to call.

The free audit link stays open: [YOUR BOOKING LINK]

Good luck.

— [Your Name]
`,
  },

};


path: scripts/import-csv.js
content: #!/usr/bin/env node
/**
 * Import Apollo CSV export → Hunter verify → Instantly campaign
 * Usage: node import-csv.js leads.csv
 */

const https = require("https");
const fs = require("fs");
const path = require("path");
const config = require("./config");

const CAMPAIGN_ID = config.sequence.campaignId; // already created

function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }

function request(options, body) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let data = "";
      res.on("data", c => data += c);
      res.on("end", () => {
        try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
        catch { resolve({ status: res.statusCode, body: data }); }
      });
    });
    req.on("error", reject);
    if (body) req.write(typeof body === "string" ? body : JSON.stringify(body));
    req.end();
  });
}

// Parse Apollo CSV (handles quoted fields)
function parseCSV(content) {
  const lines = content.trim().split("\n");
  const headers = lines[0].split(",").map(h => h.replace(/"/g, "").trim().toLowerCase());
  return lines.slice(1).map(line => {
    const values = [];
    let current = "", inQuotes = false;
    for (const char of line) {
      if (char === '"') { inQuotes = !inQuotes; continue; }
      if (char === "," && !inQuotes) { values.push(current.trim()); current = ""; continue; }
      current += char;
    }
    values.push(current.trim());
    const obj = {};
    headers.forEach((h, i) => obj[h] = values[i] || "");
    return obj;
  }).filter(r => r["email"] || r["work email"] || r["email address"]);
}

function getEmail(row) {
  return row["email"] || row["work email"] || row["email address"] || row["corporate email"] || "";
}

function getField(row, ...keys) {
  for (const k of keys) if (row[k]) return row[k];
  return "";
}

async function verifyEmail(email) {
  if (!email || !email.includes("@")) return false;
  const res = await request({
    hostname: "api.hunter.io",
    path: `/v2/email-verifier?email=${encodeURIComponent(email)}&[REDACTED_SECRET]`,
    method: "GET",
  });
  if (res.status !== 200) return true;
  const status = res.body?.data?.status;
  return ["valid", "accept_all"].includes(status);
}

function personalizeFirstLine(row) {
  const company = getField(row, "company", "company name", "organization name", "account name") || "your store";
  const country = getField(row, "country", "location") || "UAE";
  const lines = [
    `Noticed ${company} is scaling in ${country} — curious if the ops side is keeping up with growth.`,
    `${company} caught my eye — you're building something real in the GCC e-commerce space.`,
    `Saw ${company} in the ${country} market — stores at your stage usually have processes ready to automate.`,
    `Running ${company} in the GCC — you're probably the one feeling the manual ops pain most.`,
  ];
  return lines[Math.floor(Math.random() * lines.length)];
}

async function uploadLead(lead) {
  const body = JSON.stringify({
    email: lead.email,
    first_name: lead.firstName,
    last_name: lead.lastName,
    company_name: lead.company,
    personalization: lead.firstLine,
    campaign_id: CAMPAIGN_ID,
  });
  return request(
    {
      hostname: "api.instantly.ai",
      path: `/api/v2/leads`,
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${config.instantly.apiKey}`,
        "Content-Length": Buffer.byteLength(body),
      },
    },
    body
  );
}

async function run() {
  const csvPath = process.argv[2];
  if (!csvPath) {
    console.error("Usage: node import-csv.js <path-to-apollo-export.csv>");
    process.exit(1);
  }

  if (!fs.existsSync(csvPath)) {
    console.error(`File not found: ${csvPath}`);
    process.exit(1);
  }

  console.log(`⚡ Importing ${path.basename(csvPath)} into Instantly...\n`);

  const content = fs.readFileSync(csvPath, "utf8");
  const rows = parseCSV(content);
  console.log(`📋 Parsed ${rows.length} rows from CSV`);

  if (rows.length > 0) {
    console.log(`   Columns detected: ${Object.keys(rows[0]).slice(0, 8).join(", ")}...\n`);
  }

  const verifiedLeads = [];
  console.log("🔍 Verifying emails with Hunter...");

  for (const row of rows) {
    const email = getEmail(row);
    if (!email) continue;

    const valid = await verifyEmail(email);
    if (!valid) {
      console.log(`  ✗ ${email} — invalid`);
      continue;
    }

    const firstName = getField(row, "first name", "firstname", "first_name") || "there";
    const lastName = getField(row, "last name", "lastname", "last_name") || "";
    const company = getField(row, "company", "company name", "organization name", "account name") || "";

    verifiedLeads.push({ email, firstName, lastName, company, firstLine: personalizeFirstLine(row) });
    console.log(`  ✓ ${email} — ${firstName} @ ${company}`);
    await sleep(250);
  }

  console.log(`\n✅ Verified: ${verifiedLeads.length} / ${rows.length} leads\n`);

  if (!verifiedLeads.length) {
    console.log("No valid leads to upload.");
    return;
  }

  console.log(`📤 Uploading leads to Instantly campaign...`);
  let uploaded = 0;
  let failed = 0;

  for (const lead of verifiedLeads) {
    try {
      const res = await uploadLead(lead);
      if (res.status === 200 || res.status === 201) {
        uploaded++;
        console.log(`  ✓ ${lead.email}`);
      } else {
        failed++;
        console.log(`  ✗ ${lead.email} — ${res.status} ${JSON.stringify(res.body).slice(0, 80)}`);
      }
    } catch (e) {
      failed++;
      console.log(`  ✗ ${lead.email} — ${e.message}`);
    }
    await sleep(300);
  }

  console.log(`\n╔══════════════════════════════════╗`);
  console.log(`║  IMPORT COMPLETE ⚡               ║`);
  console.log(`║  Uploaded: ${String(uploaded).padEnd(23)}║`);
  console.log(`║  Failed:   ${String(failed).padEnd(23)}║`);
  console.log(`║  Campaign: Zeerotoai Outreach    ║`);
  console.log(`║                                  ║`);
  console.log(`║  NEXT: Connect inboxes in        ║`);
  console.log(`║  Instantly → hit Launch 🚀       ║`);
  console.log(`╚══════════════════════════════════╝`);
}

run().catch(console.error);


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)