How to Build an Automated Client Acquisition System with n8n (Free Template)

How to Build an Automated Client Acquisition System with n8n (Free Template)

Cold outreach at scale requires orchestrating multiple platforms: lead databases, email senders, AI reply handlers, CRMs, and booking tools. Managing these manually creates bottlenecks and missed opportunities. This guide shows you how to build a complete automated client acquisition system using n8n to connect Acquisity, Instantly, Zapmail, Go High Level, and Calendly. You'll learn the exact workflow architecture, node configurations, and integration logic to automate everything from lead sourcing to meeting bookings. A complete n8n workflow JSON template is included at the end.

The Problem: Manual Client Acquisition Doesn't Scale

Running outbound campaigns manually creates friction at every stage. You export leads from one platform, import them to another, manually check replies, update your CRM by hand, and chase booking confirmations across multiple tools.

Current challenges:

  • Lead lists sit in Acquisity but require manual CSV exports to email platforms
  • Reply monitoring demands constant inbox checking across multiple Gmail accounts
  • Positive replies don't automatically create CRM opportunities or trigger booking sequences
  • No automated classification of reply types (interested vs. out-of-office vs. not interested)
  • Bounced emails and invalid contacts pollute your lists without automatic cleanup
  • Meeting bookings don't sync to your pipeline, requiring duplicate data entry

Business impact:

  • Time spent: 10-15 hours/week on manual data transfers and reply management
  • Response delay: 4-8 hours between positive reply and booking link (vs. instant)
  • Lost opportunities: 20-30% of interested leads never receive booking links due to manual process delays
  • List quality degradation: Bounce rates increase 5-10% monthly without automated cleaning

The Solution Overview

This n8n workflow creates a unified automation layer connecting your entire client acquisition stack. It pulls leads from Acquisity, pushes them to Instantly campaigns, monitors all Gmail inboxes for replies, uses Zapmail's AI to classify responses, creates Go High Level opportunities for interested prospects, sends automated booking links, and maintains clean contact lists. The system runs continuously, processing replies within minutes and ensuring every positive response gets immediate follow-up. You'll configure webhook triggers, HTTP requests, conditional logic, and CRM integrations to eliminate manual handoffs between platforms.

What You'll Build

This system automates the complete journey from lead discovery to booked meeting. Here's what each component handles:

Component Technology Purpose
Lead Sourcing Acquisity API Pull ICP-matched leads based on filters
Campaign Management Instantly API Create contacts, assign to sequences, manage sending
Email Infrastructure Multiple Gmail Accounts Distribute sending load, improve deliverability
Reply Intelligence Zapmail Webhooks AI classification of reply sentiment and intent
Workflow Orchestration n8n Connect all platforms, execute conditional logic
Opportunity Creation Go High Level API Auto-create deals for positive replies
Booking Automation Calendly Links + Email Send personalized booking invitations
List Hygiene n8n Function Nodes Remove bounces, mark OOO contacts
Reporting Go High Level Dashboards Track metrics across entire funnel

Key capabilities:

  • Automatically sync new Acquisity leads to Instantly campaigns every 24 hours
  • Process incoming email replies within 2-5 minutes of receipt
  • Route positive replies to immediate booking sequence (no manual intervention)
  • Create Go High Level opportunities with full context (original email, reply text, lead data)
  • Clean contact lists by removing hard bounces and marking out-of-office contacts
  • Track full funnel metrics from lead count to booking show rate

Prerequisites

Before starting, ensure you have:

  • n8n instance (cloud or self-hosted with internet access for webhooks)
  • Acquisity account with API access and saved lead filters
  • Instantly account with at least 3 warmed Gmail inboxes connected
  • Zapmail account configured to monitor your Gmail sending accounts
  • Go High Level account with API credentials and custom pipeline created
  • Calendly account with booking link for sales calls
  • Basic JavaScript knowledge for Function nodes and data transformation
  • Understanding of webhook concepts (receiving POST requests from external services)

Step 1: Set Up Lead Sourcing from Acquisity

This phase connects n8n to Acquisity's API to pull fresh leads based on your ICP filters. You'll configure a scheduled trigger to run daily and push leads directly into Instantly campaigns.

Configure the Schedule Trigger

  1. Add a Schedule Trigger node set to run daily at 9:00 AM (adjust for your timezone)
  2. Set the trigger mode to "Every Day" to ensure consistent lead flow
  3. Enable the workflow to activate the schedule

Connect to Acquisity API

  1. Add an HTTP Request node named "Get Acquisity Leads"
  2. Set method to GET
  3. Configure the URL: https://api.acquisity.com/v1/leads?filter_id=YOUR_FILTER_ID&limit=100
  4. Add authentication header: Authorization: Bearer YOUR_ACQUISITY_API_KEY
  5. Set response format to JSON

Node configuration:

