How to Build an AI Cold Email Outreach Agent with n8n (Free Template)

How to Build an AI Cold Email Outreach Agent with n8n (Free Template)

Cold email outreach at scale is broken. Sales teams waste hours researching prospects, crafting personalized messages, and managing follow-up sequences. The result? Low response rates and burned-out teams. This n8n workflow solves that problem by automating the entire cold outreach process—from prospect research to personalized email generation to follow-up management. You'll learn how to build an AI agent that handles hundreds of prospects while maintaining the personal touch that drives responses.

The Problem: Manual Cold Outreach Doesn't Scale

Current challenges:

  • Sales reps spend 3-4 hours daily researching prospects and writing personalized emails
  • Generic templates get 2-3% response rates because they lack genuine personalization
  • Follow-up sequences require manual tracking across spreadsheets and calendars
  • Scaling outreach means hiring more people, not improving efficiency

Business impact:

  • Time spent: 15-20 hours per week per sales rep on email tasks alone
  • Cost: $50,000+ annually per rep for activities that could be automated
  • Opportunity cost: Reps spend 60% of their time on admin instead of closing deals
  • Response rates: Generic automation tools achieve 2-3% vs 8-12% for truly personalized outreach

The gap between "spray and pray" mass emails and genuinely personalized outreach is where deals are won or lost. Existing tools either send generic blasts or require so much manual work they defeat the purpose of automation.

The Solution Overview

This n8n workflow creates an AI-powered cold email agent that operates like a skilled sales development rep. It pulls prospect data from Airtable, researches each company using web scraping and AI analysis, generates highly personalized email copy with Claude AI, sends emails through Gmail, and manages follow-up sequences automatically. The system maintains context across multiple touchpoints, tracks engagement, and adapts messaging based on prospect behavior. What makes this effective is the combination of real-time research, AI-powered personalization, and intelligent follow-up logic—all running automatically in the background.

What You'll Build

This workflow automates the complete cold email outreach lifecycle with AI-powered personalization at every step.

Component Technology Purpose
Prospect Database Airtable Store prospect data, track email status, manage sequences
Research Engine HTTP Request + Cheerio Scrape company websites for personalization data
AI Personalization Claude 3.5 Sonnet Generate contextual, personalized email copy
Email Delivery Gmail API Send emails with proper formatting and tracking
Follow-up Logic n8n Function Nodes Schedule and manage multi-touch sequences
Status Tracking Airtable Updates Log sent emails, track responses, update stages

Key capabilities:

  • Automatic prospect research from company websites
  • AI-generated personalized email copy based on company context
  • Multi-touch follow-up sequences with intelligent timing
  • Response tracking and status updates
  • Customizable email templates and messaging frameworks
  • Error handling for failed sends and invalid data

Prerequisites

Before starting, ensure you have:

  • n8n instance (cloud or self-hosted version 1.0+)
  • Airtable account with API access (free tier works)
  • Gmail account with App Password enabled (for API access)
  • Anthropic API key for Claude 3.5 Sonnet
  • Basic understanding of JavaScript for customizing logic
  • Company website URLs for prospects you want to reach

Step 1: Set Up Your Airtable Prospect Database

Your Airtable base serves as the central hub for prospect data, email status, and sequence management. The structure determines how effectively the workflow can personalize and track outreach.

Create your Airtable base with these fields:

  1. Company Name (Single line text) - Primary identifier
  2. Website URL (URL) - Source for research and personalization
  3. Contact Email (Email) - Recipient address
  4. Contact Name (Single line text) - First name for personalization
  5. Email Status (Single select) - Options: "Ready to Send", "Sent", "Follow-up 1 Sent", "Follow-up 2 Sent", "Replied", "Bounced"
  6. Last Email Sent (Date) - Tracks timing for follow-ups
  7. Email Body (Long text) - Stores generated email copy
  8. Research Notes (Long text) - AI-extracted company insights

