AutomationFlowsAI & RAG › Send Personalized Cold Email Sequences with Google Sheets, Gmail, Gemini and…

Send Personalized Cold Email Sequences with Google Sheets, Gmail, Gemini and…

Original n8n title: Send Personalized Cold Email Sequences with Google Sheets, Gmail, Gemini and Slack

ByMuhammed Umar @umarimran1234 on n8n.io

This workflow runs daily to send Gemini-generated cold emails from leads stored in Google Sheets via Gmail, schedules two follow-ups based on time since sending, and continuously monitors Gmail for replies to update the sheet and alert a Slack channel. Runs every day at 9AM and…

Event trigger★★★★★ complexityAI-powered44 nodesGmail TriggerGoogle SheetsSlackGmailGoogle Gemini ChatChain Llm
AI & RAG Trigger: Event Nodes: 44 Complexity: ★★★★★ AI nodes: yes Added:
Send Personalized Cold Email Sequences with Google Sheets, Gmail, Gemini and… — n8n workflow card showing Gmail Trigger, Google Sheets, Slack integration

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

This workflow follows the Chainllm → 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": "b8YvaxNvM3vy9jwn",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Cold Email Machine \u2014 FINAL FIXED",
  "tags": [],
  "nodes": [
    {
      "id": "47d99b87-faf1-49d9-bc43-52d376ef741a",
      "name": "When New Email Received",
      "type": "n8n-nodes-base.gmailTrigger",
      "notes": "Polls Gmail inbox every minute for new unread emails. When found, passes email data downstream.",
      "position": [
        -6368,
        864
      ],
      "parameters": {
        "filters": {
          "labelIds": [
            "INBOX"
          ],
          "readStatus": "unread"
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "215cacea-f04d-4834-b82e-e04bf0bf5da4",
      "name": "Extract Email Sender Info",
      "type": "n8n-nodes-base.code",
      "notes": "Extracts sender email, subject, and snippet from incoming Gmail message.",
      "position": [
        -6128,
        896
      ],
      "parameters": {
        "jsCode": "// Extract sender email and message details\nconst email = $input.first().json;\n\nconst senderEmail = email.from?.value?.[0]?.address \n  || email.from \n  || '';\n\nconst subject = email.subject || '';\nconst snippet = email.snippet || email.text?.substring(0, 200) || '';\nconst messageId = email.id || '';\nconst threadId = email.threadId || '';\n\n// Clean sender email\nconst cleanSender = senderEmail.toLowerCase().trim();\n\nif (!cleanSender) {\n  throw new Error('Could not extract sender email from: ' + JSON.stringify(email.from));\n}\n\nreturn [{\n  json: {\n    sender_email: cleanSender,\n    subject: subject,\n    snippet: snippet.substring(0, 200),\n    message_id: messageId,\n    thread_id: threadId,\n    received_at: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "544609c6-7930-4f92-83fc-e0b4b36d53b6",
      "name": "Read Leads from Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Searches Leads sheet for a row where contact_email matches sender. If found = reply to our cold email.",
      "position": [
        -5904,
        864
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $json.sender_email }}",
              "lookupColumn": "contact_email"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit#gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5,
      "continueOnFail": true
    },
    {
      "id": "c7046745-b701-45db-951f-37977df656ef",
      "name": "Check Cold Email Reply",
      "type": "n8n-nodes-base.if",
      "notes": "TRUE = sender found in sheet AND not already marked replied \u2192 it's a real cold email reply! FALSE = unrelated email, stop.",
      "position": [
        -5680,
        880
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "lead-found",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.contact_email }}",
              "rightValue": ""
            },
            {
              "id": "was-sent",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "replied"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "57860957-80bf-48aa-b8ce-cfe9840517ed",
      "name": "Merge Lead and Email Data",
      "type": "n8n-nodes-base.code",
      "notes": "Combines lead data from sheet with incoming email details for updating and Slack notification.",
      "position": [
        -5424,
        880
      ],
      "parameters": {
        "jsCode": "// Merge lead data with incoming email data\nconst leadRow = $input.first().json;\nconst emailData = $('Extract Email Sender Info').first().json;\n\nreturn [{\n  json: {\n    // Lead data from sheet\n    company_name: leadRow.company_name,\n    contact_email: leadRow.contact_email,\n    website: leadRow.website,\n    your_service: leadRow.your_service,\n    sent_at: leadRow.sent_at,\n    \n    // Reply data\n    reply_subject: emailData.subject,\n    reply_snippet: emailData.snippet,\n    reply_at: emailData.received_at,\n    message_id: emailData.message_id\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "9e6c9c25-b681-4d2c-ada4-070aa61d68cc",
      "name": "Update Sheets Reply Status",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Updates lead row: reply_received=TRUE, status=replied, reply_at=timestamp, reply_snippet=first 200 chars. This automatically stops follow-up sequence.",
      "position": [
        -5200,
        864
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "replied",
            "reply_at": "={{ $json.reply_at }}",
            "contact_email": "={{ $json.contact_email }}",
            "reply_snippet": "={{ $json.reply_snippet }}",
            "reply_received": "TRUE"
          },
          "schema": [
            {
              "id": "contact_email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "contact_email",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "reply_received",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "reply_received",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "reply_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "reply_at",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "reply_snippet",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "reply_snippet",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "contact_email"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "2d8a538d-ba13-4e2f-8166-61605dbc7c74",
      "name": "Post Hot Lead Alert to Slack",
      "type": "n8n-nodes-base.slack",
      "notes": "Sends immediate Slack notification with company name, their reply snippet, and reminder to respond quickly.",
      "position": [
        -4992,
        848
      ],
      "parameters": {
        "text": "=\ud83d\udd25 *HOT LEAD \u2014 Reply Received!*\n\n\ud83c\udfe2 *Company:* {{ $json.company_name }}\n\ud83d\udce7 *Email:* {{ $json.contact_email }}\n\ud83c\udf10 *Website:* {{ $json.website }}\n\ud83d\udcec *Subject:* {{ $json.reply_subject }}\n\u23f0 *Replied at:* {{ $json.reply_at }}\n\n\ud83d\udcac *Their message:*\n_{{ $json.reply_snippet }}_\n\n\u2705 Sheet updated \u2014 follow-up sequence stopped.\n\ud83d\udc49 *Reply now while they're warm!*",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0BG0ND87N1",
          "cachedResultName": "announcements"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "b61defa6-0b79-42f0-afc0-fee46c882e24",
      "name": "Tracking Pixel Webhook",
      "type": "n8n-nodes-base.webhook",
      "notes": "GET request fires when email is opened. URL contains ?id=BASE64_EMAIL. Must be Active workflow with production /webhook/ URL.",
      "position": [
        -6400,
        464
      ],
      "parameters": {
        "path": "track-open",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "6814c450-5be8-4a09-8af0-82ab2f53b487",
      "name": "Return Pixel Image",
      "type": "n8n-nodes-base.respondToWebhook",
      "notes": "Immediately returns a 1x1 transparent GIF. This is what the email client receives. Response must be fast \u2014 do processing after.",
      "position": [
        -6128,
        336
      ],
      "parameters": {
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "image/gif"
              },
              {
                "name": "Cache-Control",
                "value": "no-cache, no-store, must-revalidate"
              },
              {
                "name": "Pragma",
                "value": "no-cache"
              }
            ]
          }
        },
        "respondWith": "binary"
      },
      "typeVersion": 1.2
    },
    {
      "id": "f0632c54-8d1a-4d02-8e0a-e562d933e033",
      "name": "Decode Tracking ID",
      "type": "n8n-nodes-base.code",
      "notes": "Decodes base64 tracking ID back to email address. Validates it looks like an email before proceeding.",
      "position": [
        -6128,
        512
      ],
      "parameters": {
        "jsCode": "// Extract and decode tracking ID from URL query param\nconst query = $input.first().json.query;\nconst trackingId = query?.id || '';\n\nif (!trackingId) {\n  return [{ json: { error: 'No tracking ID', email: null } }];\n}\n\n// Decode base64 to get email\nlet email = '';\ntry {\n  email = Buffer.from(trackingId, 'base64').toString('utf-8');\n} catch(e) {\n  // Try URL decode first then base64\n  try {\n    email = Buffer.from(decodeURIComponent(trackingId), 'base64').toString('utf-8');\n  } catch(e2) {\n    email = trackingId; // fallback \u2014 use as-is\n  }\n}\n\n// Basic email validation\nconst isValidEmail = email.includes('@') && email.includes('.');\n\nreturn [{\n  json: {\n    tracking_id: trackingId,\n    contact_email: isValidEmail ? email.toLowerCase().trim() : null,\n    opened_at: new Date().toISOString(),\n    valid: isValidEmail\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "72224efd-cfe6-48b5-890f-c86a42b738c7",
      "name": "Valid Email?",
      "type": "n8n-nodes-base.if",
      "notes": "Only proceeds if decoded tracking ID is a valid email address.",
      "position": [
        -5904,
        512
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "valid-email",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.valid }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "43bdcab6-fb80-4e66-b92b-81d403c78f1a",
      "name": "Find Lead Row",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Finds the lead row by contact_email. Gets current open_count to increment it.",
      "position": [
        -5680,
        512
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $json.contact_email }}",
              "lookupColumn": "contact_email"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit#gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5,
      "continueOnFail": true
    },
    {
      "id": "70078ef8-0156-41b9-82f3-283ef7566a3e",
      "name": "Prepare Update Data",
      "type": "n8n-nodes-base.code",
      "notes": "Increments open_count. Tracks first open separately from repeat opens. Only updates opened_at on first open.",
      "position": [
        -5456,
        512
      ],
      "parameters": {
        "jsCode": "// Build update data \u2014 increment open count\nconst leadRow = $input.first().json;\nconst trackData = $('Decode Tracking ID').first().json;\n\n// Get current open count and increment\nconst currentCount = parseInt(leadRow.open_count || '0') || 0;\nconst newCount = currentCount + 1;\n\n// First open vs repeat open\nconst isFirstOpen = currentCount === 0;\n\nreturn [{\n  json: {\n    company_name: leadRow.company_name || 'Unknown',\n    contact_email: trackData.contact_email,\n    your_service: leadRow.your_service || '',\n    email_opened: 'TRUE',\n    opened_at: isFirstOpen ? trackData.opened_at : leadRow.opened_at,\n    last_opened_at: trackData.opened_at,\n    open_count: newCount.toString(),\n    is_first_open: isFirstOpen,\n    status: leadRow.status || 'sent'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "fb36f091-951c-40dc-a9fa-248334fa0895",
      "name": "Update Sheet \u2014 Email Opened",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Updates row with appendOrUpdate \u2014 never deletes. Updates email_opened, timestamps, and increments open_count.",
      "position": [
        -5232,
        464
      ],
      "parameters": {
        "columns": {
          "value": {
            "opened_at": "={{ $json.opened_at }}",
            "open_count": "={{ $json.open_count }}",
            "email_opened": "TRUE",
            "contact_email": "={{ $json.contact_email }}",
            "last_opened_at": "={{ $json.last_opened_at }}"
          },
          "schema": [
            {
              "id": "contact_email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "contact_email",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "email_opened",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email_opened",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "opened_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "opened_at",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "last_opened_at",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_opened_at",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "open_count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "open_count",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "contact_email"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit#gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "f4346e3f-2a00-44c1-bc04-8462c899af9b",
      "name": "First Time Open?",
      "type": "n8n-nodes-base.if",
      "notes": "Only send Slack alert on FIRST open. Prevents spam alerts when someone opens email multiple times.",
      "position": [
        -5008,
        512
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "first-open",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.is_first_open }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "87715339-13fc-4e3e-92ac-d5f1674a5c54",
      "name": "Slack Alert \u2014 Email Opened",
      "type": "n8n-nodes-base.slack",
      "notes": "Sends Slack alert only on first open. If opened 3+ times \u2192 extra urgent alert to contact immediately.",
      "position": [
        -4784,
        496
      ],
      "parameters": {
        "text": "=\ud83d\udc41\ufe0f *Email Opened!*\n\n\ud83c\udfe2 *Company:* {{ $json.company_name }}\n\ud83d\udce7 *Email:* {{ $json.contact_email }}\n\u23f0 *Opened at:* {{ $json.opened_at }}\n\ud83d\udcca *Open count:* {{ $json.open_count }}\n\n{{ $json.open_count > 2 ? '\ud83d\udd25 *Opened ' + $json.open_count + ' times \u2014 VERY interested!*\\n\ud83d\udc49 Reach out now!' : '\u2705 First open detected \u2014 they saw your email!' }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0BGYBM3MT2",
          "cachedResultName": "dev-team"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "18c931ce-8221-4fb5-bf07-b3f3031619d1",
      "name": "Daily 9AM Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -6256,
        -464
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "524ddb6c-3157-49b2-a7c1-b3bf2f1036cc",
      "name": "Fetch All Leads",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "No filter \u2014 gets ALL rows. Code node below does the filtering.",
      "position": [
        -5952,
        -864
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit#gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "5d855681-3dae-406b-ac55-471deadfc8bb",
      "name": "Filter Pending Only",
      "type": "n8n-nodes-base.code",
      "notes": "Filters pending or empty status rows. Returns ALL matching rows as separate items \u2014 n8n processes each one automatically.",
      "position": [
        -5584,
        -864
      ],
      "parameters": {
        "jsCode": "// Filter only pending rows\nconst items = $input.all();\n\nconst filtered = items.filter(item => {\n  const status = (item.json.status || '').toLowerCase().trim();\n  // Accept pending or empty status\n  return status === 'pending' || status === '';\n});\n\nif (filtered.length === 0) {\n  return [{ json: { no_leads: true } }];\n}\n\n// Return all filtered items \u2014 n8n processes each separately\nreturn filtered.map(item => ({ json: item.json }));"
      },
      "typeVersion": 2
    },
    {
      "id": "6f473082-4a55-48fa-a54b-37060f01db28",
      "name": "Valid Lead?",
      "type": "n8n-nodes-base.if",
      "notes": "Skips rows with missing fields or no_leads flag.",
      "position": [
        -5360,
        -864
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "no-leads",
              "operator": {
                "type": "boolean",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.no_leads }}",
              "rightValue": true
            },
            {
              "id": "has-email",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.contact_email }}",
              "rightValue": ""
            },
            {
              "id": "has-company",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.company_name }}",
              "rightValue": ""
            },
            {
              "id": "has-website",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "e339002a-ad68-4337-a769-f5c00b250d9f",
      "name": "Scrape + Clean Website",
      "type": "n8n-nodes-base.code",
      "notes": "COMBINED NODE \u2014 scrapes website AND cleans content in one step. Fixes the $input.first() issue. Uses $input.first().json which is correct here since each item is processed separately by n8n.",
      "position": [
        -5136,
        -864
      ],
      "parameters": {
        "jsCode": "// ONE NODE: Scrape + Clean + Skip check\n// This fixes the $input.first() vs .item issue\n// by doing everything in one node\n\nconst lead = $input.first().json;\n\n// Clean email (remove spaces)\nconst cleanEmail = (lead.contact_email || '').trim();\n\n// Validate email\nif (!cleanEmail || !cleanEmail.includes('@')) {\n  return [{ json: { skip: true, reason: 'Invalid email: ' + cleanEmail } }];\n}\n\n// Scrape website using ScrapingBee\nlet websiteText = '';\ntry {\n  \n  const scrapeUrl = `https://api.scrapingant.com/v2/general?url=${encodeURIComponent(lead.website)}&x-api-key=f435ff2289f643e582dc359d5576e6b1`;\n\n  \n  const response = await this.helpers.httpRequest({\n    method: 'GET',\n    url: scrapeUrl,\n    timeout: 15000\n  });\n  \n  const rawText = typeof response === 'string'\n    ? response\n    : JSON.stringify(response);\n    \n  websiteText = rawText\n    .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n    .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n    .replace(/<[^>]+>/g, ' ')\n    .replace(/\\s+/g, ' ')\n    .trim()\n    .substring(0, 3000);\n    \n} catch(e) {\n  websiteText = 'Website content not available';\n}\n\nreturn [{\n  json: {\n    company_name: lead.company_name,\n    website: lead.website,\n    contact_email: cleanEmail,\n    your_service: lead.your_service,\n    sender_name: 'Umar',\n    website_content: websiteText,\n    scrape_success: websiteText.length > 50\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "18e4b19e-17e7-4f93-9d8a-c12d71c636eb",
      "name": "Parse + Build Email",
      "type": "n8n-nodes-base.code",
      "notes": "Parses Gemini response. Removes placeholders. Builds HTML email with tracking pixel.",
      "position": [
        -4560,
        -864
      ],
      "parameters": {
        "jsCode": "const geminiResponse = $input.first().json;\nconst rawText = geminiResponse?.candidates?.[0]?.content?.parts?.[0]?.text || '';\nconst leadData = $('Scrape + Clean Website').first().json;\n\nlet emailContent;\ntry {\n  const cleaned = rawText\n    .replace(/```json/g, '')\n    .replace(/```/g, '')\n    .trim();\n  emailContent = JSON.parse(cleaned);\n} catch(e) {\n  emailContent = {\n    subject: `Quick question about ${leadData.company_name}`,\n    body: `Hi,\\n\\nSaw what you're doing at ${leadData.company_name} \u2014 impressive work.\\n\\n${leadData.your_service} could be a strong fit.\\n\\nWorth a 15-min chat?\\n\\n${leadData.sender_name}`,\n    personalization_used: 'Fallback'\n  };\n}\n\n// Remove ALL placeholders \u2014 safety net\nlet cleanBody = (emailContent.body || '')\n  .replace(/\\[Your Name\\]/gi, leadData.sender_name)\n  .replace(/\\[Name\\]/gi, leadData.sender_name)\n  .replace(/\\[name\\]/gi, leadData.sender_name)\n  .replace(/\\[Sender\\]/gi, leadData.sender_name)\n  .replace(/\\[Your name\\]/gi, leadData.sender_name)\n  .replace(/\\[.*?\\]/g, leadData.sender_name);\n\n// Tracking pixel\nconst trackingId = Buffer.from(leadData.contact_email).toString('base64');\nconst pixelUrl = `https://umarimran99999.app.n8n.cloud/webhook/track-open?id=${trackingId}`;\n\n// HTML email with pixel\nconst htmlBody = `<div style=\"font-family:Arial,sans-serif;font-size:14px;line-height:1.8;color:#333;max-width:600px;\">\n${cleanBody.replace(/\\n/g, '<br>')}\n</div>\n<img src=\"${pixelUrl}\" width=\"1\" height=\"1\" style=\"display:none;\" border=\"0\" alt=\"\" />`;\n\nreturn [{\n  json: {\n    ...leadData,\n    email_subject: emailContent.subject || `Quick question \u2014 ${leadData.company_name}`,\n    email_body: htmlBody,\n    tracking_id: trackingId,\n    personalization_used: emailContent.personalization_used || '',\n    generated_at: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f0a6a3e3-b17c-4a4e-aca5-63b02cb7a7f1",
      "name": "Send Cold Email",
      "type": "n8n-nodes-base.gmail",
      "notes": "emailType = html (important for tracking pixel). continueOnFail = true.",
      "position": [
        -4336,
        -864
      ],
      "parameters": {
        "sendTo": "={{ $json.contact_email }}",
        "message": "={{ $json.email_body }}",
        "options": {},
        "subject": "={{ $json.email_subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2,
      "continueOnFail": true
    },
    {
      "id": "f974675d-ca41-4fa2-8129-71021b232fe0",
      "name": "Update Sheet \u2014 Sent",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "appendOrUpdate \u2014 matches by contact_email. Never deletes. Updates status to sent.",
      "position": [
        -4112,
        -864
      ],
      "parameters": {
        "columns": {
          "value": {
            "notes": "={{ $json.personalization_used }}",
            "status": "sent",
            "sent_at": "={{ $now.toISO() }}",
            "open_count": "0",
            "email_opened": "FALSE",
            "contact_email": "={{ $('Parse + Build Email').item.json.contact_email }}",
            "reply_received": "FALSE",
            "follow_up_1_sent": "FALSE",
            "follow_up_2_sent": "FALSE"
          },
          "schema": [
            {
              "id": "company_name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "company_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "website",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "contact_email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "contact_email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "your_service",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "your_service",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sent_at",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "sent_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "follow_up_1_sent",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "follow_up_1_sent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "follow_up_2_sent",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "follow_up_2_sent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reply_received",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "reply_received",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reply_at",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "reply_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reply_snippet",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "reply_snippet",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "notes",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "notes",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email_opened",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email_opened",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "opened_at",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "opened_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_opened_at",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "last_opened_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "open_count",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "open_count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "contact_email"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit#gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "88bc3002-17d2-4d2c-aeb9-3e507f3358c1",
      "name": "Fetch Follow-up 1 Leads",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -5808,
        -480
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit#gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "7dbe76ab-47dc-4783-ac69-c7ed6575322e",
      "name": "Filter FU1 Due",
      "type": "n8n-nodes-base.code",
      "position": [
        -5584,
        -464
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst now = new Date();\nconst threeDaysMs = 3 * 24 * 60 * 60 * 1000;\n\nconst due = items.filter(item => {\n  const row = item.json;\n  const status = (row.status || '').toLowerCase().trim();\n  const fu1 = (row.follow_up_1_sent || '').toString().toLowerCase();\n  const replied = (row.reply_received || '').toString().toLowerCase();\n  \n  if (status !== 'sent') return false;\n  if (fu1 === 'true') return false;\n  if (replied === 'true') return false;\n  if (!row.sent_at) return false;\n  \n  return (now - new Date(row.sent_at)) >= threeDaysMs;\n});\n\nif (due.length === 0) {\n  return [{ json: { no_followups: true } }];\n}\n\nreturn due.map(item => ({ json: item.json }));"
      },
      "typeVersion": 2
    },
    {
      "id": "f10f29d7-6d07-4aeb-9fb7-e241eb74db30",
      "name": "FU1 Valid?",
      "type": "n8n-nodes-base.if",
      "position": [
        -5280,
        -464
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "not-empty",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.contact_email }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "67b9018c-d59e-4f21-b130-114adcbe25f2",
      "name": "Send Follow-up 1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -4976,
        -464
      ],
      "parameters": {
        "sendTo": "={{ $json.contact_email }}",
        "message": "=<div style=\"font-family:Arial,sans-serif;font-size:14px;line-height:1.8;color:#333;\">\nHi,<br><br>\nJust bumping this up in case it got buried.<br><br>\nI reached out a few days ago about <strong>{{ $json.your_service }}</strong> for {{ $json.company_name }}.<br><br>\nWould a quick 15-minute call work this week?<br><br>\nMuhammad Umar \n</div>",
        "options": {},
        "subject": "=Re: Following up \u2014 {{ $json.company_name }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2,
      "continueOnFail": true
    },
    {
      "id": "b53c4a39-73e5-4ad1-b500-dd46c6add8cd",
      "name": "Update Sheet \u2014 FU1 Sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -4640,
        -512
      ],
      "parameters": {
        "columns": {
          "value": {
            "contact_email": "={{ $json.contact_email }}",
            "follow_up_1_sent": "TRUE"
          },
          "schema": [
            {
              "id": "contact_email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "contact_email",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "follow_up_1_sent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "follow_up_1_sent",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "contact_email"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit#gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "1750ff35-71b0-407b-9b16-6e143779d14d",
      "name": "Fetch Follow-up 2 Leads",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -5840,
        -144
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit#gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "09307f1f-af2e-4a28-8dee-ba6e8ceb4387",
      "name": "Filter FU2 Due",
      "type": "n8n-nodes-base.code",
      "position": [
        -5584,
        -128
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst now = new Date();\nconst sevenDaysMs = 7 * 24 * 60 * 60 * 1000;\n\nconst due = items.filter(item => {\n  const row = item.json;\n  const status = (row.status || '').toLowerCase().trim();\n  const fu1 = (row.follow_up_1_sent || '').toString().toLowerCase();\n  const fu2 = (row.follow_up_2_sent || '').toString().toLowerCase();\n  const replied = (row.reply_received || '').toString().toLowerCase();\n  \n  if (status !== 'sent') return false;\n  if (fu1 !== 'true') return false;\n  if (fu2 === 'true') return false;\n  if (replied === 'true') return false;\n  if (!row.sent_at) return false;\n  \n  return (now - new Date(row.sent_at)) >= sevenDaysMs;\n});\n\nif (due.length === 0) {\n  return [{ json: { no_followups: true } }];\n}\n\nreturn due.map(item => ({ json: item.json }));"
      },
      "typeVersion": 2
    },
    {
      "id": "16c49e89-eaeb-40b9-9885-223ba6482263",
      "name": "FU2 Valid?",
      "type": "n8n-nodes-base.if",
      "position": [
        -5344,
        -128
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "not-empty2",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.contact_email }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "6bdd9c2a-cc4f-4077-8d20-ed5ded5f5d44",
      "name": "Send Follow-up 2",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -4960,
        -160
      ],
      "parameters": {
        "sendTo": "={{ $json.contact_email }}",
        "message": "=<div style=\"font-family:Arial,sans-serif;font-size:14px;line-height:1.8;color:#333;\">\nHi,<br><br>\nThis is my last email \u2014 I don't want to fill your inbox.<br><br>\nIf the timing isn't right, no worries. But if you're open to seeing how <strong>{{ $json.your_service }}</strong> could help {{ $json.company_name }}, I'm one reply away.<br><br>\nWishing you all the best.<br><br>\nMuhammed Umar\n</div>",
        "options": {},
        "subject": "=Last email \u2014 {{ $json.company_name }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2,
      "continueOnFail": true
    },
    {
      "id": "54f98472-72b3-4487-b226-37a768ab947b",
      "name": "Update Sheet \u2014 Complete",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -4608,
        -224
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "sequence_complete",
            "contact_email": "={{ $json.contact_email }}",
            "follow_up_2_sent": "TRUE"
          },
          "schema": [
            {
              "id": "contact_email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "contact_email",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "follow_up_2_sent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "follow_up_2_sent",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "contact_email"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit#gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1CltzBmVS3tzekPISgTkvZ5pJCSwqpKw5i0aFUvO-cN4/edit?usp=drivesdk",
          "cachedResultName": "AutomateLeads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "df2bad0d-25ec-458c-9862-4bf08e99c378",
      "name": "Google Gemini Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -4848,
        -736
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "e905d801-1330-4343-986c-5fff971b8062",
      "name": "LLM Interaction Chain1",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -4880,
        -864
      ],
      "parameters": {
        "text": "={   \"contents\": [{     \"parts\": [{       \"text\": \"You are an expert cold email copywriter. Write a highly personalized cold email based on the information below.\\n\\nCompany Name: {{ $json.company_name }}\\nCompany Website: {{ $json.website }}\\nWebsite Content: {{ $json.website_content }}\\nService Being Offered: {{ $json.your_service }}\\n\\nRules:\\n1. DO NOT start with 'I hope this email finds you well' or generic openers\\n2. Reference something SPECIFIC about their business from the website\\n3. Connect their pain point to the service being offered\\n4. Keep it under 150 words\\n5. End with a soft CTA (15 min call or quick question)\\n6. Sound human, not robotic or salesy\\n7. Subject line must be curiosity-inducing, under 8 words\\n\\nReturn ONLY valid JSON (no markdown, no explanation):\\n{\\n  \\\"subject\\\": \\\"email subject line here\\\",\\n  \\\"body\\\": \\\"full email body here (use \\\\n for line breaks)\\\",\\n  \\\"personalization_used\\\": \\\"what specific thing from their website you referenced\\\"\\n}\"     }]   }],   \"generationConfig\": {     \"temperature\": 0.7,     \"maxOutputTokens\": 1000   } }",
        "batching": {},
        "messages": {
          "messageValues": []
        },
        "promptType": "define"
      },
      "typeVersion": 1.9
    },
    {
      "id": "875e100a-1f05-4c9a-8fd6-87d4f43a1057",
      "name": "BG \u2014 Section A",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6176,
        -992
      ],
      "parameters": {
        "color": 5,
        "width": 2300,
        "height": 340,
        "content": "## Section A \u2014 Main Email Flow\nFetches pending leads daily at 9AM \u2192 scrapes website \u2192 Gemini writes personalized email \u2192 sends via Gmail \u2192 updates sheet"
      },
      "typeVersion": 1
    },
    {
      "id": "94a63407-080c-478a-b572-0c677c16bf14",
      "name": "BG \u2014 LLM Chain",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4976,
        -992
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 344,
        "content": "## AI Email Writer\nGemini generates personalized cold email from scraped website content"
      },
      "typeVersion": 1
    },
    {
      "id": "9dd1137e-5ecc-4d65-99f5-588fcb56eec3",
      "name": "BG \u2014 Follow-up 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5888,
        -608
      ],
      "parameters": {
        "color": 4,
        "width": 1464,
        "height": 288,
        "content": "## Section B \u2014 Follow-up 1 (Day 3)\nChecks if 3 days passed since email sent \u2192 sends gentle bump \u2192 marks follow_up_1_sent = TRUE"
      },
      "typeVersion": 1
    },
    {
      "id": "41f00c90-048d-437d-a42f-e020ea4cd619",
      "name": "BG \u2014 Follow-up 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5888,
        -288
      ],
      "parameters": {
        "color": 4,
        "width": 1480,
        "height": 320,
        "content": "## Section B \u2014 Follow-up 2 (Day 7 \u2014 Final)\nChecks if 7 days passed \u2192 sends last chance email \u2192 marks status = sequence_complete"
      },
      "typeVersion": 1
    },
    {
      "id": "0d8bcd64-e6bf-46a8-9574-963678070c8a",
      "name": "BG \u2014 Open Tracker",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6432,
        208
      ],
      "parameters": {
        "color": 6,
        "width": 1860,
        "height": 444,
        "content": "## Section D \u2014 Email Open Tracker\nTracking pixel webhook \u2192 decodes base64 email \u2192 increments open_count in sheet \u2192 Slack alert on first open"
      },
      "typeVersion": 1
    },
    {
      "id": "2c6b0ee0-cd93-48d8-b41a-cf93c4393490",
      "name": "BG \u2014 Reply Detection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6432,
        688
      ],
      "parameters": {
        "color": 3,
        "width": 1660,
        "height": 376,
        "content": "## Section C \u2014 Reply Detection\nGmail trigger watches inbox every minute \u2192 matches sender to lead \u2192 marks replied \u2192 stops follow-up \u2192 Slack HOT LEAD alert"
      },
      "typeVersion": 1
    },
    {
      "id": "6f3a6b77-ae75-437b-aec5-1cfe1e558c82",
      "name": "Sticky Note \u2014 Master",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6688,
        -1456
      ],
      "parameters": {
        "color": 5,
        "width": 400,
        "height": 400,
        "content": "# AI Cold Email Machine\n\n**4 sections working together:**\nA = Send emails (9AM daily)\nB = Follow-ups (Day 3 + Day 7)\nC = Reply detection (every minute)\nD = Open tracking (pixel webhook)\n\n**Google Sheet:** AutomateLeads > leads\n**Status lifecycle:**\npending \u2192 sent \u2192 sequence_complete\n                \u2192 replied"
      },
      "typeVersion": 1
    },
    {
      "id": "feea1814-89ae-49b3-b2ef-c92446c9ca37",
      "name": "Sticky Note \u2014 Setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6192,
        -1568
      ],
      "parameters": {
        "color": 2,
        "width": 380,
        "height": 480,
        "content": "## Setup Checklist\n\n\u2705 Gmail OAuth2 connected\n\u2705 Google Sheets OAuth2 connected\n\u2705 Slack OAuth2 connected\n\u2705 Gemini PaLM API connected\n\u2705 ScrapingAnt key in Scrape node\n\n**Sheet columns:**\ncompany_name, website, contact_email\nyour_service, status, sent_at\nfollow_up_1_sent, follow_up_2_sent\nreply_received, reply_at, reply_snippet\nnotes, email_opened, opened_at\nlast_opened_at, open_count\n\n\u26a0\ufe0f All 3 workflows must be Active"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "8ebf32a8-0a82-4647-bb86-9ac804ab2f1a",
  "nodeGroups": [],
  "connections": {
    "FU1 Valid?": {
      "main": [
        [
          {
            "node": "Send Follow-up 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FU2 Valid?": {
      "main": [
        [
          {
            "node": "Send Follow-up 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Valid Lead?": {
      "main": [
        [
          {
            "node": "Scrape + Clean Website",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Valid Email?": {
      "main": [
        [
          {
            "node": "Find Lead Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Lead Row": {
      "main": [
        [
          {
            "node": "Prepare Update Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter FU1 Due": {
      "main": [
        [
          {
            "node": "FU1 Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter FU2 Due": {
      "main": [
        [
          {
            "node": "FU2 Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch All Leads": {
      "main": [
        [
          {
            "node": "Filter Pending Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Cold Email": {
      "main": [
        [
          {
            "node": "Update Sheet \u2014 Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "First Time Open?": {
      "main": [
        [
          {
            "node": "Slack Alert \u2014 Email Opened",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Follow-up 1": {
      "main": [
        [
          {
            "node": "Update Sheet \u2014 FU1 Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Follow-up 2": {
      "main": [
        [
          {
            "node": "Update Sheet \u2014 Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily 9AM Trigger": {
      "main": [
        [
          {
            "node": "Fetch All Leads",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Follow-up 1 Leads",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Follow-up 2 Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decode Tracking ID": {
      "main": [
        [
          {
            "node": "Valid Email?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Pending Only": {
      "main": [
        [
          {
            "node": "Valid Lead?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse + Build Email": {
      "main": [
        [
          {
            "node": "Send Cold Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Update Data": {
      "main": [
        [
          {
            "node": "Update Sheet \u2014 Email Opened",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Cold Email Reply": {
      "main": [
        [
          {
            "node": "Merge Lead and Email Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM Interaction Chain1": {
      "main": [
        [
          {
            "node": "Parse + Build Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Leads from Sheets": {
      "main": [
        [
          {
            "node": "Check Cold Email Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape + Clean Website": {
      "main": [
        [
          {
            "node": "LLM Interaction Chain1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tracking Pixel Webhook": {
      "main": [
        [
          {
            "node": "Return Pixel Image",
            "type": "main",
            "index": 0
          },
          {
            "node": "Decode Tracking ID",
            "t

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

This workflow runs daily to send Gemini-generated cold emails from leads stored in Google Sheets via Gmail, schedules two follow-ups based on time since sending, and continuously monitors Gmail for replies to update the sheet and alert a Slack channel. Runs every day at 9AM and…

Source: https://n8n.io/workflows/17488/ — 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 monitors Gmail for unread invoice emails with PDF attachments, extracts invoice details with Google Gemini, verifies Japanese qualified invoice registration numbers against the National

Gmail Trigger, Chain Llm, Google Gemini Chat +5
AI & RAG

This workflow monitors Gmail for unread emails with PDF attachments, uses Google Gemini to extract Japan Electronic Books Preservation Act filing metadata, logs a searchable index in Google Sheets, an

Gmail Trigger, Chain Llm, Google Gemini Chat +5
AI & RAG

Automate your lead intake, scoring, and outreach pipeline. This workflow collects leads from forms, enriches and scores them using Relevance AI, routes them by quality, and triggers the right follow-u

Form Trigger, HTTP Request, Chain Llm +6
AI & RAG

This workflow creates a sophisticated, self-improving customer support system that automatically handles incoming emails. It's designed to answer common questions using an AI-powered knowledge base an

Text Classifier, Chain Llm, Gmail Trigger +4
AI & RAG

Perfect for use cases such as: 💼 Finance teams managing vendor invoices 📊 Bookkeeping workflows 🔄 Automating monthly reconciliation

Output Parser Structured, HTTP Request, Gmail Trigger +4