{
  "method": "GET",
  "url": "https://api.acquisity.com/v1/leads",
  "authentication": "headerAuth",
  "headerAuth": {
    "name": "Authorization",
    "value": "Bearer {{$credentials.acquisity.apiKey}}"
  },
  "qs": {
    "filter_id": "your_saved_filter_id",
    "limit": 100,
    "status": "new"
  }
}

Why this works:
Acquisity stores leads in filtered lists based on your ICP criteria. By calling the API with a specific filter_id, you retrieve only leads matching your target profile. The limit parameter controls batch size—100 leads per day prevents overwhelming your email infrastructure while maintaining consistent outreach volume. Using the "new" status ensures you don't re-import previously processed contacts.

Transform Lead Data for Instantly

  1. Add a Function node named "Format for Instantly"
  2. Map Acquisity fields to Instantly's required schema (email, firstName, lastName, companyName)
  3. Add custom fields for personalization tokens

Variables to customize:

  • filter_id: Your specific Acquisity saved filter ID
  • limit: Number of leads per batch (50-200 depending on sending capacity)
  • Field mappings in Function node to match your Acquisity data structure

Step 2: Push Leads to Instantly Campaigns

This section creates contacts in Instantly and assigns them to your outbound email sequences. You'll handle duplicate detection and campaign assignment logic.

Create Instantly Contacts

  1. Add an HTTP Request node named "Create Instantly Contact"
  2. Set method to POST
  3. URL: https://api.instantly.ai/v1/contacts
  4. Body type: JSON
  5. Include required fields: email, firstName, lastName, campaignId

Node configuration:

{
  "method": "POST",
  "url": "https://api.instantly.ai/v1/contacts",
  "authentication": "headerAuth",
  "headerAuth": {
    "name": "Api-Key",
    "value": "{{$credentials.instantly.apiKey}}"
  },
  "body": {
    "email": "{{$json.email}}",
    "firstName": "{{$json.firstName}}",
    "lastName": "{{$json.lastName}}",
    "companyName": "{{$json.companyName}}",
    "campaignId": "YOUR_CAMPAIGN_ID",
    "variables": {
      "company": "{{$json.companyName}}",
      "industry": "{{$json.industry}}"
    }
  }
}

Handle Duplicates

  1. Add an IF node to check HTTP response status
  2. Route 409 errors (duplicate contacts) to a separate branch
  3. Log duplicates without stopping the workflow

Why this approach:
Instantly's API returns a 409 status code when you attempt to create a contact that already exists. Instead of treating this as a failure, route these responses to a logging node. This prevents workflow errors while maintaining visibility into how many leads were already in your system. The campaignId assignment immediately enrolls new contacts in your sequence—no manual campaign assignment needed.

Step 3: Configure Zapmail Reply Monitoring

Zapmail monitors your Gmail inboxes and uses AI to classify replies. This phase sets up webhook receivers in n8n to process Zapmail's classifications.

Create Webhook Trigger for Zapmail

  1. Add a Webhook node named "Receive Zapmail Classification"
  2. Set HTTP Method to POST
  3. Path: /zapmail-reply (or your preferred endpoint)
  4. Response Code: 200
  5. Copy the webhook URL for Zapmail configuration

Configure Zapmail Routing Rules

In your Zapmail dashboard:

  • Set webhook destination to your n8n webhook URL
  • Configure classification categories: Positive, Neutral, Not Interested, Out of Office, Bounce
  • Enable automatic forwarding of classified emails

Process Reply Classifications

  1. Add a Switch node named "Route by Reply Type"
  2. Create routes for each classification:
    • Positive → Go High Level opportunity creation
    • Positive → Send booking link email
    • Bounce → Remove from Instantly
    • Out of Office → Mark as OOO in CRM
    • Not Interested → Update status, pause outreach

Node configuration for Switch logic:

// In Switch node, Mode: Rules
{
  "rules": [
    {
      "name": "Positive Reply",
      "condition": "={{$json.classification === 'positive'}}"
    },
    {
      "name": "Bounce",
      "condition": "={{$json.classification === 'bounce'}}"
    },
    {
      "name": "Out of Office",
      "condition": "={{$json.classification === 'ooo'}}"
    },
    {
      "name": "Not Interested",
      "condition": "={{$json.classification === 'not_interested'}}"
    }
  ]
}

Why this works:
Zapmail's AI analyzes reply content and assigns classifications based on intent and sentiment. By routing these classifications through n8n's Switch node, you execute different automation paths without writing complex conditional logic. Positive replies trigger immediate action (CRM + booking), while bounces trigger list cleanup. This separation ensures appropriate response to each reply type.

Step 4: Automate Go High Level Opportunity Creation

When Zapmail identifies a positive reply, this workflow creates a detailed opportunity in your Go High Level CRM with full conversation context.

