...

How to build a cold email optimizer

How to Build an Autonomous AI Cold Email Optimizer Using Claude, GitHub Actions & Instantly

Category: AI Tools · Automation · Email Marketing · Python
Skill Level: Intermediate–Advanced
Target Audience: Founders, developers, growth engineers, B2B marketers


Table of Contents

  1. What Is an Autonomous Email Optimizer?
  2. How the System Works: The 3-Phase Loop
  3. Tech Stack & Architecture
  4. The Science of Cold Email: Built-In Best Practices
  5. Project File Structure Explained
  6. Step-by-Step Setup Guide
  7. How Claude AI Generates Challenger Emails
  8. Deploying to GitHub Actions for Fully Autonomous Operation
  9. Safety Rules & Data Integrity
  10. Key Takeaways & Practical Insights

1. What Is an Autonomous Email Optimizer? {#what-is-it}

Most cold email campaigns are written once, sent, and never systematically improved. This project changes that entirely.

The Email Optimizer is an open-source Python project that runs a continuous, headless A/B testing loop on cold email copy — with no human intervention after initial setup. It is inspired by Andrej Karpathy’s “autoresearch” pattern, applying the same self-improving agent concept to outbound sales email.

Every 4 hours, the system automatically:

  • Harvests reply rate data from experiments that have run for 48 hours
  • Uses Claude AI (Anthropic’s LLM) to generate a mutated “challenger” version of the current best email
  • Deploys both the baseline and challenger as live campaigns via the Instantly.ai API
  • Commits all results back to the GitHub repository

Over time, the baseline email ratchets upward — only proven winners survive and get promoted. The result is a system that gets smarter with every cycle, autonomously discovering what copy resonates with your specific target audience.


2. How the System Works: The 3-Phase Loop {#how-it-works}

Each run of orchestrator.py executes three sequential phases:

Phase 1: HARVEST

  • Scans all currently active experiments
  • Identifies any experiment that has been running for 48 hours or longer
  • Pulls reply rate statistics from the Instantly.ai API for both baseline and challenger campaigns
  • Compares winner vs. loser: if the challenger has a higher reply rate and meets the minimum reply threshold, it is promoted to baseline
  • Saves results to an append-only JSONL log (results/results.log) and per-experiment folders under results/experiments/

Phase 2: GENERATE

  • Reads the current config/baseline.md (the best-performing email so far)
  • Reads data/resource.md (what you sell, who you target, your value proposition)
  • Optionally reads data/cold-email-course.md (custom strategy notes and frameworks)
  • Sends all of this context to Claude via the Anthropic API
  • Claude returns a fully formatted challenger config — a mutated version of the baseline with a new subject line, body, opening line, or CTA

Phase 3: DEPLOY

  • Draws 250 fresh leads per arm from the local SQLite lead pool (data/lead_pool.db)
  • Creates two new Instantly.ai campaigns: one for the baseline, one for the challenger
  • Marks all assigned leads as status = 'assigned' in the pool DB
  • Writes dedup records to data/contacted.db so no one is ever emailed twice
  • Updates data/active_experiments.json with the new experiment metadata
HARVEST → GENERATE → DEPLOY → (wait 4 hours) → HARVEST → ...

3. Tech Stack & Architecture {#tech-stack}

ComponentTool / ServiceRole
LLM / AIAnthropic Claude (claude-sonnet)Challenger email generation
Email platformInstantly.ai API v2Campaign creation & analytics
Lead sourcingApify + SQLitePre-scraped lead pool management
OrchestrationPython 3.12Core logic (orchestrator.py)
AutomationGitHub Actions (cron)Runs every 4 hours, headless
NotificationsSlack WebhooksOptional run summaries
Data persistenceSQLite (2 databases)Lead pool + dedup tracking
Config formatMarkdown (.md files)Human-readable, Claude-parseable

Python dependencies (from requirements.txt):

  • anthropic>=0.40.0 — Anthropic Python SDK
  • apify-client>=1.8.0 — Lead scraping client
  • requests>=2.32.0 — HTTP calls to Instantly API
  • python-dotenv>=1.0.0 — Secrets management via .env

4. The Science of Cold Email: Built-In Best Practices {#cold-email-science}

One of the most valuable parts of this project is its embedded cold email knowledge base (data/resource.md). These are the optimization levers it uses, ranked by impact:

Lead Quality (Highest Leverage)

  • Decision-maker titles only: CEO, Founder, Owner, Managing Director reply significantly more than VP or Director roles at large companies
  • Company size sweet spot: 5–50 employees — large enough to need your service, small enough that the founder reads their own email
  • Industry targeting: Service businesses, agencies, and consulting firms reply 2–3× more than SaaS or ecommerce companies
  • Avoid gatekeeping layers: At 500+ person companies, emails rarely reach the decision-maker

Subject Lines

  • Lowercase beats Title Case in cold email open rates
  • Keep it under 40 characters
  • Personal and direct outperforms clever: "quick question" > "Revolutionize Your Business"
  • Question format adds approximately 15% open rate lift
  • Avoid "re:" tricks — they work short-term but damage domain reputation

Body Copy Rules

  • First line must not be about you — open with something about the recipient (their company, their work, a relevant observation)
  • Under 100 words total — brevity is a feature, not a limitation
  • One CTA only — “quick call” or “worth a chat” (avoid “book a demo” language)
  • No em dashes, no corporate jargon, no exclamation marks
  • Line breaks after every 1–2 sentences for mobile readability
  • Use {{firstName}} and {{companyName}} merge tags

Deliverability

  • Text-only or minimal HTML (no images, no heavy formatting)
  • Disable link tracking and open tracking — both hurt inbox placement
  • Maximum 125 emails per day per campaign
  • Minimum 10-minute gap between sends

Experiment Control Variables (Never Mutated)

These settings are held constant across all experiments to ensure clean A/B test results:

  • Email gap: 10 minutes
  • Daily limit: 125 sends/campaign
  • Steps: 1 (single initial email only — no follow-up sequences)
  • Send schedule: 9am–5pm local time
  • Tracking: disabled

5. Project File Structure Explained {#file-structure}

email-optimizer-demo/
│
├── orchestrator.py          # Main 3-phase loop: harvest → generate → deploy
├── instantly_client.py      # Wrapper for Instantly.ai API v2
├── deploy_batch.py          # Deploy multiple experiments in one run
├── export_campaigns.py      # Archive all campaigns to JSON + CSV
├── purge_old_leads.py       # Free Instantly contact slots (export then delete)
├── test_parsers.py          # Validate config parser logic
│
├── config/
│   ├── baseline.md          # The current best email (the one being mutated)
│   └── challenger_preview.md # Auto-generated preview from --dry-run mode
│
├── data/
│   ├── resource.md          # Product description + cold email strategy (read-only)
│   ├── cold-email-course.md # Optional: paste course notes or playbooks here
│   ├── active_experiments.json  # Currently running experiment metadata
│   ├── lead_pool.db         # SQLite: pre-scraped leads (not in git — use LFS)
│   └── contacted.db         # SQLite: global dedup — nobody gets emailed twice
│
├── results/
│   ├── results.log          # Append-only JSONL: all experiment history
│   └── experiments/         # Per-experiment records: copy, configs, results
│
├── .github/workflows/
│   └── optimize.yml         # GitHub Actions cron: runs every 4 hours
│
├── .env.example             # API key template
└── requirements.txt         # Python dependencies

Two critical SQLite databases:

  • lead_pool.db — the “inbox” of available leads, drawn down per experiment with fields: email, first_name, last_name, company_name, job_title, industry, city, state, status, experiment_id
  • contacted.db — the dedup safeguard that ensures no contact is ever emailed twice, even across hundreds of experiments

6. Step-by-Step Setup Guide {#setup-guide}

Step 1: Clone and Install

git clone <your-repo-url>
cd email-optimizer-demo
pip install -r requirements.txt
cp .env.example .env

Step 2: Add API Keys to .env

INSTANTLY_API_KEY=your_instantly_api_v2_bearer_token   # Required
APIFY_API_TOKEN=your_apify_api_token                   # Required
ANTHROPIC_API_KEY=your_anthropic_api_key               # Required
WEBHOOK_URL=your_slack_webhook_url                     # Optional

Step 3: Define Your Product in data/resource.md

Fill in:

  • What you offer (2–3 sentences)
  • Who your ideal customer is (industry, company size, job titles)
  • Your core value proposition
  • Any social proof (revenue figures, client count, case studies)

Step 4: Write Your Baseline Email in config/baseline.md

The baseline config uses a structured Markdown + YAML format:

---
version: 1
last_updated: 2026-01-01
experiment_id: exp-2026-01-01
---

## Lead Filter
contact_job_title:
  - "CEO"
  - "Founder"
company_industry:
  - "marketing"
size:
  - "1-10"
  - "11-50"
fetch_count: 250

## Email Sequence

### Step 1 (Day 0)
subject: quick question
body: |
  Hey {{firstName}},

  [Opening line about THEM — not you.]

  [Your offer in under 50 words. Social proof if you have it.]

  [Specific, low-commitment CTA — e.g., "How's a 15-min call Thursday?"]

  Thanks,
  - {{sendingAccountFirstName}}

Step 5: Build the Lead Pool

Run deploy_batch.py to populate data/lead_pool.db using your Apify scraper, or create it manually using the schema:

CREATE TABLE leads (
  email TEXT,
  first_name TEXT,
  last_name TEXT,
  company_name TEXT,
  job_title TEXT,
  industry TEXT,
  city TEXT,
  state TEXT,
  status TEXT DEFAULT 'available',
  experiment_id TEXT
);

Step 6: Test with Dry Run

python orchestrator.py --dry-run

This generates a challenger preview saved to config/challenger_preview.md without deploying any campaigns. Review it to verify Claude’s output quality.

Step 7: Push to GitHub and Enable Automation

git push origin main

Add these secrets to your GitHub repository (Settings → Secrets → Actions):

  • INSTANTLY_API_KEY
  • APIFY_API_TOKEN
  • ANTHROPIC_API_KEY
  • WEBHOOK_URL

Enable GitHub Actions and the optimize.yml workflow runs automatically at 02:00, 06:00, 10:00, 14:00, 18:00, and 22:00 UTC.


7. How Claude AI Generates Challenger Emails {#claude-ai-role}

This is the intellectual core of the system. During Phase 2 (GENERATE), the orchestrator builds a rich prompt for Claude that includes:

  1. The current baseline email (config/baseline.md) — what Claude is mutating from
  2. Product and audience context (data/resource.md) — what you’re selling and to whom
  3. Cold email strategy notes (data/cold-email-course.md) — optional expert knowledge base
  4. Previous experiment history from results/results.log — what has worked and what hasn’t
  5. A structured mutation strategy defining which levers to pull and in what priority order

Mutation Levers (Ranked by Impact)

PriorityLeverWhat Changes
1Lead filterDifferent audience segment entirely
2Subject lineDifferent hook / angle
3Opening lineDifferent personalization approach
4CTACall vs. audit vs. case study
5Body lengthShorter vs. longer
6ToneCasual vs. professional

Early experiments (no history): Claude is instructed to go bold — change multiple categories at once to explore the search space quickly.

Later experiments (with history): Claude makes targeted, surgical changes based on patterns from winning experiments.

Claude returns a complete, valid baseline config in the same Markdown format — ready to deploy without any human editing.


8. Deploying to GitHub Actions for Fully Autonomous Operation {#github-actions}

The .github/workflows/optimize.yml file handles the entire automation:

name: Email Optimization

on:
  schedule:
    - cron: "0 2,6,10,14,18,22 * * *"  # Every 4 hours
  workflow_dispatch:  # Manual trigger available

jobs:
  optimize:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          lfs: true
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -r requirements.txt
      - run: python orchestrator.py
        env:
          INSTANTLY_API_KEY: ${{ secrets.INSTANTLY_API_KEY }}
          APIFY_API_TOKEN: ${{ secrets.APIFY_API_TOKEN }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }}
      - name: Commit results
        run: |
          git config user.name "email-optimizer[bot]"
          git add config/baseline.md data/active_experiments.json \
                  results/results.log results/experiments/ \
                  data/contacted.db data/lead_pool.db
          git diff --staged --quiet || git commit -m "exp $(date +%Y-%m-%d-%H): auto-optimize"
          git push

Key design decisions:

  • Results, the promoted baseline, and the dedup database are all committed back to the repo after every run — the git history is your full experiment audit trail
  • timeout-minutes: 30 prevents runaway jobs from consuming GitHub Actions minutes
  • workflow_dispatch allows manual runs for testing without waiting for the cron schedule
  • Git LFS (lfs: true) is required because lead_pool.db can grow large

9. Safety Rules & Data Integrity {#safety-rules}

The project enforces several explicit safety rules that are worth understanding — especially before making any manual changes:

RuleWhy It Matters
Never delete Instantly campaigns without explicit confirmationThe API can return stale analytics; that doesn’t mean a campaign is broken
Never overwrite active_experiments.json, results.log, or contacted.dbThese are irreplaceable experiment state — overwriting them corrupts your history and dedup protection
Never pause active campaigns without a validated reasonPausing mid-experiment invalidates the 48-hour comparison window
Always use the dedup DBcontacted.db is the system-wide guarantee that no one receives duplicate emails across all experiments

10. Key Takeaways & Practical Insights {#takeaways}

For Developers

  • The project is a clean example of an agentic loop with a stateful feedback mechanism — each run reads prior results and uses them to inform the next generation step
  • Using Markdown files as the “config format” is a deliberate choice: it keeps configs human-readable and trivially parseable by LLMs without any special schema
  • SQLite is used for both the lead pool and dedup tracking — a pragmatic choice that requires zero infrastructure beyond the repo itself
  • The system decouples lead scraping (offline, bulk) from campaign deployment (online, per-run) to eliminate a common point of failure in outbound automation

For Marketers & Founders

  • The 250 leads per arm / 125 per day / 48-hour window is a deliberately calibrated setup that balances statistical signal with experiment velocity
  • The single-step (no follow-up) constraint is intentional: follow-ups dilute measurement and make it harder to isolate which variable is driving results
  • The system embeds a full cold email best-practice framework as operating knowledge for Claude — meaning the AI challenger generator is constrained by real-world email deliverability and conversion principles, not just arbitrary text mutations
  • Slack notifications give you a passive feed of what’s winning without requiring you to log in anywhere

SEO & Content Takeaway for Technowild.com Readers

This project demonstrates a broader pattern that is increasingly relevant across digital marketing:

AI + APIs + CI/CD = self-optimizing systems that improve continuously without human bottlenecks

The same architecture — a feedback loop where an LLM reads historical performance data and generates an improved variant for autonomous deployment — can be applied to landing page copy, ad creative, SEO meta descriptions, push notification text, and more. The email optimizer is a working blueprint for that class of system.


Further Resources


This article was produced for Technowild.com based on a technical analysis of the email-optimizer-demo open-source project. All code references reflect the project files as analyzed.

Seraphinite AcceleratorOptimized by Seraphinite Accelerator
Turns on site high speed to be attractive for people and search engines.