{
  "id": "SkOA5njFb0ZAvaSy",
  "name": "AI SDR \u2014 Fully Automated Cold Outreach with Scoring and Follow-ups",
  "tags": [],
  "nodes": [
    {
      "id": "ff288d70-ed51-4639-a4fc-cd9f84c57e73",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "width": 620,
        "height": 860,
        "content": "## AI SDR \u2014 Fully Automated Cold Outreach with Scoring and Follow-ups\nAutomate your entire cold email pipeline: lead research, AI scoring, personalized email generation, sending, follow-ups, and reply detection \u2014 all driven from a Google Sheet.\n\n### How it works\n1. **New Lead Processing (8 AM weekdays):** Reads new leads from Google Sheets, scrapes their website, builds a research dossier, scores them with AI, and generates 3 personalized emails.\n2. **Email 1 sent immediately** for leads scoring 40+. Low-fit leads are marked as skipped.\n3. **Follow-up engine (every 2hrs weekdays):** Sends Email 2 after 3 days, Email 3 after 7 days.\n4. **Reply detection (every 2hrs weekdays):** Searches Gmail for replies from active leads. If found, marks the lead as \"replied\" and stops the sequence.\n5. **Telegram notifications** sent at every step \u2014 emails sent, leads skipped, and replies detected.\n\n### Setup steps\n1. Connect your **Google Sheets** credential and point to your lead sheet (columns: Lead Name, Email, Company, Website, Role/Title, Status, Reply Date, Reply Subject, Reply Snippet).\n2. Connect your **Gmail** credential (OAuth2).\n3. Connect your **Telegram** credential and set `YOUR_TELEGRAM_CHAT_ID` in each Telegram node.\n4. Connect your **Ollama** credential (or swap for OpenAI/Anthropic).\n5. Edit the **AI Lead Scorer** system prompt with your product, ICP, and scoring criteria.\n6. Edit the **AI Email Writer** system prompt with your name, company, and value prop.\n7. Set your sender name in the **Extract Emails** code node (line 12) and **Find Follow-ups** code node (line 9).\n8. Set your Gmail address in the **Filter Active Leads** code node (line 10) and **Check Reply Results** code node (line 10).\n\n### Customization\n- Adjust the scoring threshold (default 40) in the **Extract Score** node.\n- Change follow-up timing (default 3 and 7 days) in the **Find Follow-ups** node.\n- Modify cron schedules on any trigger to match your timezone and preferences."
      },
      "typeVersion": 1
    },
    {
      "id": "e6bd6e84-b8d3-4233-a3ba-a9f1e84abae2",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1104,
        640
      ],
      "parameters": {
        "color": 6,
        "width": 2260,
        "height": 380,
        "content": "## \ud83d\udd2c Research, Score & Send\nScrapes lead websites, builds research dossiers, scores with AI, generates 3 personalized emails, and sends Email 1 immediately."
      },
      "typeVersion": 1
    },
    {
      "id": "90cd7117-dd95-4f9a-b230-d6f9ddef14c8",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        912,
        1104
      ],
      "parameters": {
        "color": 6,
        "width": 1260,
        "height": 420,
        "content": "## \ud83d\udce7 Follow-up Engine\nSends Email 2 after 3 days and Email 3 after 7 days. Skips leads that have already replied."
      },
      "typeVersion": 1
    },
    {
      "id": "3be24aab-7a78-45c3-b88f-c602bcbefa28",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        912,
        1584
      ],
      "parameters": {
        "color": 6,
        "width": 1304,
        "height": 420,
        "content": "## \ud83d\udcec Reply Detection\nSearches Gmail for replies from active leads. Marks them as \"replied\" and stops the email sequence automatically."
      },
      "typeVersion": 1
    },
    {
      "id": "0ec61a66-98cf-4a44-84d9-37b22611b70e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2608,
        1904
      ],
      "parameters": {
        "color": 3,
        "width": 460,
        "content": "## \u26a0\ufe0f Set your Gmail address\nEdit line 10 in both **Filter Active Leads** and **Check Reply Results** code nodes \u2014 replace `your-email@gmail.com` with your actual sending Gmail address. This prevents your own outbound emails from being detected as replies."
      },
      "typeVersion": 1
    },
    {
      "id": "b43da6bf-e5a3-480e-953d-54d135e68bee",
      "name": "\u23f0 8 AM \u2014 New Leads",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        928,
        752
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "00d418c1-2115-466b-9e5e-190b83333889",
      "name": "\ud83d\udccb Read Sheet (New)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1152,
        752
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "53f04d54-e0be-4437-8e4b-37bc17257e32",
      "name": "\ud83c\udd95 Filter New",
      "type": "n8n-nodes-base.code",
      "position": [
        1376,
        752
      ],
      "parameters": {
        "jsCode": "const allRows = $input.all();\nconst newLeads = [];\n\nfor (let i = 0; i < allRows.length; i++) {\n  const r = allRows[i].json;\n  const status = (r.Status || r.status || '').toLowerCase().trim();\n  const name = r['Lead Name'] || r.leadName || '';\n  const email = r.Email || r.email || '';\n  const website = r.Website || r.website || '';\n\n  if (status === 'new' && name && email) {\n    newLeads.push({\n      json: {\n        leadName: name,\n        company: r.Company || r.company || '',\n        website: website,\n        role: r['Role/Title'] || r.role || r.title || '',\n        email: email,\n        linkedinUrl: r['LinkedIn URL'] || r.linkedinUrl || '',\n        rowNumber: i + 2\n      }\n    });\n  }\n}\n\nif (newLeads.length === 0) {\n  return [{ json: { hasNewLeads: false } }];\n}\n\nreturn newLeads;"
      },
      "typeVersion": 2
    },
    {
      "id": "6d454456-c5c1-4060-8006-c1ec1daeb7a5",
      "name": "\ud83c\udd95 Has Leads?",
      "type": "n8n-nodes-base.if",
      "position": [
        1600,
        752
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-lead",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.leadName }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "77fc3456-e21c-4b0e-9434-d7a6a1e500cf",
      "name": "\ud83c\udf10 Scrape Website",
      "type": "n8n-nodes-base.code",
      "position": [
        1824,
        656
      ],
      "parameters": {
        "jsCode": "const lead = $json;\nlet research = { companyInfo: '', scrapedSuccessfully: false };\n\nif (!lead.website) {\n  research.companyInfo = `${lead.company} \u2014 no website provided. Role: ${lead.role}.`;\n  return { json: { ...lead, research } };\n}\n\ntry {\n  let url = lead.website;\n  if (!url.startsWith('http')) url = 'https://' + url;\n\n  const response = await this.helpers.httpRequest({\n    method: 'GET',\n    url: url,\n    timeout: 15000,\n    headers: { 'User-Agent': 'Mozilla/5.0 (compatible; research-bot)' },\n    returnFullResponse: true,\n    ignoreHttpStatusErrors: true\n  });\n\n  if (response && response.body) {\n    let html = typeof response.body === 'string' ? response.body : JSON.stringify(response.body);\n\n    const titleMatch = html.match(/<title[^>]*>(.*?)<\\/title>/i);\n    const title = titleMatch ? titleMatch[1].trim() : '';\n\n    const metaMatch = html.match(/<meta[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']*)[\"']/i);\n    const metaDesc = metaMatch ? metaMatch[1].trim() : '';\n\n    html = html.replace(/<script[\\s\\S]*?<\\/script>/gi, ' ');\n    html = html.replace(/<style[\\s\\S]*?<\\/style>/gi, ' ');\n    html = html.replace(/<nav[\\s\\S]*?<\\/nav>/gi, ' ');\n    html = html.replace(/<footer[\\s\\S]*?<\\/footer>/gi, ' ');\n    html = html.replace(/<[^>]+>/g, ' ');\n    html = html.replace(/&nbsp;|&amp;|&lt;|&gt;/g, ' ');\n    html = html.replace(/\\s+/g, ' ').trim();\n\n    research.companyInfo = `Title: ${title}\\nDescription: ${metaDesc}\\nContent: ${html.substring(0, 1500)}`;\n    research.scrapedSuccessfully = true;\n  }\n} catch (e) {\n  research.companyInfo = `Could not scrape ${lead.website}. Company: ${lead.company}, Role: ${lead.role}.`;\n}\n\nreturn { json: { ...lead, research } };"
      },
      "typeVersion": 2
    },
    {
      "id": "a407e7ad-a48a-49f2-8c4c-b867a9dc2b51",
      "name": "\ud83d\udcc4 Build Dossier",
      "type": "n8n-nodes-base.code",
      "position": [
        2048,
        656
      ],
      "parameters": {
        "jsCode": "const lead = $json;\nconst r = lead.research || {};\n\nconst dossier = `=== LEAD DOSSIER ===\nPerson: ${lead.leadName}\nRole: ${lead.role}\nCompany: ${lead.company}\nWebsite: ${lead.website || 'N/A'}\nEmail: ${lead.email}\n\n--- Company Research ---\n${r.companyInfo || 'No info available.'}\n\nScraped: ${r.scrapedSuccessfully ? 'Yes' : 'No'}`;\n\nreturn { json: { ...lead, dossier } };"
      },
      "typeVersion": 2
    },
    {
      "id": "975ad2ef-3697-451e-b7cc-4b84795a5ae4",
      "name": "\ud83d\udcca AI Lead Scorer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2272,
        656
      ],
      "parameters": {
        "text": "=Score this lead for sales outreach:\n\n{{ $json.dossier }}\n\nScore 0-100.",
        "options": {
          "systemMessage": "You are a sales intelligence analyst.\n\n=== YOUR PRODUCT (EDIT THIS) ===\nProduct: [YOUR PRODUCT NAME]\nWhat it does: [WHAT IT DOES]\nIdeal customer: [YOUR ICP]\nProblems solved: [KEY PROBLEMS]\n\n=== SCORING ===\n1. Company-Solution Fit (0-30): Do they have our problem?\n2. Role Fit (0-25): Can they buy? (C-level=high, IC=low)\n3. Timing Signals (0-25): Hiring? Funded? Growing?\n4. Engagement Potential (0-20): Will they respond?\n\nRAW JSON ONLY. No markdown fences.\n{\"totalScore\":0,\"companySolutionFit\":{\"score\":0,\"reason\":\"\"},\"roleFit\":{\"score\":0,\"reason\":\"\"},\"timingSignals\":{\"score\":0,\"reason\":\"\"},\"engagementPotential\":{\"score\":0,\"reason\":\"\"},\"overallAssessment\":\"\",\"recommendation\":\"pursue\",\"keyInsight\":\"\"}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.1
    },
    {
      "id": "7454887b-5a92-4d21-b927-f8124a31eb27",
      "name": "\ud83c\udfaf Extract Score",
      "type": "n8n-nodes-base.code",
      "position": [
        2624,
        656
      ],
      "parameters": {
        "jsCode": "const lead = $('\ud83d\udcc4 Build Dossier').item.json;\nconst raw = $json;\nlet score = {};\n\nfunction tryParse(str) {\n  if (typeof str !== 'string') return null;\n  str = str.replace(/```json\\s*/gi, '').replace(/```\\s*/g, '').trim();\n  try { return JSON.parse(str); } catch(e) {\n    const m = str.match(/\\{[\\s\\S]*\"totalScore\"[\\s\\S]*\\}/);\n    if (m) try { return JSON.parse(m[0]); } catch(e2) {}\n    return null;\n  }\n}\n\nfunction dig(obj, d) {\n  if (d > 5 || !obj || typeof obj !== 'object') return null;\n  if (obj.totalScore !== undefined) return obj;\n  for (const k of ['output','text','message','content','response','result','data','kwargs','lc_kwargs']) {\n    if (obj[k]) {\n      if (typeof obj[k] === 'string') { const r = tryParse(obj[k]); if (r) return r; }\n      else { const r = dig(obj[k], d+1); if (r) return r; }\n    }\n  }\n  for (const k of Object.keys(obj)) {\n    if (typeof obj[k] === 'string' && obj[k].includes('totalScore')) {\n      const r = tryParse(obj[k]); if (r) return r;\n    } else if (typeof obj[k] === 'object' && obj[k]) {\n      const r = dig(obj[k], d+1); if (r) return r;\n    }\n  }\n  return null;\n}\n\ntry {\n  if (raw && raw.totalScore !== undefined) score = raw;\n  else { const f = dig(raw, 0); if (f) score = f; }\n  if (score.totalScore === undefined) {\n    const p = tryParse(JSON.stringify(raw));\n    if (p) score = p;\n  }\n} catch(e) {}\n\nif (score.totalScore === undefined) {\n  score = { totalScore: 50, recommendation: 'pursue', overallAssessment: 'AI parse failed \u2014 default score.', keyInsight: 'Review manually' };\n}\n\nreturn { json: {\n  ...lead,\n  score: score.totalScore,\n  recommendation: score.recommendation || (score.totalScore >= 40 ? 'pursue' : 'skip'),\n  keyInsight: score.keyInsight || '',\n  overallAssessment: score.overallAssessment || '',\n  shouldPursue: score.totalScore >= 40\n}};"
      },
      "typeVersion": 2
    },
    {
      "id": "268c6d25-e5fb-4157-bb26-6a13c235a14a",
      "name": "\ud83d\udcca Pursue?",
      "type": "n8n-nodes-base.if",
      "position": [
        2848,
        656
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "pursue",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.shouldPursue }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e84960a8-3d0b-484d-a436-3d37174a8c5e",
      "name": "\u270d\ufe0f AI Email Writer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        3072,
        400
      ],
      "parameters": {
        "text": "=Write 3 cold outreach emails for:\n\n{{ $json.dossier }}\n\nScore: {{ $json.score }}/100\nKey Insight: {{ $json.keyInsight }}\nAssessment: {{ $json.overallAssessment }}",
        "options": {
          "systemMessage": "You are writing cold outreach emails.\n\n=== SENDER INFO (EDIT THIS) ===\n[YOUR NAME]: Your full name\n[YOUR COMPANY]: Your company name\n[WHAT YOU SELL]: One-line description of your product\n[#1 BENEFIT]: The main benefit your customers get\n[ONE SPECIFIC RESULT]: A concrete result you've achieved for a customer\n\n=== STRICT FORMATTING RULES ===\n\nEVERY email must follow this EXACT structure:\n\nLine 1: Greeting (Hi [FirstName],)\nLine 2: (blank line)\nLines 3-8: Body (4-6 short sentences)\nLine N-1: (blank line)\nLine N: Sign-off (Best, [YOUR NAME])\n\nFORMATTING:\n- Use real line breaks between paragraphs\n- NO markdown (no **, no ##, no bullets)\n- NO \"Subject:\" in the body\n- Keep sentences SHORT (under 20 words each)\n- One idea per paragraph\n- Max 6 sentences in body\n- Sign off with: Best, [YOUR NAME]\n\nCONTENT RULES:\n- Reference something SPECIFIC from their company research\n- Mention their role naturally\n- Email 1: Lead with THEIR world, not yours\n- Email 2: Share a different angle or result (never say \"following up\")\n- Email 3: Be human, make it easy to say no\n- Sound like a PERSON, not a salesperson\n- NO buzzwords (leverage, synergy, revolutionary, game-changing)\n- NO \"I hope this email finds you well\"\n- NO \"I came across your company\"\n\n=== SUBJECT LINE RULES ===\n- 4-7 words only\n- Lowercase (except first word and proper nouns)\n- No clickbait, no ALL CAPS\n- Create curiosity, not hype\n\n=== OUTPUT FORMAT ===\nRAW JSON ONLY. No markdown fences. No explanation.\n\n{\"email1\":{\"subject\":\"subject here\",\"body\":\"Hi FirstName,\\n\\nFirst paragraph.\\n\\nSecond paragraph.\\n\\nCTA paragraph.\\n\\nBest,\\n[YOUR NAME]\"},\"email2\":{\"subject\":\"different subject\",\"body\":\"Hi FirstName,\\n\\nDifferent angle.\\n\\nProof or insight.\\n\\nSoft CTA.\\n\\nBest,\\n[YOUR NAME]\"},\"email3\":{\"subject\":\"last note subject\",\"body\":\"Hi FirstName,\\n\\nAcknowledge outreach.\\n\\nGenuine value.\\n\\nEasy to say no.\\n\\nCheers,\\n[YOUR NAME]\"},\"personalizationUsed\":[\"specific thing 1\",\"specific thing 2\"]}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.1
    },
    {
      "id": "b51ecaf0-3369-41ed-b2b5-d1a8fbddf1cf",
      "name": "\ud83d\udcdd Extract Emails",
      "type": "n8n-nodes-base.code",
      "position": [
        3424,
        512
      ],
      "parameters": {
        "jsCode": "const lead = $('\ud83d\udcca Pursue?').item.json;\nconst raw = $json;\nlet emails = {};\n\n// \u26a0\ufe0f CHANGE THIS to your name\nconst senderName = 'YOUR_NAME';\nconst firstName = lead.leadName.split(' ')[0];\n\nfunction tryParse(str) {\n  if (typeof str !== 'string') return null;\n  str = str.replace(/```json\\s*/gi, '').replace(/```\\s*/g, '').trim();\n  try { return JSON.parse(str); } catch(e) {\n    const m = str.match(/\\{[\\s\\S]*\"email1\"[\\s\\S]*\\}/);\n    if (m) try { return JSON.parse(m[0]); } catch(e2) {}\n    return null;\n  }\n}\n\nfunction dig(obj, d) {\n  if (d > 5 || !obj || typeof obj !== 'object') return null;\n  if (obj.email1 && typeof obj.email1 === 'object') return obj;\n  for (const k of ['output','text','message','content','response','result','data','kwargs','lc_kwargs']) {\n    if (obj[k]) {\n      if (typeof obj[k] === 'string') { const r = tryParse(obj[k]); if (r && r.email1) return r; }\n      else { const r = dig(obj[k], d+1); if (r) return r; }\n    }\n  }\n  for (const k of Object.keys(obj)) {\n    if (typeof obj[k] === 'string' && obj[k].includes('email1')) { const r = tryParse(obj[k]); if (r && r.email1) return r; }\n    else if (typeof obj[k] === 'object' && obj[k]) { const r = dig(obj[k], d+1); if (r) return r; }\n  }\n  return null;\n}\n\ntry {\n  if (raw && raw.email1) emails = raw;\n  else { const f = dig(raw, 0); if (f) emails = f; }\n  if (!emails.email1) { const p = tryParse(JSON.stringify(raw)); if (p && p.email1) emails = p; }\n} catch(e) {}\n\nif (!emails.email1) {\n  emails = {\n    email1: { subject: `Quick question about ${lead.company}`, body: `Hi ${firstName},\\n\\nI noticed ${lead.company} and wanted to reach out.\\n\\nWould love to share something relevant to your work as ${lead.role}.\\n\\nWorth a quick chat?\\n\\nBest,\\n${senderName}` },\n    email2: { subject: `Thought of ${lead.company}`, body: `Hi ${firstName},\\n\\nI had another thought about how we might help ${lead.company}.\\n\\nWe recently helped a similar company solve a challenge you might relate to.\\n\\nWould it make sense to share the details?\\n\\nBest,\\n${senderName}` },\n    email3: { subject: `Last note from me`, body: `Hi ${firstName},\\n\\nI know you're busy, so I'll keep this brief.\\n\\nIf the timing isn't right, totally understand.\\n\\nWishing you and ${lead.company} all the best.\\n\\nCheers,\\n${senderName}` },\n    personalizationUsed: ['company name only \u2014 AI parse failed']\n  };\n}\n\nfunction formatEmailBody(rawBody) {\n  if (!rawBody) return '';\n  let body = rawBody;\n  body = body.replace(/\\\\n/g, '\\n');\n  body = body.replace(/\\\\\\\\n/g, '\\n');\n  body = body.replace(/^Subject:.*?\\n\\n?/i, '');\n  body = body.replace(/^Subject:.*?\\n/i, '');\n  body = body.replace(/\\*\\*/g, '');\n  body = body.replace(/^#+\\s/gm, '');\n  body = body.replace(/\\[YOUR NAME\\]/gi, senderName);\n  body = body.replace(/\\[Your Name\\]/g, senderName);\n  body = body.replace(/YourName/g, senderName);\n  body = body.replace(/\\n\\n?(Best|Cheers|Thanks|Thank you|Regards|Kind regards|Warm regards|Sincerely|Talk soon|All the best),?\\s*\\n\\s*[A-Z][a-zA-Z]*(\\s+[A-Z][a-zA-Z]*)?\\s*(\\n\\n?(Best|Cheers|Thanks|Thank you|Regards|Kind regards|Warm regards|Sincerely|Talk soon|All the best),?\\s*\\n\\s*(\\[YOUR NAME\\]|YourName|[A-Z][a-zA-Z]*(\\s+[A-Z][a-zA-Z]*)?))?$/i, '');\n  body = body.replace(/\\n\\n?(Best|Cheers|Thanks|Regards|Sincerely|Talk soon),?\\s*$/i, '');\n  body = body.trimEnd();\n  if (!/^(Hi|Hey|Hello|Dear|Good)\\s/i.test(body.trim())) {\n    body = `Hi ${firstName},\\n\\n${body}`;\n  }\n  body = body + `\\n\\nBest,\\n${senderName}`;\n  body = body.replace(/\\n{4,}/g, '\\n\\n\\n').trim();\n  return body;\n}\n\nfunction formatSubject(rawSubject, emailNumber) {\n  if (!rawSubject) return `Following up \u2014 ${lead.company}`;\n  let subject = rawSubject;\n  subject = subject.replace(/^[\"']|[\"']$/g, '');\n  subject = subject.replace(/^Subject:\\s*/i, '');\n  subject = subject.replace(/\\[YOUR NAME\\]/gi, senderName);\n  if (emailNumber > 1 && !/^Re:/i.test(subject)) subject = 'Re: ' + subject;\n  subject = subject.replace(/^(Re:\\s*)+/i, 'Re: ');\n  return subject.trim();\n}\n\nfunction toHTML(plainText) {\n  let html = plainText.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\\n\\n/g, '</p><p>').replace(/\\n/g, '<br>');\n  return '<div style=\"font-family: Arial, sans-serif; font-size: 14px; color: #333; line-height: 1.6;\"><p>' + html + '</p></div>';\n}\n\nconst email1Subject = formatSubject(emails.email1.subject, 1);\nconst email1Body = formatEmailBody(emails.email1.body);\nconst email2Subject = formatSubject(emails.email2.subject || emails.email1.subject, 2);\nconst email2Body = formatEmailBody(emails.email2.body);\nconst email3Subject = formatSubject(emails.email3.subject || emails.email1.subject, 3);\nconst email3Body = formatEmailBody(emails.email3.body);\n\nreturn { json: {\n  leadName: lead.leadName, company: lead.company, email: lead.email,\n  role: lead.role, website: lead.website, score: lead.score,\n  keyInsight: lead.keyInsight, overallAssessment: lead.overallAssessment,\n  rowNumber: lead.rowNumber,\n  email1Subject, email1Body, email1HTML: toHTML(email1Body),\n  email2Subject, email2Body, email2HTML: toHTML(email2Body),\n  email3Subject, email3Body, email3HTML: toHTML(email3Body),\n  email1Full: 'Subject: ' + email1Subject + '\\n\\n' + email1Body,\n  email2Full: 'Subject: ' + email2Subject + '\\n\\n' + email2Body,\n  email3Full: 'Subject: ' + email3Subject + '\\n\\n' + email3Body,\n  personalizationUsed: Array.isArray(emails.personalizationUsed) ? emails.personalizationUsed.join(', ') : ''\n}};"
      },
      "typeVersion": 2
    },
    {
      "id": "6f9bc1bd-d6da-4415-9326-130018f64386",
      "name": "\ud83d\udce7 Send Email 1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3648,
        512
      ],
      "parameters": {
        "sendTo": "={{ $json.email }}",
        "message": "={{ $json.email1HTML }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $json.email1Subject }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "7be872d9-8aef-43fe-b79c-aa848628fef9",
      "name": "\ud83d\udcbe Save + Mark Sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3872,
        608
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "email_1_sent",
            "Lead Name": "={{ $('\ud83d\udcdd Extract Emails').item.json.leadName }}",
            "email 3 sub": "={{ $('\ud83d\udcdd Extract Emails').item.json.email3Subject }}",
            "email 1 body": "={{ $('\ud83d\udcdd Extract Emails').item.json.email1Body }}",
            "email 2 body": "={{ $('\ud83d\udcdd Extract Emails').item.json.email2Body }}",
            "email 3 body": "={{ $('\ud83d\udcdd Extract Emails').item.json.email3Body }}",
            "email 1subject": "={{ $('\ud83d\udcdd Extract Emails').item.json.email1Subject }}",
            "email 2 subject": "={{ $('\ud83d\udcdd Extract Emails').item.json.email2Subject }}",
            "email 1 body html": "={{ $('\ud83d\udcdd Extract Emails').item.json.email1HTML }}",
            "email 1 sent date": "={{ new Date().toISOString() }}",
            "email 2 body html": "={{ $('\ud83d\udcdd Extract Emails').item.json.email2HTML }}",
            "email 3 body html": "={{ $('\ud83d\udcdd Extract Emails').item.json.email3HTML }}"
          },
          "schema": [
            {
              "id": "Lead Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Lead Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 1subject",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 1subject",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 1 body",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 1 body",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 1 sent date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 1 sent date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 1 body html",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 1 body html",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 2 subject",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 2 subject",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 2 body",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 2 body",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 2 body html",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 2 body html",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 3 sub",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 3 sub",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 3 body",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 3 body",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 3 body html",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 3 body html",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Lead Name"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "1441a932-a034-4196-b322-ac6272e7af10",
      "name": "\ud83d\udcf2 Email 1 Sent \u2705",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3872,
        416
      ],
      "parameters": {
        "text": "=\ud83d\ude80 *Email 1 Sent \u2014 Auto-SDR*\n\n\ud83d\udc64 {{ $('\ud83d\udcdd Extract Emails').item.json.leadName }}\n\ud83c\udfe2 {{ $('\ud83d\udcdd Extract Emails').item.json.company }} ({{ $('\ud83d\udcdd Extract Emails').item.json.role }})\n\ud83d\udcca Score: {{ $('\ud83d\udcdd Extract Emails').item.json.score }}/100\n\ud83d\udce7 To: {{ $('\ud83d\udcdd Extract Emails').item.json.email }}\n\ud83d\udcdd Subject: {{ $('\ud83d\udcdd Extract Emails').item.json.email1Subject }}\n\n\ud83d\udca1 {{ $('\ud83d\udcdd Extract Emails').item.json.keyInsight }}\n\n_Email 2 in 3 days. Email 3 in 7 days._\n\u23f0 {{ new Date().toLocaleString() }}",
        "chatId": "YOUR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "3b92637a-f2c2-432c-b957-03ecf5526c8a",
      "name": "\u23ed\ufe0f Mark Skipped",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3136,
        800
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "low_fit_skipped",
            "Lead Name": "={{ $json.leadName }}"
          },
          "schema": [
            {
              "id": "Lead Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Lead Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Lead Name"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "835b1e19-c429-4814-92e4-d15dd031ac76",
      "name": "\ud83d\udcf2 Skipped",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3424,
        800
      ],
      "parameters": {
        "text": "=\u23ed\ufe0f *Lead Skipped*\n\ud83d\udc64 {{ $json.leadName }} @ {{ $json.company }}\n\ud83d\udcca Score: {{ $json.score }}/100\n\ud83d\udcac {{ $json.overallAssessment }}",
        "chatId": "YOUR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c4316023-e077-4889-ac3d-86ad9059d458",
      "name": "\ud83d\udca4 No New Leads",
      "type": "n8n-nodes-base.code",
      "position": [
        1824,
        848
      ],
      "parameters": {
        "jsCode": "return { json: { status: 'no_new_leads', checkedAt: new Date().toLocaleString() } };"
      },
      "typeVersion": 2
    },
    {
      "id": "9da28257-a906-4425-81f9-a11fde5a8864",
      "name": "\u23f0 Every 2hrs \u2014 Follow-ups",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        928,
        1264
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 10,12,14,16 * * 1-5"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "cffcf037-6615-4705-8a8f-49dbbcbe1e40",
      "name": "\ud83d\udccb Read Sheet (Follow-ups)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1152,
        1264
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "44cea630-cb7a-461f-9459-0b602f839d00",
      "name": "\ud83d\udd0d Find Follow-ups",
      "type": "n8n-nodes-base.code",
      "position": [
        1376,
        1264
      ],
      "parameters": {
        "jsCode": "const allRows = $input.all();\nconst now = new Date();\nconst actions = [];\n\n// \u26a0\ufe0f CHANGE THIS to your name\nconst senderName = 'YOUR_NAME';\n\nfunction formatBody(rawContent, firstName) {\n  if (!rawContent) return '';\n  let body = rawContent;\n  body = body.replace(/^Subject:.*?\\n\\n?/i, '');\n  body = body.replace(/^Subject:.*?\\n/i, '');\n  body = body.replace(/\\\\n/g, '\\n');\n  body = body.replace(/\\\\\\\\n/g, '\\n');\n  body = body.replace(/\\*\\*/g, '');\n  body = body.replace(/^#+\\s/gm, '');\n  body = body.replace(/\\[YOUR NAME\\]/gi, senderName);\n  body = body.replace(/\\[Your Name\\]/g, senderName);\n  body = body.replace(/YourName/g, senderName);\n  body = body.replace(/\\n\\n?(Best|Cheers|Thanks|Thank you|Regards|Kind regards|Warm regards|Sincerely|Talk soon|All the best),?\\s*\\n\\s*[A-Z][a-zA-Z]*(\\s+[A-Z][a-zA-Z]*)?\\s*(\\n\\n?(Best|Cheers|Thanks|Thank you|Regards|Kind regards|Warm regards|Sincerely|Talk soon|All the best),?\\s*\\n\\s*(\\[YOUR NAME\\]|YourName|[A-Z][a-zA-Z]*(\\s+[A-Z][a-zA-Z]*)?))?$/i, '');\n  body = body.replace(/\\n\\n?(Best|Cheers|Thanks|Regards|Sincerely|Talk soon),?\\s*$/i, '');\n  body = body.trimEnd();\n  if (!/^(Hi|Hey|Hello|Dear|Good)\\s/i.test(body.trim())) {\n    body = `Hi ${firstName},\\n\\n${body}`;\n  }\n  body = body + `\\n\\nBest,\\n${senderName}`;\n  body = body.replace(/\\n{4,}/g, '\\n\\n\\n').trim();\n  return body;\n}\n\nfunction toHTML(text) {\n  let html = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\\n\\n/g, '</p><p>').replace(/\\n/g, '<br>');\n  return '<div style=\"font-family: Arial, sans-serif; font-size: 14px; color: #333; line-height: 1.6;\"><p>' + html + '</p></div>';\n}\n\nfor (let i = 0; i < allRows.length; i++) {\n  const r = allRows[i].json;\n  const name = r['Lead Name'] || '';\n  const email = r['Email'] || r['email'] || '';\n  const company = r['Company'] || '';\n  const status = (r['Status'] || '').toLowerCase().trim();\n  const firstName = (name || '').split(' ')[0] || 'there';\n\n  if (!name || !email) continue;\n  if (['low_fit_skipped', 'sequence_complete', 'replied', 'new'].includes(status)) continue;\n\n  const email1SentStr = (r['email 1 sent date'] || '').toString().trim();\n  const email2SentStr = (r['email 2 sent date '] || r['email 2 sent date'] || '').toString().trim();\n  const email3SentStr = (r['email 3 sent date'] || '').toString().trim();\n\n  if (!email1SentStr) continue;\n\n  const email1SentDate = new Date(email1SentStr);\n  if (isNaN(email1SentDate.getTime())) continue;\n\n  const daysSinceEmail1 = (now - email1SentDate) / (1000 * 60 * 60 * 24);\n\n  if (!email2SentStr && daysSinceEmail1 >= 3) {\n    const subject = r['email 2 subject'] || ('Re: ' + (r['email 1subject'] || `Following up \u2014 ${company}`).replace(/^Re:\\s*/i, ''));\n    const body = formatBody(\n      r['email 2 body'] || `Hi ${firstName},\\n\\nI had another thought about how we might help ${company}.\\n\\nWould it make sense to connect briefly?`,\n      firstName\n    );\n    actions.push({ json: {\n      action: 'send_email_2', leadName: name, email: email, company: company,\n      subject: subject, body: body, htmlBody: toHTML(body), emailNumber: 2,\n      daysSinceEmail1: Math.round(daysSinceEmail1 * 10) / 10\n    }});\n  }\n  else if (email2SentStr && !email3SentStr && daysSinceEmail1 >= 7) {\n    const subject = r['email 3 sub'] || ('Re: ' + (r['email 1subject'] || `Following up \u2014 ${company}`).replace(/^Re:\\s*/i, ''));\n    const body = formatBody(\n      r['email 3 body'] || `Hi ${firstName},\\n\\nI know you're busy, so I'll keep this short.\\n\\nIf timing isn't right, no worries at all.\\n\\nWishing you and ${company} all the best.`,\n      firstName\n    );\n    actions.push({ json: {\n      action: 'send_email_3', leadName: name, email: email, company: company,\n      subject: subject, body: body, htmlBody: toHTML(body), emailNumber: 3,\n      daysSinceEmail1: Math.round(daysSinceEmail1 * 10) / 10\n    }});\n  }\n}\n\nif (actions.length === 0) {\n  return [{ json: { hasActions: false, message: 'No follow-ups needed right now.', totalRowsChecked: allRows.length, checkedAt: new Date().toLocaleString() }}];\n}\n\nreturn actions;"
      },
      "typeVersion": 2
    },
    {
      "id": "efbc486b-0d8f-4001-bbe4-63cf0ca2dbdd",
      "name": "\ud83d\udce7 Has Follow-ups?",
      "type": "n8n-nodes-base.if",
      "position": [
        1600,
        1264
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-action",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.action }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f6c8fed2-d1bc-43d4-bb79-87517538c114",
      "name": "\ud83d\udce7 Send Follow-up",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1824,
        1168
      ],
      "parameters": {
        "sendTo": "={{ $json.email }}",
        "message": "={{ $json.htmlBody }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $json.subject }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "999b991c-87ad-402d-a9c3-2ded4af66698",
      "name": "\ud83d\udccb Prepare Update",
      "type": "n8n-nodes-base.code",
      "position": [
        2048,
        1168
      ],
      "parameters": {
        "jsCode": "const data = $('\ud83d\udce7 Has Follow-ups?').item.json;\nconst nowISO = new Date().toISOString();\n\nconst update = { 'Lead Name': data.leadName };\n\nif (data.emailNumber === 2) {\n  update['Status'] = 'email_2_sent';\n  update['email 2 sent date '] = nowISO;\n  update['email 2 subject'] = data.subject;\n  update['email 2 body'] = data.body;\n  update['email 2 body html'] = data.htmlBody;\n} else if (data.emailNumber === 3) {\n  update['Status'] = 'sequence_complete';\n  update['email 3 sent date'] = nowISO;\n  update['email 3 sub'] = data.subject;\n  update['email 3 body'] = data.body;\n  update['email 3 body html'] = data.htmlBody;\n}\n\nreturn { json: update };"
      },
      "typeVersion": 2
    },
    {
      "id": "852f7db1-c97c-4c6e-9cd6-1f4ff55b382d",
      "name": "\u2705 Mark Follow-up Sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2272,
        1168
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "={{ $json.Status }}",
            "Lead Name": "={{ $json['Lead Name'] }}",
            "email 3 sub": "={{ $json['email 3 sub'] || '' }}",
            "email 2 body": "={{ $json['email 2 body'] || '' }}",
            "email 3 body": "={{ $json['email 3 body'] || '' }}",
            "email 2 subject": "={{ $json['email 2 subject'] || '' }}",
            "email 2 body html": "={{ $json['email 2 body html'] || '' }}",
            "email 3 body html": "={{ $json['email 3 body html'] || '' }}",
            "email 3 sent date": "={{ $json['email 3 sent date'] || '' }}",
            "email 2 sent date ": "={{ $json['email 2 sent date '] || '' }}"
          },
          "schema": [
            {
              "id": "Lead Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Lead Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 2 sent date ",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 2 sent date ",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 2 subject",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 2 subject",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 2 body",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 2 body",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 2 body html",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 2 body html",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 3 sent date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 3 sent date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 3 sub",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 3 sub",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 3 body",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 3 body",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email 3 body html",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "email 3 body html",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Lead Name"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "9149478f-d8fa-4c9a-8c9c-7f3095661c23",
      "name": "\ud83d\udcf2 Follow-up Sent",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2272,
        1008
      ],
      "parameters": {
        "text": "=\ud83d\udce7 *Follow-up Email {{ $('\ud83d\udce7 Has Follow-ups?').item.json.emailNumber }} Sent*\n\n\ud83d\udc64 {{ $('\ud83d\udce7 Has Follow-ups?').item.json.leadName }} @ {{ $('\ud83d\udce7 Has Follow-ups?').item.json.company }}\n\ud83d\udce8 {{ $('\ud83d\udce7 Has Follow-ups?').item.json.email }}\n\ud83d\udcdd {{ $('\ud83d\udce7 Has Follow-ups?').item.json.subject }}\n\n{{ $('\ud83d\udce7 Has Follow-ups?').item.json.emailNumber === 3 ? '\ud83c\udfc1 Sequence complete!' : '\u23f3 Next follow-up scheduled.' }}\n\u23f0 {{ new Date().toLocaleString() }}",
        "chatId": "YOUR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "19415433-ff65-4a27-94e6-7c86cacf41b8",
      "name": "\ud83d\udca4 Nothing Pending",
      "type": "n8n-nodes-base.code",
      "position": [
        1824,
        1360
      ],
      "parameters": {
        "jsCode": "return { json: { status: 'no_followups', checkedAt: new Date().toLocaleString() } };"
      },
      "typeVersion": 2
    },
    {
      "id": "f5e432ab-8110-4c6b-a121-2aaa1d6d3826",
      "name": "Ollama Scorer",
      "type": "@n8n/n8n-nodes-langchain.lmChatOllama",
      "position": [
        2352,
        880
      ],
      "parameters": {
        "model": "YOUR_MODEL_NAME",
        "options": {
          "temperature": 0.3
        }
      },
      "typeVersion": 1
    },
    {
      "id": "227b7180-940f-496c-9762-9690f7782591",
      "name": "Ollama Writer",
      "type": "@n8n/n8n-nodes-langchain.lmChatOllama",
      "position": [
        3152,
        624
      ],
      "parameters": {
        "model": "YOUR_MODEL_NAME",
        "options": {
          "temperature": 0.7
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4e08504a-2411-4ce8-a17f-9281ec0c34e6",
      "name": "\u23f0 Every 2hrs \u2014 Reply Check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        928,
        1744
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "30 9,11,13,15,17 * * 1-5"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6c1293fa-42f5-485c-9568-179bf8e55414",
      "name": "\ud83d\udccb Read Sheet (Replies)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1152,
        1744
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "2a290c73-5488-4b01-8bfa-93879cbe60e0",
      "name": "\ud83d\udd0d Filter Active Leads",
      "type": "n8n-nodes-base.code",
      "position": [
        1376,
        1744
      ],
      "parameters": {
        "jsCode": "const allRows = $input.all();\nconst activeLeads = [];\n\n// \u26a0\ufe0f REPLACE WITH YOUR GMAIL ADDRESS\nconst MY_GMAIL = 'user@example.com';\n\nfor (let i = 0; i < allRows.length; i++) {\n  const r = allRows[i].json;\n  const name = r['Lead Name'] || '';\n  const email = (r['Email'] || r['email'] || '').trim().toLowerCase();\n  const company = r['Company'] || '';\n  const status = (r['Status'] || '').toLowerCase().trim();\n\n  if (!name || !email) continue;\n\n  const activeStatuses = ['email_1_sent', 'email_2_sent', 'sequence_complete'];\n  if (!activeStatuses.includes(status)) continue;\n\n  const email1SentStr = (r['email 1 sent date'] || '').toString().trim();\n  if (!email1SentStr) continue;\n\n  activeLeads.push({\n    json: {\n      leadName: name,\n      email: email,\n      company: company,\n      status: status,\n      email1SentDate: email1SentStr,\n      myGmail: MY_GMAIL,\n      rowIndex: i + 2\n    }\n  });\n}\n\nif (activeLeads.length === 0) {\n  return [{ json: { hasActiveLeads: false, message: 'No active leads to check for replies.', checkedAt: new Date().toLocaleString() } }];\n}\n\nreturn activeLeads;"
      },
      "typeVersion": 2
    },
    {
      "id": "e9b61105-f943-4b5d-8edd-2837a0fec3d3",
      "name": "\ud83d\udcec Has Active Leads?",
      "type": "n8n-nodes-base.if",
      "position": [
        1600,
        1744
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-active",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.email }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "3a7dc4af-95a5-4434-990e-4e2fc37a4b84",
      "name": "\ud83d\udd0e Search Gmail Replies",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1824,
        1648
      ],
      "parameters": {
        "limit": 30,
        "simple": false,
        "filters": {
          "q": "=from:{{ $json.email }} in:inbox newer_than:30d -in:sent",
          "includeSpamTrash": false
        },
        "options": {},
        "operation": "getAll"
      },
      "typeVersion": 2.1
    },
    {
      "id": "a9320a6b-095c-442b-ae07-d7422554dc99",
      "name": "\ud83d\udce9 Check Reply Results",
      "type": "n8n-nodes-base.code",
      "position": [
        2048,
        1648
      ],
      "parameters": {
        "jsCode": "const lead = $('\ud83d\udcec Has Active Leads?').item.json;\nconst gmailResults = $input.all();\n\n// \u26a0\ufe0f REPLACE WITH YOUR GMAIL ADDRESS\nconst MY_GMAIL = (lead.myGmail || 'user@example.com').toLowerCase().trim();\n\nfunction safeParseDate(val) {\n  if (val === null || val === undefined || val === '') return null;\n  const str = String(val).trim();\n  if (!str || str === '0' || str === 'undefined' || str === 'null') return null;\n\n  if (/^\\d{10,13}$/.test(str)) {\n    const num = parseInt(str, 10);\n    const ms = str.length <= 10 ? num * 1000 : num;\n    const d = new Date(ms);\n    if (!isNaN(d.getTime()) && d.getFullYear() > 2000 && d.getFullYear() < 2100) return d;\n  }\n\n  try {\n    const d1 = new Date(str);\n    if (!isNaN(d1.getTime()) && d1.getFullYear() > 2000 && d1.getFullYear() < 2100) return d1;\n  } catch(e) {}\n\n  const num = Number(val);\n  if (!isNaN(num) && num > 1+1234567890) {\n    const ms = num > 1+1234567890 ? num : num * 1000;\n    try {\n      const d2 = new Date(ms);\n      if (!isNaN(d2.getTime()) && d2.getFullYear() > 2000 && d2.getFullYear() < 2100) return d2;\n    } catch(e) {}\n  }\n\n  return null;\n}\n\nfunction extractEmail(fromStr) {\n  if (!fromStr) return '';\n  const s = String(fromStr).toLowerCase().trim();\n  const match = s.match(/<([^>]+)>/);\n  if (match) return match[1].trim();\n  if (s.includes('@')) return s.trim();\n  return s;\n}\n\nconst email1SentDate = safeParseDate(lead.email1SentDate);\n\nif (!email1SentDate) {\n  return [{ json: {\n    hasReply: false, leadName: lead.leadName || '', email: lead.email || '',\n    company: lead.company || '', currentStatus: lead.status || '',\n    replyDate: '', replySubject: '', replySnippet: '',\n    debug: 'Could not parse email1SentDate: ' + String(lead.email1SentDate)\n  }}];\n}\n\nif (!gmailResults || gmailResults.length === 0) {\n  return [{ json: {\n    hasReply: false, leadName: lead.leadName || '', email: lead.email || '',\n    company: lead.company || '', currentStatus: lead.status || '',\n    replyDate: '', replySubject: '', replySnippet: ''\n  }}];\n}\n\nconst leadEmail = (lead.email || '').toLowerCase().trim();\nlet replyFound = false;\nlet replyDate = '';\nlet replySubject = '';\nlet replySnippet = '';\n\nfor (const msg of gmailResults) {\n  const m = msg.json || {};\n\n  const labels = m.labelIds || m.labels || [];\n  const labelArr = Array.isArray(labels) ? labels : [String(labels)];\n  const labelStr = labelArr.join(',').toUpperCase();\n  if (labelStr.includes('SENT') && !labelStr.includes('INBOX')) continue;\n\n  const fromRaw = m.from || m.From || m.sender || m.Sender || '';\n  const senderEmail = extractEmail(fromRaw);\n\n  if (senderEmail === MY_GMAIL) continue;\n  if (!senderEmail.includes(leadEmail) && !leadEmail.includes(senderEmail)) continue;\n\n  const msgDate = safeParseDate(m.internalDate)\n    || safeParseDate(m.date)\n    || safeParseDate(m.Date)\n    || safeParseDate(m.receivedDate)\n    || null;\n\n  if (!msgDate) continue;\n  if (msgDate <= email1SentDate) continue;\n\n  if (extractEmail(fromRaw) === MY_GMAIL) continue;\n\n  replyFound = true;\n  replyDate = msgDate.toISOString();\n  replySubject = m.subject || m.Subject || '(no subject)';\n  replySnippet = (m.snippet || m.textPlain || m.text || m.body || '').toString().substring(0, 300);\n  break;\n}\n\nreturn [{ json: {\n  hasReply: replyFound, leadName: lead.leadName || '', email: lead.email || '',\n  company: lead.company || '', currentStatus: lead.status || '',\n  replyDate: replyDate, replySubject: replySubject, replySnippet: replySnippet\n}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "83fe6612-0f68-486a-94fd-0277ddc815d1",
      "name": "\ud83d\udcac Reply Found?",
      "type": "n8n-nodes-base.if",
      "position": [
        2272,
        1648
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-reply",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.hasReply }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "91a4af5a-cdbd-42c6-bdc9-0eb1245acb04",
      "name": "\u2705 Mark Replied in Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2496,
        1536
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "replied",
            "Lead Name": "={{ $json.leadName }}",
            "Reply Date": "={{ $json.replyDate }}",
            "Reply Snippet": "={{ $json.replySnippet }}",
            "Reply Subject": "={{ $json.replySubject }}"
          },
          "schema": [
            {
              "id": "Lead Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Lead Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reply Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Reply Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reply Subject",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Reply Subject",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reply Snippet",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Reply Snippet",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Lead Name"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SHEET_GID",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "46f06680-5f22-4431-9a79-a934fc9846b8",
      "name": "\ud83d\udcf2 Reply Alert! \ud83c\udf89",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2496,
        1376
      ],
      "parameters": {
        "text": "=\ud83c\udf89 *REPLY DETECTED \u2014 Auto-SDR*\n\n\ud83d\udc64 {{ $('\ud83d\udcac Reply Found?').item.json.leadName }}\n\ud83c\udfe2 {{ $('\ud83d\udcac Reply Found?').item.json.company }}\n\ud83d\udce7 From: {{ $('\ud83d\udcac Reply Found?').item.json.email }}\n\ud83d\udcdd Subject: {{ $('\ud83d\udcac Reply Found?').item.json.replySubject }}\n\n\ud83d\udcac Preview:\n_{{ $('\ud83d\udcac Reply Found?').item.json.replySnippet }}_\n\n\ud83d\uded1 Sequence stopped. Follow up manually!\n\ud83d\udcc5 {{ $('\ud83d\udcac Reply Found?').item.json.replyDate }}\n\u23f0 Detected: {{ new Date().toLocaleString() }}",
        "chatId": "YOUR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6985015c-8601-4beb-86d1-7a5c2723231a",
      "name": "\ud83d\udca4 No Reply Yet",
      "type": "n8n-nodes-base.code",
      "position": [
        2496,
        1744
      ],
      "parameters": {
        "jsCode": "return { json: { status: 'no_reply', leadName: $json.leadName || '', email: $json.email || '', checkedAt: new Date().toLocaleString() } };"
      },
      "typeVersion": 2
    },
    {
      "id": "6f41120c-93b1-43a7-98cc-8e54bafa2a03",
      "name": "\ud83d\udca4 No Active Leads",
      "type": "n8n-nodes-base.code",
      "position": [
        1824,
        1840
      ],
      "parameters": {
        "jsCode": "return { json: { status: 'no_active_leads_to_check', checkedAt: new Date().toLocaleString() } };"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "5cffe5e9-b1db-48f0-b5bc-7ac32e366b44",
  "connections": {
    "\ud83d\udcca Pursue?": {
      "main": [
        [
          {
            "node": "\u270d\ufe0f AI Email Writer",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u23ed\ufe0f Mark Skipped",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ollama Scorer": {
      "ai_languageModel": [
        [
          {
            "node": "\ud83d\udcca AI Lead Scorer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Ollama Writer": {
      "ai_languageModel": [
        [
          {
            "node": "\u270d\ufe0f AI Email Writer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udd95 Filter New": {
      "main": [
        [
          {
            "node": "\ud83c\udd95 Has Leads?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udd95 Has Leads?": {
      "main": [
        [
          {
            "node": "\ud83c\udf10 Scrape Website",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udca4 No New Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcac Reply Found?": {
      "main": [
        [
          {
            "node": "\u2705 Mark Replied in Sheet",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83d\udcf2 Reply Alert! \ud83c\udf89",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udca4 No Reply Yet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce7 Send Email 1": {
      "main": [
        [
          {
            "node": "\ud83d\udcbe Save + Mark Sent",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83d\udcf2 Email 1 Sent \u2705",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udfaf Extract Score": {
      "main": [
        [
          {
            "node": "\ud83d\udcca Pursue?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcc4 Build Dossier": {
      "main": [
        [
          {
            "node": "\ud83d\udcca AI Lead Scorer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23ed\ufe0f Mark Skipped": {
      "main": [
        [
          {
            "node": "\ud83d\udcf2 Skipped",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udf10 Scrape Website": {
      "main": [
        [
          {
            "node": "\ud83d\udcc4 Build Dossier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcca AI Lead Scorer": {
      "main": [
        [
          {
            "node": "\ud83c\udfaf Extract Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Prepare Update": {
      "main": [
        [
          {
            "node": "\u2705 Mark Follow-up Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcdd Extract Emails": {
      "main": [
        [
          {
            "node": "\ud83d\udce7 Send Email 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce7 Send Follow-up": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Prepare Update",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83d\udcf2 Follow-up Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce7 Has Follow-ups?": {
      "main": [
        [
          {
            "node": "\ud83d\udce7 Send Follow-up",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udca4 Nothing Pending",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd0d Find Follow-ups": {
      "main": [
        [
          {
            "node": "\ud83d\udce7 Has Follow-ups?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Read Sheet (New)": {
      "main": [
        [
          {
            "node": "\ud83c\udd95 Filter New",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23f0 8 AM \u2014 New Leads": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Read Sheet (New)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u270d\ufe0f AI Email Writer": {
      "main": [
        [
          {
            "node": "\ud83d\udcdd Extract Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcec Has Active Leads?": {
      "main": [
        [
          {
            "node": "\ud83d\udd0e Search Gmail Replies",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udca4 No Active Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce9 Check Reply Results": {
      "main": [
        [
          {
            "node": "\ud83d\udcac Reply Found?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd0d Filter Active Leads": {
      "main": [
        [
          {
            "node": "\ud83d\udcec Has Active Leads?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Read Sheet (Replies)": {
      "main": [
        [
          {
            "node": "\ud83d\udd0d Filter Active Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd0e Search Gmail Replies": {
      "main": [
        [
          {
            "node": "\ud83d\udce9 Check Reply Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Read Sheet (Follow-ups)": {
      "main": [
        [
          {
            "node": "\ud83d\udd0d Find Follow-ups",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23f0 Every 2hrs \u2014 Follow-ups": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Read Sheet (Follow-ups)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23f0 Every 2hrs \u2014 Reply Check": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Read Sheet (Replies)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}