Create GHL Opportunity

  1. Add an HTTP Request node named "Create GHL Opportunity"
  2. Set method to POST
  3. URL: https://rest.gohighlevel.com/v1/opportunities
  4. Include authentication header with your GHL API key

Node configuration:

{
  "method": "POST",
  "url": "https://rest.gohighlevel.com/v1/opportunities",
  "authentication": "headerAuth",
  "headerAuth": {
    "name": "Authorization",
    "value": "Bearer {{$credentials.ghl.apiKey}}"
  },
  "body": {
    "contactId": "{{$json.contactId}}",
    "pipelineId": "YOUR_PIPELINE_ID",
    "pipelineStageId": "replied_stage_id",
    "name": "{{$json.companyName}} - Positive Reply",
    "status": "open",
    "monetaryValue": 5000,
    "customFields": {
      "original_email_subject": "{{$json.originalSubject}}",
      "reply_text": "{{$json.replyBody}}",
      "reply_date": "{{$json.receivedAt}}",
      "campaign_name": "{{$json.campaignName}}"
    }
  }
}

Add Contact Notes

  1. Add a second HTTP Request node for GHL Notes API
  2. Attach the full email thread to the contact record
  3. Include timestamps and classification confidence scores

Why this approach:
Creating opportunities immediately upon positive reply ensures no lead falls through cracks. The customFields object preserves conversation context—your sales team sees the original outreach message and prospect's reply without switching tools. Setting monetaryValue helps with pipeline forecasting. The pipelineStageId of "replied" automatically positions the opportunity in your workflow, triggering any stage-specific automations you've configured in GHL.

Step 5: Send Automated Booking Link

After creating the GHL opportunity, send an immediate follow-up email with your Calendly booking link.

Compose Booking Email

  1. Add a Gmail node (or your preferred email service)
  2. Set recipient to the prospect's email from Zapmail webhook data
  3. Compose personalized email referencing their reply
  4. Include Calendly link with pre-filled parameters

Email template example:

// In Function node before Gmail send
const emailBody = `
Hi ${firstName},

Thanks for your reply! I'd love to continue the conversation.

Here's a link to grab 15 minutes on my calendar this week:
${calendlyLink}?name=${firstName}%20${lastName}&email=${email}

Looking forward to speaking with you.

Best,
[Your Name]
`;

return {
  to: email,
  subject: `Re: ${originalSubject}`,
  body: emailBody
};

Configure Calendly Parameters

Pre-fill Calendly booking form using URL parameters:

  • ?name= - Prospect's full name
  • &email= - Prospect's email
  • &a1= - Custom field for company name
  • &a2= - Custom field for campaign source

Why this works:
Sending the booking link within minutes of a positive reply capitalizes on prospect interest while it's highest. Pre-filling Calendly parameters reduces friction—prospects don't re-enter information they've already provided. Using Gmail's reply threading (matching subject line with "Re:") keeps the conversation connected, improving response rates by 15-20% compared to new thread emails.

Step 6: Implement List Hygiene Automation

Bounced emails and out-of-office responses require different handling to maintain list quality and sender reputation.

Remove Hard Bounces from Instantly

  1. From the Switch node's "Bounce" output, add HTTP Request node
  2. Method: DELETE
  3. URL: https://api.instantly.ai/v1/contacts/{email}
  4. This permanently removes invalid addresses

Mark Out-of-Office Contacts

  1. Add HTTP Request to update GHL contact
  2. Add tag "OOO" with timestamp
  3. Pause contact in Instantly campaigns for 30 days
  4. Schedule re-activation using n8n Wait node

Node configuration for OOO handling:

{
  "method": "PATCH",
  "url": "https://rest.gohighlevel.com/v1/contacts/{{$json.contactId}}",
  "body": {
    "tags": ["Out of Office"],
    "customFields": {
      "ooo_detected_date": "{{$now}}",
      "resume_outreach_date": "{{$now.plus({days: 30})}}"
    }
  }
}

Why this approach:
Hard bounces damage sender reputation if you continue emailing them. Immediate removal from Instantly prevents future send attempts to invalid addresses. Out-of-office responses indicate temporary unavailability—marking these contacts with a resume date prevents premature removal while respecting their absence. The 30-day pause aligns with typical business travel and vacation durations.

Workflow Architecture Overview

This workflow consists of 24 nodes organized into 5 main sections:

  1. Lead ingestion (Nodes 1-4): Schedule trigger → Acquisity API call → data transformation → Instantly contact creation
  2. Reply processing (Nodes 5-8): Zapmail webhook receiver → classification routing → conditional branching
  3. Opportunity management (Nodes 9-14): GHL opportunity creation → contact notes → custom field updates
  4. Booking automation (Nodes 15-18): Email composition → Calendly link insertion → Gmail send → confirmation logging
  5. List maintenance (Nodes 19-24): Bounce removal → OOO tagging → campaign pause → re-activation scheduling

