DEV Community

Christian Mbah
Christian Mbah

Posted on

Build an AI-Powered Lead Follow-Up System with n8n and OpenAI 🚀

Let's build an AI-powered lead follow-up system today on n8n.

A webhook captures the lead. OpenAI writes a personalized email.
Gmail sends it. Google Sheets logs it.

Four nodes that runs in under 10 seconds with zero humans in the loop.

By the end of this post you'll have a fully working workflow you can import directly into n8n with the JSON export is at the bottom.

What you'll learn:

  • How to capture lead data via a webhook trigger
  • How to use OpenAI (gpt-4o-mini) to write personalised follow-up emails
  • How to send automatically via Gmail
  • How to log every lead and email sent to Google Sheets

What You're Building

Here's the flow:

Webhook → OpenAI (personalize email) → Gmail (send) → Google Sheets (log)
Enter fullscreen mode Exit fullscreen mode

A lead submits a form anywhere — your website, Tally, Typeform, whatever. The webhook fires, OpenAI reads their data and writes a personalized follow-up email, Gmail sends it automatically and finally Google Sheets logs the lead and the email that was sent, so you have a record.

No human in the loop. The whole thing runs in under 10 seconds.


Prerequisites

  • n8n account (cloud or self-hosted — either works)
  • OpenAI API key
  • A Gmail account connected to n8n
  • A Google Sheet to log leads to

Step 1 — The Webhook Trigger

Create a new workflow in n8n. Add your first node: Webhook.

Set the HTTP Method to POST. n8n will generate a unique URL for you. Copy it — this is what your form will POST to.

Your webhook will receive a payload that looks something like this:

{
  "name": "Chris Mbah",
  "email": "chris@example.com",
  "company": "Endy Studios",
  "role": "Founder",
  "message": "Looking for help automating our lead pipeline"
}
Enter fullscreen mode Exit fullscreen mode

The field names will match whatever your form sends. Keep that in mind when configuring the nodes below — you'll reference these fields by name.

Test it: Click "Listen for test event" in n8n, then send a test POST request using Postman or a simple curl command. Once n8n receives it, the data shows up in the node output and you can reference it downstream.

curl -X POST https://your-n8n-webhook-url \
  -H "Content-Type: application/json" \
  -d '{"name":"Chris Mbah","email":"chris@example.com","company":"Endy Studios","role":"Founder","message":"Looking for help automating our lead pipeline"}'
Enter fullscreen mode Exit fullscreen mode

Step 2 — OpenAI (Write the Personalised Email)

Add an OpenAI node. Connect it to the Webhook node.

Set the resource to Message and the operation to Complete.

In the Model field, use gpt-4o-mini. It's fast, cheap, and more than capable for email writing.

In the Prompt field, this is where the personalisation happens. Use n8n's expression syntax to pull in the webhook data:

You are an AI assistant helping a freelance AI automation consultant follow up with new leads.

Write a short, professional follow-up email to a lead with the following details:

Name: {{ $json.name }}
Company: {{ $json.company }}
Role: {{ $json.role }}
Message they sent: {{ $json.message }}

Guidelines:
- Keep it under 120 words
- Sound human, not like a template
- Acknowledge what they said specifically
- End with a clear CTA to book a 20-minute call
- Do not use subject lines or greetings like "Dear" — start directly
- Return only the email body, nothing else
Enter fullscreen mode Exit fullscreen mode

Set Max Tokens to 300. That's plenty for a short follow-up email and keeps your API costs low.

The node output will be a single string — the email body. You'll reference it in the next step as {{ $json.message.content }}.


Step 3 — Gmail (Send the Email)

Add a Gmail node. Connect it to the OpenAI node.

Set the operation to Send.

Configure the fields:

Field Value
To {{ $('Webhook').item.json.email }}
Subject Re: Your enquiry — let's talk automation
Message {{ $json.message.content }}
Send As HTML or Plain Text (your preference)

The To field references the original webhook data directly — not the OpenAI output — so make sure you're pulling from the right node. In n8n, you can reference any previous node's output using $('Node Name').item.json.fieldName.

One thing worth noting: the subject line above is generic. You can make it dynamic too. Ask OpenAI to return a subject line alongside the body by adjusting your prompt to output JSON:

Return a JSON object with two fields:
- "subject": a short, specific subject line (max 8 words)
- "body": the email body
Return only valid JSON. No markdown. No backticks.
Enter fullscreen mode Exit fullscreen mode

Then parse it in n8n with a Code node before passing to Gmail:

const response = JSON.parse($input.item.json.message.content);
return { subject: response.subject, body: response.body };
Enter fullscreen mode Exit fullscreen mode

Step 4 — Google Sheets (Log the Lead)

Add a Google Sheets node. Connect it to the Gmail node.

Set the operation to Append Row.

Select your spreadsheet and sheet. Then map the columns:

Column Value
Name {{ $('Webhook').item.json.name }}
Email {{ $('Webhook').item.json.email }}
Company {{ $('Webhook').item.json.company }}
Role {{ $('Webhook').item.json.role }}
Message {{ $('Webhook').item.json.message }}
Email Sent {{ $('OpenAI').item.json.message.content }}
Timestamp {{ $now }}

This gives you a running log of every lead and exactly what was sent to them. Useful if you ever need to review what went out, or hand things off to a client.


The Full Workflow

Here's what the complete canvas looks like:

[Webhook] → [OpenAI] → [Gmail] → [Google Sheets]
Enter fullscreen mode Exit fullscreen mode

Four nodes. Linear. No branching required for this version.

If you want to add branching later — for example, only email leads who mention a specific intent, or route high-value leads to a Slack notification — you'd add an IF node between Webhook and OpenAI and split the flow from there.


Activating the Workflow

Once everything is tested and working, toggle the workflow to Active in the top right.

From this point on, every POST request to your webhook URL triggers the full sequence automatically. No need to keep the n8n editor open.

If you're self-hosting, make sure your n8n instance is always running. If you're on cloud, it handles that for you.


What This Costs to Run

At current OpenAI pricing, gpt-4o-mini costs roughly $0.15 per million input tokens and $0.60 per million output tokens.

A single lead follow-up — prompt + response — uses around 300–400 tokens total.

That's roughly $0.0001 per lead. You could process 10,000 leads for about $1.

Gmail is free. Google Sheets is free. n8n cloud has a free tier that covers this comfortably.

This entire system runs at near-zero cost.

AI Lead Conversion Workflow


Full JSON Export

Import this directly into n8n. Go to your workflow, click the menu (⋯), select Import from JSON, and paste this in. Swap in your own credentials and webhook URL.

{
  "name": "AI Lead Follow-Up System",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "lead-followup",
        "responseMode": "onReceived",
        "options": {}
      },
      "id": "webhook-node",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [250, 300]
    },
    {
      "parameters": {
        "resource": "chat",
        "operation": "complete",
        "model": "gpt-4o-mini",
        "messages": {
          "values": [
            {
              "role": "user",
              "content": "You are an AI assistant helping a freelance AI automation consultant follow up with new leads.\n\nWrite a short, professional follow-up email to a lead with the following details:\n\nName: {{ $json.name }}\nCompany: {{ $json.company }}\nRole: {{ $json.role }}\nMessage they sent: {{ $json.message }}\n\nGuidelines:\n- Keep it under 120 words\n- Sound human, not like a template\n- Acknowledge what they said specifically\n- End with a clear CTA to book a 20-minute call\n- Do not use subject lines or greetings like \"Dear\" — start directly\n- Return only the email body, nothing else"
            }
          ]
        },
        "options": {
          "maxTokens": 300
        }
      },
      "id": "openai-node",
      "name": "OpenAI",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1,
      "position": [500, 300]
    },
    {
      "parameters": {
        "operation": "send",
        "toList": "={{ $('Webhook').item.json.email }}",
        "subject": "Re: Your enquiry — let's talk automation",
        "message": "={{ $json.message.content }}",
        "options": {}
      },
      "id": "gmail-node",
      "name": "Gmail",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [750, 300]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "value": "YOUR_GOOGLE_SHEET_ID"
        },
        "sheetName": {
          "value": "Sheet1"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Name": "={{ $('Webhook').item.json.name }}",
            "Email": "={{ $('Webhook').item.json.email }}",
            "Company": "={{ $('Webhook').item.json.company }}",
            "Role": "={{ $('Webhook').item.json.role }}",
            "Message": "={{ $('Webhook').item.json.message }}",
            "Email Sent": "={{ $('OpenAI').item.json.message.content }}",
            "Timestamp": "={{ $now }}"
          }
        },
        "options": {}
      },
      "id": "sheets-node",
      "name": "Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [1000, 300]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [[{ "node": "OpenAI", "type": "main", "index": 0 }]]
    },
    "OpenAI": {
      "main": [[{ "node": "Gmail", "type": "main", "index": 0 }]]
    },
    "Gmail": {
      "main": [[{ "node": "Google Sheets", "type": "main", "index": 0 }]]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Where to Take This Next

This is the foundation. Once it's running, there are a few natural extensions:

Add lead scoring. Ask OpenAI to rate the lead 1–10 based on their message and role before writing the email. Route low-score leads to a different, shorter email. Route high-score leads to also ping your Slack.

Add a delay. If you want the email to feel less instant (some people find immediate auto-replies suspicious), add a Wait node between OpenAI and Gmail. Set it to 10–15 minutes.

Connect it to your CRM. Swap Google Sheets for a HubSpot or Airtable node. Same logic, cleaner data management.

But start with what's here. Get it running. See leads come in and emails go out automatically.

That's the point of automation — not the complexity. The simplicity.


Building more of these publicly. Follow along if you want to see what comes next.


n8n #automation #openai #tutorial #nocode #webdev #javascript #productivity

Top comments (0)