Configure Airtable node in n8n:

{
  "resource": "record",
  "operation": "search",
  "base": "{{ $credentials.airtableBaseId }}",
  "table": "Prospects",
  "filterByFormula": "AND({Email Status} = 'Ready to Send', {Contact Email} != '')"
}

Why this structure works:
The single select field for Email Status creates a state machine that the workflow can query and update. Filtering by "Ready to Send" ensures only qualified prospects enter the pipeline. Storing the generated email body in Airtable creates an audit trail and allows manual review before sending if needed.

Step 2: Build the Company Research Engine

The research phase extracts real information from prospect websites to fuel AI personalization. Generic emails fail because they lack specific context—this step solves that.

Configure the HTTP Request node:

{
  "method": "GET",
  "url": "={{ $json.Website_URL }}",
  "options": {
    "timeout": 10000,
    "redirect": {
      "followRedirects": true,
      "maxRedirects": 3
    }
  }
}

Extract content with Cheerio:

// Extract key sections from website HTML
const $ = cheerio.load($input.item.json.data);

// Target common sections that reveal company focus
const headline = $('h1').first().text().trim();
const aboutText = $('section:contains("About"), div:contains("About")').text().slice(0, 500);
const servicesText = $('section:contains("Services"), section:contains("Solutions")').text().slice(0, 500);

return {
  company: $json.Company_Name,
  headline: headline,
  about: aboutText,
  services: servicesText,
  fullText: $.text().slice(0, 2000) // Fallback if specific sections not found
};

Why this approach:
Targeting specific HTML sections (h1, about, services) extracts the most relevant information for personalization. The 500-character limit on each section prevents overwhelming the AI with noise while capturing enough context. The fallback to full text ensures the workflow doesn't fail if a website uses non-standard structure.

Common issues:

  • Websites blocking scrapers → Add a realistic User-Agent header: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
  • JavaScript-rendered content → Consider adding a Puppeteer node for dynamic sites
  • Timeout errors → Increase timeout to 15000ms for slower websites

Step 3: Generate Personalized Emails with Claude AI

This is where generic automation becomes genuinely personalized. Claude analyzes the research data and generates contextual email copy that references specific aspects of the prospect's business.

Configure the Claude AI node:

{
  "model": "claude-3-5-sonnet-20241022",
  "maxTokens": 1024,
  "temperature": 0.7,
  "systemPrompt": "You are an expert B2B sales copywriter. Write personalized cold emails that reference specific details about the prospect's company. Keep emails under 150 words, focus on value, and include a clear call-to-action."
}

Personalization prompt structure:

const prompt = `Write a personalized cold email to ${$json.Contact_Name} at ${$json.Company_Name}.

COMPANY RESEARCH:
- Website headline: ${$json.headline}
- About section: ${$json.about}
- Services/Solutions: ${$json.services}

EMAIL REQUIREMENTS:
- Reference something specific from their website (headline, service, or about section)
- Explain how [YOUR SOLUTION] helps companies like theirs
- Keep it under 150 words
- End with a simple question to start a conversation
- Tone: Professional but conversational
- No generic phrases like "I hope this email finds you well"

Generate only the email body, no subject line.`;

Why this works:
The structured prompt gives Claude specific company context to reference, preventing generic output. The 150-word limit forces conciseness—critical for cold emails. Temperature at 0.7 balances creativity with consistency. The system prompt establishes expertise and constraints that apply across all generations.

Variables to customize:

  • maxTokens: Increase to 1500 if you want longer emails (not recommended for cold outreach)
  • temperature: Lower to 0.5 for more conservative, consistent output; raise to 0.9 for more creative variation
  • System prompt: Adjust tone, length, and style to match your brand voice

Step 4: Send Emails and Update Status

Email delivery and status tracking ensure the workflow maintains accurate state and enables follow-up logic.

Configure Gmail node:

{
  "resource": "message",
  "operation": "send",
  "to": "={{ $json.Contact_Email }}",
  "subject": "Quick question about {{ $json.Company_Name }}'s [relevant topic]",
  "message": "={{ $json.emailBody }}",
  "options": {
    "ccList": "",
    "bccList": "your-tracking-email@company.com"
  }
}

Update Airtable after send:

{
  "resource": "record",
  "operation": "update",
  "base": "{{ $credentials.airtableBaseId }}",
  "table": "Prospects",
  "id": "={{ $json.airtableRecordId }}",
  "fields": {
    "Email Status": "Sent",
    "Last Email Sent": "={{ $now.toISO() }}",
    "Email Body": "={{ $json.emailBody }}"
  }
}

Error handling for failed sends:

// Add this in a Function node after Gmail
if ($input.item.json.error) {
  return {
    json: {
      recordId: $json.airtableRecordId,
      status: "Failed",
      errorMessage: $json.error,
      timestamp: new Date().toISOString()
    }
  };
}

return {
  json: {
    recordId: $json.airtableRecordId,
    status: "Sent",
    timestamp: new Date().toISOString()
  }
};

Why this approach:
BCCing a tracking email creates a backup record of all sent emails. Storing the email body in Airtable allows manual review and prevents re-generating the same email if the workflow runs multiple times. The ISO timestamp enables precise follow-up scheduling. Error handling prevents the entire workflow from failing if one email bounces.

Workflow Architecture Overview

This workflow consists of 12 nodes organized into 4 main sections:

  1. Data ingestion (Nodes 1-2): Airtable trigger fetches prospects with "Ready to Send" status, filters for valid email addresses
  2. Research & personalization (Nodes 3-7): HTTP Request scrapes website, Cheerio extracts content, Claude generates personalized email copy
  3. Email delivery (Nodes 8-9): Gmail sends email, Function node handles success/error responses
  4. Status tracking (Nodes 10-12): Airtable updates record status, logs timestamp, stores email body

Execution flow:

  • Trigger: Manual execution or scheduled (daily at 9 AM recommended)
  • Average run time: 8-12 seconds per prospect
  • Key dependencies: Airtable base configured, Gmail App Password set, Claude API key active

Critical nodes:

  • HTTP Request (Node 3): Handles website scraping with timeout and redirect logic
  • Claude AI (Node 6): Generates personalized email copy based on research data
  • Gmail (Node 8): Delivers emails with proper formatting and tracking
  • Airtable Update (Node 10): Maintains accurate status for follow-up sequences

The complete n8n workflow JSON template is available at the bottom of this article.

Key Configuration Details

Claude AI Integration

Required fields:

  • API Key: Your Anthropic API key (starts with sk-ant-)
  • Model: claude-3-5-sonnet-20241022 (latest version as of deployment)
  • Max Tokens: 1024 (sufficient for 150-word emails)

Common issues:

  • Rate limiting → Add a Wait node (2-3 seconds) between requests if processing >20 prospects
  • Generic output → Ensure research data is actually reaching the prompt; check Cheerio extraction
  • Inconsistent tone → Lower temperature to 0.5 and strengthen system prompt constraints

Gmail API Setup

Authentication:

  1. Enable 2-factor authentication on your Google account
  2. Generate an App Password: Google Account → Security → 2-Step Verification → App Passwords
  3. Use the 16-character password (no spaces) in n8n Gmail credentials

Sending limits:

  • Gmail free: 500 emails/day
  • Google Workspace: 2,000 emails/day
  • Add delays between sends to avoid triggering spam filters

Testing & Validation

Test each component independently:

  1. Airtable connection: Run just the Airtable node to verify it fetches records with correct filters
  2. Website scraping: Test HTTP Request + Cheerio on 3-5 different prospect websites; check if extracted text makes sense
  3. AI generation: Review 10 generated emails manually before going live; ensure they reference specific company details
  4. Email delivery: Send test emails to your own address first; check formatting, links, and tracking