Execution flow:

  • Trigger: Dual triggers (Schedule for lead import at 9 AM daily, Webhook for 24/7 reply processing)
  • Average run time: 3-8 seconds per reply, 45-90 seconds for daily lead batch
  • Key dependencies: Acquisity API, Instantly API, Zapmail webhooks, Go High Level API, Gmail SMTP

Critical nodes:

  • Switch Node (Reply Router): Determines all downstream actions based on Zapmail classification
  • HTTP Request (GHL Opportunity): Creates the CRM record that tracks deal progression
  • Function Node (Email Composer): Generates personalized booking invitation with Calendly parameters
  • HTTP Request (Instantly Delete): Maintains list quality by removing bounces immediately

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

Key Configuration Details

Zapmail Webhook Integration

Required fields:

  • Webhook URL: Your n8n instance URL + /webhook/zapmail-reply
  • Authentication: None (use n8n's built-in webhook authentication if needed)
  • Payload format: JSON with fields classification, email, replyBody, originalSubject, receivedAt

Common issues:

  • Zapmail sends test webhooks during setup → Filter these out using IF node checking for test: true field
  • Classification confidence scores below 0.7 → Add manual review step for ambiguous replies
  • Missing contactId in webhook → Implement lookup function to match email to GHL contact

Go High Level Pipeline Configuration

Required pipeline stages:

  1. New Lead (entry point for all Acquisity imports)
  2. Contacted (automatically set when Instantly sends first email)
  3. Replied (set by this workflow when Zapmail detects positive reply)
  4. Booked (updated when Calendly webhook fires)
  5. Showed (manually updated after meeting)
  6. Qualified (sales team assessment)
  7. Closed Won / Closed Lost

Stage automation triggers:

  • Replied → Booked: Calendly webhook integration (separate workflow)
  • Booked → Showed: Zoom/Riverside attendance webhook
  • Any stage → Closed Lost: "Not Interested" classification from Zapmail

Instantly Campaign Settings

Variables to customize:

  • dailySendLimit: 50 per inbox (150 total for 3 inboxes) - adjust based on domain age
  • delayBetweenEmails: 60-120 seconds randomization to appear human
  • followUpSequence: 3-4 emails spaced 3-4 days apart
  • unsubscribeHandling: Automatically pause on unsubscribe keywords

Why this approach:
Spreading sends across multiple inboxes (50/day each) keeps individual inbox sending volumes low, improving deliverability. The 60-120 second randomization prevents pattern detection by email providers. Three-email sequences balance persistence with respect—more than 4 follow-ups significantly increases spam complaints.

Testing & Validation

Test each component independently:

  1. Acquisity → Instantly flow: Manually trigger the Schedule node, verify contacts appear in Instantly campaign within 2 minutes
  2. Zapmail classification: Send test emails to your monitored inboxes with known positive/negative language, confirm correct routing
  3. GHL opportunity creation: Check that opportunities appear in correct pipeline stage with all custom fields populated
  4. Booking email delivery: Verify emails arrive in prospect inbox (not spam), Calendly link works, pre-fill parameters populate correctly

Review inputs and outputs:

  • Enable "Always Output Data" on all HTTP Request nodes during testing
  • Check the execution log for each node to verify API responses
  • Confirm error handling catches API rate limits and retries appropriately

Common troubleshooting:

  • Zapmail webhook not triggering → Verify webhook URL is publicly accessible (not localhost)
  • GHL opportunities missing data → Check that Zapmail webhook includes all required fields
  • Calendly links not pre-filling → URL encode special characters in query parameters
  • Instantly API 429 errors → Add Wait node with 1-second delay between batch contact creation

Deployment Considerations

Production Deployment Checklist

Area Requirement Why It Matters
Error Handling Retry logic with exponential backoff on all HTTP nodes Prevents data loss during temporary API outages
Monitoring Webhook health checks every 5 minutes Detect Zapmail integration failures within 5 minutes vs. discovering hours later
Documentation Node-by-node comments explaining business logic Reduces modification time by 2-4 hours when updating campaigns
Credentials Use n8n credential system, never hardcode API keys Prevents accidental exposure in workflow exports
Rate Limiting Implement queue system for high-volume reply processing Prevents GHL API throttling during reply spikes
Logging Store all webhook payloads in database for 90 days Enables troubleshooting and classification accuracy audits

Monitoring recommendations:

  • Set up n8n workflow error notifications to Slack or email
  • Create GHL dashboard tracking: leads added daily, reply rate, positive reply rate, booking rate
  • Monitor Instantly deliverability metrics: open rate >40%, bounce rate <2%, spam rate <0.1%
  • Weekly review of Zapmail classification accuracy (sample 20 replies, verify correct routing)

Customization ideas:

  • Add SMS notification via GHL when high-value prospects reply positively
  • Implement lead scoring based on company size, industry, and engagement level
  • Create separate workflows for different ICPs with tailored messaging
  • Build automated A/B testing by rotating email variants and tracking performance

Use Cases & Variations

Use Case 1: Agency Client Acquisition

  • Industry: Marketing/development agencies
  • Scale: 200-300 leads/week, 15-25 positive replies/week
  • Modifications needed: Add portfolio link insertion based on prospect industry, integrate Proposify for automated proposal sending after discovery calls

Use Case 2: SaaS Sales Development

  • Industry: B2B SaaS companies
  • Scale: 500+ leads/week, 30-50 positive replies/week
  • Modifications needed: Replace Calendly with Chili Piper for instant meeting routing to available reps, add Clearbit enrichment before GHL opportunity creation, implement lead scoring to prioritize high-value accounts

Use Case 3: Recruiting/Talent Acquisition

  • Industry: Staffing agencies, internal recruiting teams
  • Scale: 1000+ candidates/week, 100-150 positive responses/week
  • Modifications needed: Swap Acquisity for LinkedIn Sales Navigator scraping, modify reply classification for candidate interest levels, integrate ATS instead of GHL for candidate tracking

Use Case 4: Event Promotion

  • Industry: Conference organizers, webinar hosts
  • Scale: 2000+ invites/event, 200-400 registrations
  • Modifications needed: Time-bound campaigns with urgency messaging, integrate event platform API (Eventbrite, Hopin) for automatic registration, add countdown timers in follow-up emails

Customizing This Workflow

Alternative Integrations

Instead of Acquisity:

  • Apollo.io: Best for tech company targeting - requires updating HTTP Request node endpoints and authentication method to Apollo's API
  • ZoomInfo: Better if you need phone numbers and technographic data - swap out nodes 2-3, add phone number field mapping
  • Clay.com: Use when you need multi-source enrichment - requires webhook trigger instead of scheduled API call

Instead of Instantly:

  • Lemlist: Better personalization features - API structure similar, minimal node changes needed
  • Smartlead: Best for high-volume (1000+/day) - requires different rate limiting logic in batch creation
  • Reply.io: Use when you need LinkedIn + email sequences - add LinkedIn automation nodes

Instead of Zapmail:

  • Lavender AI: Better for reply coaching - requires different webhook payload structure
  • Custom GPT-4 classification: Full control over categories - replace Zapmail webhook with Gmail trigger + OpenAI node
  • Instantly's native AI: Simpler setup - reduces nodes by 3-4 but less classification accuracy

Workflow Extensions

Add automated reporting:

  • Add a Schedule node to run weekly on Monday 8 AM
  • Connect to Google Sheets API to log weekly metrics
  • Generate executive summary with key KPIs (reply rate, booking rate, show rate)
  • Nodes needed: +6 (Schedule, HTTP Request to GHL for data, Function for calculations, Google Sheets write, Gmail for report delivery)

Scale to handle more data:

  • Replace daily batch import with real-time Acquisity webhook
  • Add Redis/database node for deduplication checking before Instantly creation
  • Implement queue system using n8n's built-in queue mode for reply processing
  • Performance improvement: Process 500+ replies/hour vs. current 100-150/hour

Integration possibilities:

Add This To Get This Complexity
Slack notifications Real-time alerts when high-value prospects reply Easy (2 nodes)
Clearbit enrichment Enhanced company data before GHL creation Medium (4 nodes)
Proposify integration Auto-send proposals after discovery calls Medium (6 nodes)
Stripe payment links Include pricing in booking confirmation emails Easy (3 nodes)
Zoom recording analysis Transcribe calls, extract action items, update CRM Hard (12+ nodes)

Get Started Today

Ready to automate your client acquisition process?

  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 your API credentials for Acquisity, Instantly, Zapmail, and Go High Level in n8n's credential manager
  4. Set up Zapmail webhook: Copy your n8n webhook URL and configure it in Zapmail's routing rules
  5. Test with sample data: Create a test lead in Acquisity, send a test reply to your monitored inbox, verify the full flow executes correctly
  6. Deploy to production: Activate the Schedule trigger for daily lead imports and enable the webhook for 24/7 reply processing

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


Complete n8n Workflow JSON Template

{
  "name": "Automated Client Acquisition System",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 * * *"
            }
          ]
        }
      },
      "name": "Daily Lead Import - 9 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [250, 300]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "zapmail-reply",
        "responseMode": "responseNode",
        "options": {}
      },
      "name": "Zapmail Reply Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [250, 500],
      "webhookId": "zapmail-classification"
    },
    {
      "parameters": {
        "url": "https://api.acquisity.com/v1/leads",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "acquisityApi",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "filter_id",
              "value": "={{$credentials.acquisity.filterId}}"
            },
            {
              "name": "limit",
              "value": "100"
            },
            {
              "name": "status",
              "value": "new"
            }
          ]
        },
        "options": {}
      },
      "name": "Get Acquisity Leads",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [450, 300]
    },
    {
      "parameters": {
        "functionCode": "const leads = $input.all();
const formatted = [];

for (const lead of leads) {
  formatted.push({
    email: lead.json.email,
    firstName: lead.json.first_name,
    lastName: lead.json.last_name,
    companyName: lead.json.company_name,
    industry: lead.json.industry,
    variables: {
      company: lead.json.company_name,
      industry: lead.json.industry,
      title: lead.json.job_title
    }
  });
}

return formatted;"
      },
      "name": "Format for Instantly",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [650, 300]
    },
    {
      "parameters": {
        "url": "https://api.instantly.ai/v1/contacts",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "instantlyApi",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "email",
              "value": "={{$json.email}}"
            },
            {
              "name": "firstName",
              "value": "={{$json.firstName}}"
            },
            {
              "name": "lastName",
              "value": "={{$json.lastName}}"
            },
            {
              "name": "companyName",
              "value": "={{$json.companyName}}"
            },
            {
              "name": "campaignId",
              "value": "={{$credentials.instantly.campaignId}}"
            },
            {
              "name": "variables",
              "value": "={{$json.variables}}"
            }
          ]
        },
        "options": {
          "batching": {
            "batch": {
              "batchSize": 10,
              "batchInterval": 1000
            }
          }
        }
      },
      "name": "Create Instantly Contact",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [850, 300]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.classification}}",
              "operation": "equals",
              "value2": "positive"
            }
          ]
        }
      },
      "name": "Route by Reply Type",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 1,
      "position": [450, 500]
    },
    {
      "parameters": {
        "url": "https://rest.gohighlevel.com/v1/opportunities",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "goHighLevelApi",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "contactId",
              "value": "={{$json.contactId}}"
            },
            {
              "name": "pipelineId",
              "value": "={{$credentials.ghl.pipelineId}}"
            },
            {
              "name": "pipelineStageId",
              "value": "replied_stage"
            },
            {
              "name": "name",
              "value": "={{$json.companyName}} - Positive Reply"
            },
            {
              "name": "status",
              "value": "open"
            },
            {
              "name": "monetaryValue",
              "value": "5000"
            }
          ]
        },
        "options": {}
      },
      "name": "Create GHL Opportunity",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [650, 450]
    },
    {
      "parameters": {
        "functionCode": "const firstName = $json.firstName;
const lastName = $json.lastName;
const email = $json.email;
const companyName = $json.companyName;
const calendlyLink = $credentials.calendly.bookingLink;

const emailBody = `Hi ${firstName},

Thanks for your reply! I'd love to continue the conversation.

Here's a link to grab 15 minutes on my calendar this week:
${calendlyLink}?name=${encodeURIComponent(firstName + ' ' + lastName)}&email=${encodeURIComponent(email)}&a1=${encodeURIComponent(companyName)}

Looking forward to speaking with you.

Best,
[Your Name]`;

return {
  to: email,
  subject: `Re: ${$json.originalSubject}`,
  body: emailBody
};"
      },
      "name": "Compose Booking Email",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [850, 450]
    },
    {
      "parameters": {
        "sendTo": "={{$json.to}}",
        "subject": "={{$json.subject}}",
        "message": "={{$json.body}}",
        "options": {}
      },
      "name": "Send Booking Email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [1050, 450],
      "credentials": {
        "gmailOAuth2": {
          "id": "1",
          "name": "Gmail account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.classification}}",
              "operation": "equals",
              "value2": "bounce"
            }
          ]
        }
      },
      "name": "Check if Bounce",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [650, 550]
    },
    {
      "parameters": {
        "method": "DELETE",
        "url": "=https://api.instantly.ai/v1/contacts/{{$json.email}}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "instantlyApi",
        "options": {}
      },
      "name": "Remove from Instantly",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [850, 550]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.classification}}",
              "operation": "equals",
              "value2": "ooo"
            }
          ]
        }
      },
      "name": "Check if OOO",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [650, 650]
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "=https://rest.gohighlevel.com/v1/contacts/{{$json.contactId}}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "goHighLevelApi",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "tags",
              "value": "=[\"Out of Office\"]"
            }
          ]
        },
        "options": {}
      },
      "name": "Tag as OOO in GHL",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [850, 650]
    }
  ],
  "connections": {
    "Daily Lead Import - 9 AM": {
      "main": [
        [
          {
            "node": "Get Acquisity Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Acquisity Leads": {
      "main": [
        [
          {
            "node": "Format for Instantly",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format for Instantly": {
      "main": [
        [
          {
            "node": "Create Instantly Contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Zapmail Reply Webhook": {
      "main": [
        [
          {
            "node": "Route by Reply Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Reply Type": {
      "main": [
        [
          {
            "node": "Create GHL Opportunity",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check if Bounce",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check if OOO",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create GHL Opportunity": {
      "main": [
        [
          {
            "node": "Compose Booking Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compose Booking Email": {
      "main": [
        [
          {
            "node": "Send Booking Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Bounce": {
      "main": [
        [
          {
            "node": "Remove from Instantly",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if OOO": {
      "main": [
        [
          {
            "node": "Tag as OOO in GHL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}

Complete N8N Workflow Template

Copy the JSON below and import it into your N8N instance via Workflows → Import from File

{
  "name": "Automated B2B Lead Acquisition Pipeline",
  "nodes": [
    {
      "id": "webhook-lead-intake",
      "name": "Lead Intake Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        50,
        100
      ],
      "parameters": {
        "path": "lead-intake",
        "httpMethod": "POST",
        "responseCode": 200,
        "responseData": "firstEntryJson",
        "responseMode": "onReceived"
      },
      "typeVersion": 2
    },
    {
      "id": "validate-and-enrich",
      "name": "Validate & Enrich Lead",
      "type": "n8n-nodes-base.code",
      "position": [
        250,
        100
      ],
      "parameters": {
        "jsCode": "const email = $json.email || '';\nconst domain = email.split('@')[1] || '';\nconst emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nconst isValid = emailRegex.test(email) && !['noemail', 'test@test', 'fake', 'spam'].some(p => email.toLowerCase().includes(p));\n\nreturn {\n  ...($json),\n  domain: domain,\n  isValid: isValid,\n  status: 'new',\n  addedDate: new Date().toISOString()\n};",
        "language": "javascript"
      },
      "typeVersion": 2
    },
    {
      "id": "check-valid-email",
      "name": "Email Valid?",
      "type": "n8n-nodes-base.if",
      "position": [
        450,
        100
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "{{ $json.isValid }}",
              "value2": true,
              "condition": "equal"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "store-lead-sheet",
      "name": "Store Lead in Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        650,
        100
      ],
      "parameters": {
        "range": "A:G",
        "fieldsUi": {
          "values": [
            {
              "header": "Email",
              "fieldValue": "{{ $json.email }}"
            },
            {
              "header": "Company",
              "fieldValue": "{{ $json.company }}"
            },
            {
              "header": "First Name",
              "fieldValue": "{{ $json.firstName }}"
            },
            {
              "header": "Status",
              "fieldValue": "New"
            },
            {
              "header": "Added Date",
              "fieldValue": "{{ $json.addedDate }}"
            },
            {
              "header": "Domain",
              "fieldValue": "{{ $json.domain }}"
            },
            {
              "header": "Validation",
              "fieldValue": "Valid"
            }
          ]
        },
        "resource": "sheet",
        "operation": "append",
        "sheetName": {
          "value": "Leads"
        },
        "documentId": {
          "value": "{{ $env.GOOGLE_SHEETS_ID }}"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "create-contact-ghl",
      "name": "Create Contact in GHL",
      "type": "n8n-nodes-base.highLevel",
      "position": [
        850,
        100
      ],
      "parameters": {
        "email": "{{ $json.email }}",
        "lastName": "{{ $json.lastName }}",
        "resource": "contact",
        "firstName": "{{ $json.firstName }}",
        "operation": "create",
        "customFields": {
          "company": "{{ $json.company }}",
          "leadSource": "{{ $json.source || 'Email Campaign' }}"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "gmail-monitor",
      "name": "Monitor Email Replies",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        50,
        300
      ],
      "parameters": {
        "event": "messageReceived",
        "simple": true,
        "filters": {
          "readStatus": "unread"
        },
        "authentication": "oAuth2"
      },
      "typeVersion": 1
    },
    {
      "id": "parse-email",
      "name": "Parse Email Reply",
      "type": "n8n-nodes-base.code",
      "position": [
        250,
        300
      ],
      "parameters": {
        "jsCode": "const from = $json.from || '';\nconst senderEmail = from.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9_-]+)/)?.[1] || from;\nconst body = $json.body || '';\n\nreturn {\n  senderEmail: senderEmail,\n  subject: $json.subject || '',\n  body: body.substring(0, 300),\n  threadId: $json.threadId\n};",
        "language": "javascript"
      },
      "typeVersion": 2
    },
    {
      "id": "ai-classify",
      "name": "AI Classify Reply",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        450,
        300
      ],
      "parameters": {
        "prompt": "Classify this email as INTERESTED, NOT_INTERESTED, or SPAM. Return JSON: {\"classification\": \"\", \"sentiment\": \"positive/neutral/negative\"}\n\nSubject: {{ $json.subject }}\nBody: {{ $json.body }}",
        "modelId": "gpt-4o-mini",
        "resource": "text",
        "operation": "response"
      },
      "typeVersion": 2
    },
    {
      "id": "parse-classification",
      "name": "Parse AI Result",
      "type": "n8n-nodes-base.code",
      "position": [
        650,
        300
      ],
      "parameters": {
        "jsCode": "const response = $json.response || '{}';\nconst match = response.match(/\\{[^{}]*\\}/);\nlet result = { classification: 'UNKNOWN', sentiment: 'neutral' };\n\ntry {\n  result = JSON.parse(match ? match[0] : response);\n} catch (e) {\n  console.log('Parse error');\n}\n\nreturn result;",
        "language": "javascript"
      },
      "typeVersion": 2
    },
    {
      "id": "check-interested",
      "name": "Lead Interested?",
      "type": "n8n-nodes-base.if",
      "position": [
        850,
        300
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "{{ $json.classification }}",
              "value2": "INTERESTED",
              "condition": "equal"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "create-opportunity",
      "name": "Create Opportunity",
      "type": "n8n-nodes-base.highLevel",
      "position": [
        1050,
        250
      ],
      "parameters": {
        "name": "{{ $json.subject }} - Sales Opportunity",
        "status": "open",
        "resource": "opportunity",
        "contactId": "{{ $json.senderEmail }}",
        "operation": "create",
        "customFields": {
          "leadSource": "Cold Email",
          "replyClassification": "{{ $json.classification }}"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "calendly-webhook",
      "name": "Calendly Booking Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        50,
        500
      ],
      "parameters": {
        "path": "calendly-booking",
        "httpMethod": "POST",
        "responseCode": 200,
        "responseData": "firstEntryJson",
        "responseMode": "onReceived"
      },
      "typeVersion": 2
    },
    {
      "id": "parse-booking",
      "name": "Parse Booking",
      "type": "n8n-nodes-base.code",
      "position": [
        250,
        500
      ],
      "parameters": {
        "jsCode": "const booking = $json.payload || $json;\nreturn {\n  email: booking.invitee_email || '',\n  name: booking.invitee_name || '',\n  startTime: booking.start_time || '',\n  eventName: booking.event_name || 'Meeting'\n};",
        "language": "javascript"
      },
      "typeVersion": 2
    },
    {
      "id": "sync-booking-ghl",
      "name": "Sync Booking to GHL",
      "type": "n8n-nodes-base.highLevel",
      "position": [
        450,
        500
      ],
      "parameters": {
        "title": "{{ $json.eventName }} - {{ $json.name }}",
        "dueDate": "{{ $json.startTime }}",
        "resource": "task",
        "operation": "create",
        "contactIds": [
          "{{ $json.email }}"
        ],
        "description": "Scheduled meeting with {{ $json.email }}"
      },
      "typeVersion": 2
    },
    {
      "id": "log-metrics",
      "name": "Log Metrics",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        650,
        500
      ],
      "parameters": {
        "range": "A:D",
        "fieldsUi": {
          "values": [
            {
              "header": "Date",
              "fieldValue": "{{ $now.toFormat('yyyy-MM-dd') }}"
            },
            {
              "header": "Event Type",
              "fieldValue": "{{ $json.eventType || 'reply' }}"
            },
            {
              "header": "Classification",
              "fieldValue": "{{ $json.classification || 'booked' }}"
            },
            {
              "header": "Email",
              "fieldValue": "{{ $json.email || $json.senderEmail }}"
            }
          ]
        },
        "resource": "sheet",
        "operation": "append",
        "sheetName": {
          "value": "Metrics"
        },
        "documentId": {
          "value": "{{ $env.GOOGLE_SHEETS_ID }}"
        }
      },
      "typeVersion": 4
    }
  ],
  "connections": {
    "Email Valid?": {
      "main": [
        [
          {
            "node": "Store Lead in Sheet",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Parse Booking": {
      "main": [
        [
          {
            "node": "Sync Booking to GHL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Result": {
      "main": [
        [
          {
            "node": "Lead Interested?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lead Interested?": {
      "main": [
        [
          {
            "node": "Create Opportunity",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Classify Reply": {
      "main": [
        [
          {
            "node": "Parse AI Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Email Reply": {
      "main": [
        [
          {
            "node": "AI Classify Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Opportunity": {
      "main": [
        [
          {
            "node": "Log Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lead Intake Webhook": {
      "main": [
        [
          {
            "node": "Validate & Enrich Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Lead in Sheet": {
      "main": [
        [
          {
            "node": "Create Contact in GHL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sync Booking to GHL": {
      "main": [
        [
          {
            "node": "Log Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Contact in GHL": {
      "main": [
        [
          {
            "node": "Monitor Email Replies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monitor Email Replies": {
      "main": [
        [
          {
            "node": "Parse Email Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate & Enrich Lead": {
      "main": [
        [
          {
            "node": "Email Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calendly Booking Webhook": {
      "main": [
        [
          {
            "node": "Parse Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}