AutomationFlowsAI & RAG › Generate Personalized Cold Email Drafts From Google Sheets with Groq and Tavily

Generate Personalized Cold Email Drafts From Google Sheets with Groq and Tavily

ByUtkarsh Kapoor @utkarsh-automates on n8n.io

This scheduled workflow reads leads from Google Sheets, researches each company with Tavily Search, uses Groq (Llama 3.3 70B) to generate a short personalized cold email, and saves the result as a Gmail draft while updating the lead status back in the sheet. Runs on an hourly…

Cron / scheduled trigger★★★★☆ complexityAI-powered21 nodesGoogle SheetsHTTP RequestAgentGroq ChatGmail
AI & RAG Trigger: Cron / scheduled Nodes: 21 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #16725 — we link there as the canonical source.

This workflow follows the Agent → Gmail recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "4j0k7ox1P84mMlhJ",
  "name": "Generate personalized cold emails from Google Sheets leads with Groq and Tavily",
  "tags": [
    {
      "id": "7MBrqY6qk4mY8vJP",
      "name": "ai-content",
      "createdAt": "2026-06-14T15:55:44.934Z",
      "updatedAt": "2026-06-14T15:55:44.934Z"
    },
    {
      "id": "kchX80UgGVQMYFTq",
      "name": "AI Automation",
      "createdAt": "2026-06-14T13:36:36.357Z",
      "updatedAt": "2026-06-14T13:36:36.357Z"
    }
  ],
  "nodes": [
    {
      "id": "e3426970-c12d-492a-bcec-bdcf38f30b63",
      "name": "Check for new leads on schedule",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2944,
        512
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "3e24373c-73ba-47e5-a412-3e9a72b2e4a7",
      "name": "Read Leads",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2704,
        512
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "9ac80360-f762-4645-bc0e-8d8ddd14dcbb",
      "name": "Filter Unsent",
      "type": "n8n-nodes-base.if",
      "position": [
        -2464,
        512
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "filter-unsent",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "sent"
            },
            {
              "id": "has-email",
              "operator": {
                "type": "string",
                "operation": "isNotEmpty"
              },
              "leftValue": "={{ $json.email }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "9d9aee74-dbe3-43cb-850d-d9d0fb7dd948",
      "name": "Process One by One",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -2224,
        512
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "82728ffd-0016-45a6-aedb-e04e8ca74bce",
      "name": "Research Company",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1984,
        512
      ],
      "parameters": {
        "url": "https://api.tavily.com/search",
        "options": {
          "timeout": 10000
        },
        "jsonBody": "={\n  \"query\": \"{{ $json.company_name }} company overview products services\",\n  \"search_depth\": \"basic\",\n  \"max_results\": 3,\n  \"include_answer\": true\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "68ab3b1a-165c-4700-b15c-81868fb08916",
      "name": "Prepare lead context for AI",
      "type": "n8n-nodes-base.set",
      "position": [
        -1744,
        512
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "lead-name",
              "name": "lead_name",
              "type": "string",
              "value": "={{ $('Process One by One').first().json.name }}"
            },
            {
              "id": "lead-email",
              "name": "lead_email",
              "type": "string",
              "value": "={{ $('Process One by One').first().json.email }}"
            },
            {
              "id": "company",
              "name": "company_name",
              "type": "string",
              "value": "={{ $('Process One by One').first().json.company_name }}"
            },
            {
              "id": "role",
              "name": "lead_role",
              "type": "string",
              "value": "={{ $('Process One by One').first().json.role || 'Decision Maker' }}"
            },
            {
              "id": "research",
              "name": "company_research",
              "type": "string",
              "value": "={{ $json.answer || 'No additional research available' }}"
            },
            {
              "id": "row-num",
              "name": "row_number",
              "type": "number",
              "value": "={{ $('Process One by One').first().json.row_number }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1f81e108-a9a2-4c82-bd58-14042de34a76",
      "name": "Email Writer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1504,
        512
      ],
      "parameters": {
        "text": "=Write a personalized cold email for this lead.\n\n## Lead Information:\n- Name: {{ $json.lead_name }}\n- Email: {{ $json.lead_email }}\n- Company: {{ $json.company_name }}\n- Role: {{ $json.lead_role }}\n\n## Company Research:\n{{ $json.company_research }}\n\n## Rules:\n1. Subject line must be under 50 characters and curiosity-driven (no spam words like FREE, GUARANTEED, ACT NOW)\n2. Email body must be under 150 words\n3. First line must reference something specific about their company from the research (not generic)\n4. Clearly state what value you offer in one sentence\n5. End with a low-friction CTA (question, not a demand)\n6. No attachments, no links in the first email\n7. Professional but conversational tone, not salesy\n\nRespond ONLY with a valid JSON object (no markdown, no backticks):\n{\n  \"subject\": \"<subject line>\",\n  \"body\": \"<full email body with line breaks as \\n>\",\n  \"personalization_hook\": \"<the specific research detail you used>\"\n}",
        "options": {
          "systemMessage": "You are an expert cold email copywriter who writes short, personalized, high-converting emails for B2B outreach. You never use generic openings like 'I hope this email finds you well' or 'I came across your company'. Every email must start with a specific observation about the lead's company that proves you did research. Keep emails under 150 words. Return ONLY valid JSON, no markdown, no explanation."
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "65438eb1-a836-4d11-94bd-36e33f727f90",
      "name": "Groq Llama 3.3 70B",
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "position": [
        -1504,
        752
      ],
      "parameters": {
        "model": "llama-3.3-70b-versatile",
        "options": {
          "temperature": 0.7
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a1404bb1-6816-428b-aeab-1dae087b2e13",
      "name": "Parse Email",
      "type": "n8n-nodes-base.code",
      "position": [
        -1264,
        512
      ],
      "parameters": {
        "jsCode": "const raw = $input.first().json.output || $input.first().json.text || '';\nconst leadData = $('Prep for Email Writer').first().json;\n\ntry {\n  let jsonStr = raw;\n  const codeBlockMatch = raw.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n  if (codeBlockMatch) jsonStr = codeBlockMatch[1];\n  \n  const startIdx = jsonStr.indexOf('{');\n  const endIdx = jsonStr.lastIndexOf('}');\n  if (startIdx !== -1 && endIdx !== -1) {\n    jsonStr = jsonStr.substring(startIdx, endIdx + 1);\n  }\n  \n  const email = JSON.parse(jsonStr);\n  \n  if (!email.subject || !email.body) {\n    throw new Error('Missing subject or body in AI response');\n  }\n  \n  return [{\n    json: {\n      parseSuccess: true,\n      lead_name: leadData.lead_name,\n      lead_email: leadData.lead_email,\n      company_name: leadData.company_name,\n      lead_role: leadData.lead_role,\n      subject: email.subject,\n      body: email.body,\n      personalization_hook: email.personalization_hook || '',\n      row_number: leadData.row_number,\n      timestamp: new Date().toISOString()\n    }\n  }];\n} catch (e) {\n  return [{\n    json: {\n      parseSuccess: false,\n      lead_name: leadData.lead_name,\n      lead_email: leadData.lead_email,\n      company_name: leadData.company_name,\n      error: e.message,\n      row_number: leadData.row_number,\n      timestamp: new Date().toISOString()\n    }\n  }];\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "ab9f7e81-e877-4a00-a04b-6c664fcdb494",
      "name": "Email Valid?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1024,
        512
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "check-parse",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.parseSuccess }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "da6ecedc-9112-45e6-850c-c32fd690d967",
      "name": "Create Gmail Draft",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -784,
        416
      ],
      "parameters": {
        "message": "={{ $json.body }}",
        "options": {},
        "subject": "={{ $json.subject }}",
        "resource": "draft"
      },
      "typeVersion": 2.1
    },
    {
      "id": "966b82c7-4451-4f2a-9d91-3327ed870601",
      "name": "Update Lead Status",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -544,
        416
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "40d4f819-6462-4205-992f-89d63bcc2883",
      "name": "Rate Limit Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        -304,
        512
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "3daa25a5-d2dd-402b-be66-c17a63e81582",
      "name": "Log failed email to sheet",
      "type": "n8n-nodes-base.set",
      "position": [
        -784,
        704
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "err-log",
              "name": "error_log",
              "type": "string",
              "value": "=Failed to generate email for {{ $json.lead_name }} at {{ $json.company_name }}: {{ $json.error }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e1b3b618-09a5-4037-86b8-c01c5aaa5e70",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3552,
        176
      ],
      "parameters": {
        "width": 492,
        "height": 632,
        "content": "## Generate personalized cold emails from Google Sheets leads with Groq and Tavily\n\n### How it works\n\nThis workflow reads leads from a Google Sheet, researches each company using Tavily web search, and generates a hyper-personalized cold email using Groq Llama 3.3 70B. Each email references specific details about the lead's company that the AI found during research. Emails are saved as Gmail drafts for your review before sending. The lead sheet is updated with status, subject line, and personalization hook used.\n\n### What you get per lead\n\n- Automated company research via Tavily (products, services, recent news)\n- A personalized subject line under 50 characters\n- Email body under 150 words with a specific opening line about their company\n- Gmail draft ready for review and sending\n- Google Sheet updated with draft status and timestamp"
      },
      "typeVersion": 1
    },
    {
      "id": "6bf5b136-8c8e-4ad2-92cd-6554c964dfa0",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3024,
        304
      ],
      "parameters": {
        "color": 2,
        "width": 520,
        "height": 360,
        "content": "## Read and filter leads\n\nCheck for new leads on schedule runs the workflow on your chosen interval (default: hourly). Read Leads pulls all rows from your Google Sheet. Filter Unsent removes rows that already have status set to 'sent' and rows missing an email address, so only new leads are processed."
      },
      "typeVersion": 1
    },
    {
      "id": "0dfe3245-401c-4627-b754-70bc0b5dfbd0",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2480,
        288
      ],
      "parameters": {
        "color": 4,
        "width": 644,
        "height": 376,
        "content": "## Research each company\n\nProcess One by One ensures leads are handled sequentially to respect API rate limits. For each lead, Tavily searches for the company name plus 'overview products services' and returns a summary answer with the top 3 results. The Prep node combines lead data with research results for the AI writer."
      },
      "typeVersion": 1
    },
    {
      "id": "49b34317-8e36-461e-a564-9b600fa8d5d0",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1792,
        272
      ],
      "parameters": {
        "color": 3,
        "width": 652,
        "height": 636,
        "content": "## AI email generation\n\nThe Email Writer agent receives the lead name, role, company, and research summary. It writes a personalized cold email following strict rules: under 150 words, specific opening line from research, clear value proposition, low-friction CTA, no spam words. Groq Llama 3.3 70B handles generation on the free tier. Parse Email extracts the JSON response and validates that subject and body exist."
      },
      "typeVersion": 1
    },
    {
      "id": "939f981b-43e9-46de-97ae-8bd4408d4a68",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1056,
        192
      ],
      "parameters": {
        "color": 6,
        "width": 908,
        "height": 700,
        "content": "## Save draft and update sheet\n\nValid emails are saved as Gmail drafts, never auto-sent, so you can review every email before it goes out. The lead's row in Google Sheets is updated with status 'sent', the subject line used, the personalization hook, and a timestamp. A 5-second wait between leads prevents hitting API rate limits. Failed emails are logged with the error message for debugging."
      },
      "typeVersion": 1
    },
    {
      "id": "1a77d000-e927-43e5-b939-99d722e59fc3",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        144
      ],
      "parameters": {
        "color": "#F2BBBB",
        "width": 476,
        "height": 1080,
        "content": "## Setup Guide\n\n### Step 1 - Create your lead sheet\nCreate a Google Sheet with these columns:\nname | email | company_name | role | status | subject_used | personalization_hook | drafted_at\n\nFill in name, email, company_name, and role for each lead. Leave status and later columns empty.\n\n### Step 2 - Groq API Key (required)\n1. Go to console.groq.com - API Keys - Create\n2. n8n: Credentials - Add - Groq API - paste key - Save\n3. Select in the Groq Llama 3.3 70B node\n\n### Step 3 - Tavily API Key (required)\n1. Go to app.tavily.com and sign up (1000 free searches/month)\n2. Copy your API key\n3. Add it to the Research Company HTTP Request node body as the api_key field\n\n### Step 4 - Google Sheets (required)\n1. n8n: Credentials - Add - Google Sheets OAuth2\n2. Select your lead spreadsheet in both the Read Leads and Update Lead Status nodes\n\n### Step 5 - Gmail (required)\n1. n8n: Credentials - Add - Gmail OAuth2\n2. Select in the Create Gmail Draft node\n\n### Step 6 - Activate\n1. Click Test Workflow with a few test leads first\n2. Review the Gmail drafts it creates\n3. Toggle Active to run on schedule"
      },
      "typeVersion": 1
    },
    {
      "id": "dc10250b-f405-4783-91f6-86f964ddaa34",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        144
      ],
      "parameters": {
        "color": 5,
        "width": 476,
        "height": 900,
        "content": "## Customization\n\n### Change your value proposition\nEdit the Email Writer agent prompt to describe what YOUR business offers. Replace the generic value statement with your specific service (e.g., 'I build AI automation workflows that save 10+ hours per week').\n\n### Add follow-up sequences\nDuplicate the workflow and modify the filter to find leads where status is 'sent' and drafted_at is more than 3 days ago. Write a follow-up email referencing the first one.\n\n### Use LinkedIn data instead of Tavily\nReplace the Research Company node with a LinkedIn profile scraper or manually add a 'linkedin_summary' column to your sheet that the AI can reference.\n\n### Auto-send instead of drafting\nChange the Create Gmail Draft node from draft creation to email sending. Only do this after you have tested and trust the AI output quality.\n\n### Adjust batch size\nChange the Check for new leads on schedule interval and batch size to process more or fewer leads per run. Groq free tier allows 30 requests per minute."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "2131d795-c61b-4183-ae15-66beeeaac506",
  "nodeGroups": [],
  "connections": {
    "Read Leads": {
      "main": [
        [
          {
            "node": "Filter Unsent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Email": {
      "main": [
        [
          {
            "node": "Email Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Valid?": {
      "main": [
        [
          {
            "node": "Create Gmail Draft",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log failed email to sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Writer": {
      "main": [
        [
          {
            "node": "Parse Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Unsent": {
      "main": [
        [
          {
            "node": "Process One by One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Wait": {
      "main": [
        [
          {
            "node": "Process One by One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Research Company": {
      "main": [
        [
          {
            "node": "Prepare lead context for AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Gmail Draft": {
      "main": [
        [
          {
            "node": "Update Lead Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Groq Llama 3.3 70B": {
      "ai_languageModel": [
        [
          {
            "node": "Email Writer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Process One by One": {
      "main": [
        [
          {
            "node": "Research Company",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Lead Status": {
      "main": [
        [
          {
            "node": "Rate Limit Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log failed email to sheet": {
      "main": [
        [
          {
            "node": "Rate Limit Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare lead context for AI": {
      "main": [
        [
          {
            "node": "Email Writer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for new leads on schedule": {
      "main": [
        [
          {
            "node": "Read Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This scheduled workflow reads leads from Google Sheets, researches each company with Tavily Search, uses Groq (Llama 3.3 70B) to generate a short personalized cold email, and saves the result as a Gmail draft while updating the lead status back in the sheet. Runs on an hourly…

Source: https://n8n.io/workflows/16725/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This workflow automates invoicing and payment follow-ups using Google Sheets, PDFShift, Groq (LLM), Gmail, and Telegram, sending initial invoices with PDF attachments, scheduling overdue reminders at

Google Sheets, Gmail, Telegram +6
AI & RAG

This scheduled workflow pulls recent resolved procurement tickets and chat queries, clusters similar issues, and uses Groq LLMs to analyze patterns and draft Markdown knowledge base articles, then ema

Groq Chat, Agent, Gmail +2
AI & RAG

This workflow automatically monitors competitor product prices stored in Google Sheets. It scrapes product pages, extracts pricing and offer data using AI, and compares it with historical values. Base

Google Sheets, Agent, Groq Chat +3
AI & RAG

Supplier Capacity Risk Alert Workflow. Uses httpRequest, lmChatGroq, agent, gmail. Scheduled trigger; 28 nodes.

HTTP Request, Groq Chat, Agent +3
AI & RAG

This workflow automatically generates 2–3 high-quality Indian stock investment ideas daily by combining trending stock data + latest market news, processing it with AI (Groq/OpenAI), avoiding duplicat

HTTP Request, Agent, RSS Feed Read +5