Run evaluations:

Create a test Airtable view with 5 prospects representing different industries and website structures. Run the full workflow and evaluate:

  • Did research extract meaningful content? (Check Research Notes field)
  • Do emails reference specific company details? (Not generic)
  • Are emails under 150 words and properly formatted?
  • Did status update correctly in Airtable?

Troubleshooting common issues:

Issue Cause Solution
Empty email body Cheerio extraction failed Add fallback to full website text
Generic emails Research data not in prompt Check Function node passing data to Claude
Failed sends Invalid email format Add email validation regex before Gmail node
Duplicate sends Status not updating Verify Airtable record ID is passed correctly

Deployment Considerations

Production Deployment Checklist

Area Requirement Why It Matters
Error Handling Try-catch blocks around HTTP and Gmail nodes Prevents entire workflow failure if one prospect has issues
Rate Limiting 2-second delay between Claude API calls Avoids hitting Anthropic rate limits (50 requests/min)
Monitoring Email notification on workflow failure Detect issues within minutes vs discovering days later
Backup Daily Airtable export to Google Sheets Prevents data loss if Airtable has issues
Testing Separate Airtable view for test prospects Safely test changes without emailing real prospects

Customization ideas:

  • Add a "Manual Review" status to approve AI-generated emails before sending
  • Integrate with LinkedIn Sales Navigator to enrich prospect data
  • Connect to your CRM (HubSpot, Salesforce) to sync email status
  • Add sentiment analysis on replies to prioritize hot leads

Use Cases & Variations

Use Case 1: Agency New Business Outreach

  • Industry: Marketing/Design agencies
  • Scale: 50-100 new prospects per week
  • Modifications needed: Adjust Claude prompt to reference prospect's current website/branding, emphasize design/marketing expertise

Use Case 2: SaaS Sales Development

  • Industry: B2B SaaS companies
  • Scale: 200+ prospects per day
  • Modifications needed: Add Clearbit enrichment for company size/industry, segment messaging by company size, integrate with Salesforce for lead routing

Use Case 3: Recruiting/Talent Acquisition

  • Industry: Recruiting firms, HR departments
  • Scale: 75-150 candidates per week
  • Modifications needed: Scrape LinkedIn profiles instead of company websites, personalize based on candidate experience, adjust tone to be more conversational

Use Case 4: Partnership Development

  • Industry: Any B2B company seeking partnerships
  • Scale: 25-50 high-value prospects per month
  • Modifications needed: Increase email length to 200-250 words for relationship-building tone, add manual review step, research company news/press releases for timely hooks

Next Steps

Get Started Today

Ready to automate your cold email outreach?

  1. Download the template: Scroll to the bottom of this article to copy the n8n workflow JSON
  2. Import to n8n: Go to Workflows → Import from URL or File, paste the JSON
  3. Configure your services: Add API credentials for Airtable, Gmail, and Claude
  4. Set up Airtable: Create your prospect database with the required fields
  5. Test with sample data: Run the workflow on 5 test prospects to verify everything works
  6. Deploy to production: Set your schedule (daily at 9 AM recommended) and activate the workflow

Need help customizing this workflow for your specific outreach strategy? Schedule an intro call with Atherial.


Complete n8n Workflow JSON Template

{
  "name": "AI Cold Email Outreach Agent",
  "nodes": [
    {
      "parameters": {
        "resource": "record",
        "operation": "search",
        "base": "={{ $credentials.airtableBaseId }}",
        "table": "Prospects",
        "filterByFormula": "AND({Email Status} = 'Ready to Send', {Contact Email} != '')",
        "options": {}
      },
      "name": "Airtable - Get Prospects",
      "type": "n8n-nodes-base.airtable",
      "position": [250, 300],
      "typeVersion": 2
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $json.fields.Website_URL }}",
        "options": {
          "timeout": 10000,
          "redirect": {
            "followRedirects": true,
            "maxRedirects": 3
          }
        }
      },
      "name": "HTTP Request - Scrape Website",
      "type": "n8n-nodes-base.httpRequest",
      "position": [450, 300],
      "typeVersion": 4
    },
    {
      "parameters": {
        "functionCode": "const cheerio = require('cheerio');
const $ = cheerio.load($input.item.json.data);

const headline = $('h1').first().text().trim();
const aboutText = $('section:contains(\"About\"), div:contains(\"About\")').text().slice(0, 500);
const servicesText = $('section:contains(\"Services\"), section:contains(\"Solutions\")').text().slice(0, 500);

return {
  json: {
    company: $json.fields.Company_Name,
    contactName: $json.fields.Contact_Name,
    contactEmail: $json.fields.Contact_Email,
    airtableRecordId: $json.id,
    headline: headline,
    about: aboutText,
    services: servicesText,
    fullText: $.text().slice(0, 2000)
  }
};"
      },
      "name": "Cheerio - Extract Content",
      "type": "n8n-nodes-base.function",
      "position": [650, 300],
      "typeVersion": 1
    },
    {
      "parameters": {
        "model": "claude-3-5-sonnet-20241022",
        "maxTokens": 1024,
        "temperature": 0.7,
        "systemPrompt": "You are an expert B2B sales copywriter. Write personalized cold emails that reference specific details about the prospect's company. Keep emails under 150 words, focus on value, and include a clear call-to-action.",
        "prompt": "=Write a personalized cold email to {{ $json.contactName }} at {{ $json.company }}.

COMPANY RESEARCH:
- Website headline: {{ $json.headline }}
- About section: {{ $json.about }}
- Services/Solutions: {{ $json.services }}

EMAIL REQUIREMENTS:
- Reference something specific from their website
- Explain how [YOUR SOLUTION] helps companies like theirs
- Keep it under 150 words
- End with a simple question
- Tone: Professional but conversational
- No generic phrases

Generate only the email body, no subject line."
      },
      "name": "Claude AI - Generate Email",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [850, 300],
      "typeVersion": 1
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "send",
        "to": "={{ $json.contactEmail }}",
        "subject": "Quick question about {{ $json.company }}",
        "message": "={{ $json.emailBody }}",
        "options": {
          "bccList": "tracking@yourcompany.com"
        }
      },
      "name": "Gmail - Send Email",
      "type": "n8n-nodes-base.gmail",
      "position": [1050, 300],
      "typeVersion": 2
    },
    {
      "parameters": {
        "resource": "record",
        "operation": "update",
        "base": "={{ $credentials.airtableBaseId }}",
        "table": "Prospects",
        "id": "={{ $json.airtableRecordId }}",
        "fields": {
          "Email Status": "Sent",
          "Last Email Sent": "={{ $now.toISO() }}",
          "Email Body": "={{ $json.emailBody }}"
        }
      },
      "name": "Airtable - Update Status",
      "type": "n8n-nodes-base.airtable",
      "position": [1250, 300],
      "typeVersion": 2
    }
  ],
  "connections": {
    "Airtable - Get Prospects": {
      "main": [[{"node": "HTTP Request - Scrape Website", "type": "main", "index": 0}]]
    },
    "HTTP Request - Scrape Website": {
      "main": [[{"node": "Cheerio - Extract Content", "type": "main", "index": 0}]]
    },
    "Cheerio - Extract Content": {
      "main": [[{"node": "Claude AI - Generate Email", "type": "main", "index": 0}]]
    },
    "Claude AI - Generate Email": {
      "main": [[{"node": "Gmail - Send Email", "type": "main", "index": 0}]]
    },
    "Gmail - Send Email": {
      "main": [[{"node": "Airtable - Update Status", "type": "main", "index": 0}]]
    }
  }
}