{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Cold Email Engine (Sender + Follow-ups + Reply Detection)",
  "nodes": [
    {
      "id": "every-35-min-weekdays-9-4",
      "name": "Every 35 min (weekdays 9-4)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "*/35 9-16 * * 1-5"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "get-leads",
      "name": "Get leads",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        220,
        0
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1tpCenP_1Qmj4f3DCQPO3iSd7Kz2fdz_G0m7BYuiQpio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "pick-next-lead",
      "name": "Pick next lead",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        0
      ],
      "parameters": {
        "jsCode": "// Warm-up ramp + daily cap + pick the next un-contacted lead.\nconst staticData = $getWorkflowStaticData('global');\nconst now = new Date();\nif (!staticData.startDate) { staticData.startDate = now.toISOString(); }\nconst start = new Date(staticData.startDate);\nconst dayNum = Math.floor((now - start) / (1000 * 60 * 60 * 24)) + 1;\n\nlet cap;\nif (dayNum <= 7) cap = 5;\nelse if (dayNum <= 14) cap = 8;\nelse if (dayNum <= 21) cap = 12;\nelse cap = 15;\n\nconst todayStr = now.toISOString().slice(0, 10);\nconst rows = items.map(i => i.json);\nconst sentToday = rows.filter(r => String(r.sent_at || '').slice(0, 10) === todayStr).length;\nif (sentToday >= cap) {\n  return [{ json: { proceed: false, reason: 'Daily cap reached', cap, sentToday, dayNum } }];\n}\n\nconst next = rows.find(r => {\n  const status = String(r.status || '').trim().toLowerCase();\n  const email = String(r['Email'] || '').trim();\n  return email.includes('@') && (status === '' || status === 'pending');\n});\nif (!next) {\n  return [{ json: { proceed: false, reason: 'No pending leads left', cap, sentToday, dayNum } }];\n}\nreturn [{ json: { proceed: true, cap, sentToday, dayNum, lead: next, row_number: next.row_number } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "within-daily-limit",
      "name": "Within daily limit?",
      "type": "n8n-nodes-base.if",
      "position": [
        660,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c1",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.proceed }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "build-email-prompt",
      "name": "Build email prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        880,
        0
      ],
      "parameters": {
        "jsCode": "const lead = $json.lead || {};\n\nconst system = `You are Shafeel writing a SHORT, casual, confident cold email to one prospect. You build AI agent systems and outbound automation for companies and agencies, done-for-you on commission.\n\nSTYLE RULES:\n- 3 to 4 short sentences total. Never more.\n- Plain text only. Sound like one founder writing to another: confident, direct, human, not salesy.\n- Open with \"Hey [first name],\" then ONE genuine, specific sentence about their company based on the company description provided. Make it real and personal, never generic flattery.\n- NEVER use em-dashes or double hyphens. Use commas, periods, or new sentences instead.\n- No buzzwords, no exclamation marks, no marketing-speak, no ALL CAPS, no links, no images.\n- Use ONLY the true facts below. Never invent results, numbers, clients, or claims.\n- Close with the call to action, then sign off on its own line with just: Shafeel\n\nTRUE FACTS (do not go beyond these):\n- I recently added $20K for a SaaS client in Indiana by building them an outbound system.\n- I build AI agent systems and outbound automation for clients, working on commission.\n- Guarantee I can make: I will get you your next 5 clients or I will work for free until I do.\n\nCALL TO ACTION (phrase it naturally, do not paste any link):\n- Offer a quick 15 minute call later today or tomorrow afternoon, and say you will send a Google Meet link.\n\nOutput STRICT JSON only, no markdown, exactly:\n{\"subject\": \"...\", \"body\": \"...\"}\nThe subject must be casual, lowercase-ish, under 5 words, and must not look like an ad.`;\n\nconst userMsg = `Write the cold email now for this lead.\nFirst name: ${lead['First Name'] || 'there'}\nCompany: ${lead['Company Name'] || ''}\nTitle: ${lead['Title'] || ''}\nIndustry: ${lead['Industry'] || ''}\nCompany description (use this for the personalized opening line): ${lead['Company Short Description'] || lead['Headline'] || 'no description available'}\n\nIf no company description is available, open with a genuine line based on their title or industry instead. Keep it to 3 or 4 short sentences. No em-dashes. Sign off as Shafeel.`;\n\nconst body = { model: 'claude-sonnet-4-6', max_tokens: 500, system, messages: [{ role: 'user', content: userMsg }] };\nreturn [{ json: { body } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "write-email-claude",
      "name": "Write email (Claude)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1100,
        0
      ],
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify($json.body) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "anthropicApi"
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "parse-email",
      "name": "Parse email",
      "type": "n8n-nodes-base.code",
      "position": [
        1320,
        0
      ],
      "parameters": {
        "jsCode": "const resp = $json;\nlet text = '';\ntry { text = resp.content && resp.content[0] && resp.content[0].text ? resp.content[0].text : ''; } catch (e) { text = ''; }\ntext = text.replace(/^```json\\s*/i, '').replace(/^```\\s*/i, '').replace(/```\\s*$/i, '').trim();\nlet parsed;\ntry { parsed = JSON.parse(text); } catch (e) { parsed = { subject: 'quick question', body: text || 'Hi, reaching out.' }; }\n\nconst decide = $('Pick next lead').item.json;\nconst lead = decide.lead || {};\nreturn [{ json: {\n  to: lead['Email'],\n  first_name: lead['First Name'] || '',\n  company: lead['Company Name'] || '',\n  row_number: decide.row_number,\n  subject: String(parsed.subject || 'quick question').slice(0, 120),\n  body: String(parsed.body || '').trim()\n} }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "send-email",
      "name": "Send email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1540,
        0
      ],
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "message": "={{ $json.body }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $json.subject }}",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "build-row-update",
      "name": "Build row update",
      "type": "n8n-nodes-base.set",
      "position": [
        1760,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "row-number-a",
              "name": "row_number",
              "type": "number",
              "value": "={{ $('Parse email').item.json.row_number }}"
            },
            {
              "id": "status-a",
              "name": "status",
              "type": "string",
              "value": "active"
            },
            {
              "id": "sent-at-a",
              "name": "sent_at",
              "type": "string",
              "value": "={{ $now.toFormat('yyyy-LL-dd HH:mm:ss') }}"
            },
            {
              "id": "subject-sent-a",
              "name": "subject_sent",
              "type": "string",
              "value": "={{ $('Parse email').item.json.subject }}"
            },
            {
              "id": "stage-a",
              "name": "stage",
              "type": "number",
              "value": "1"
            },
            {
              "id": "thread-id-a",
              "name": "thread_id",
              "type": "string",
              "value": "={{ $json.threadId }}"
            },
            {
              "id": "message-id-a",
              "name": "message_id",
              "type": "string",
              "value": "={{ $json.id }}"
            }
          ]
        },
        "includeOtherFields": false
      },
      "typeVersion": 3.4
    },
    {
      "id": "mark-lead-as-sent",
      "name": "Mark lead as sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1980,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "stage": "={{ $json.stage }}",
            "status": "={{ $json.status }}",
            "sent_at": "={{ $json.sent_at }}",
            "thread_id": "={{ $json.thread_id }}",
            "message_id": "={{ $json.message_id }}",
            "row_number": "={{ $json.row_number }}",
            "subject_sent": "={{ $json.subject_sent }}"
          },
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ]
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1tpCenP_1Qmj4f3DCQPO3iSd7Kz2fdz_G0m7BYuiQpio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "pause-after-send",
      "name": "Pause after send",
      "type": "n8n-nodes-base.wait",
      "position": [
        2200,
        0
      ],
      "parameters": {
        "unit": "seconds",
        "amount": "={{ Math.floor(Math.random() * 90) + 30 }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "nothing-to-send",
      "name": "Nothing to send",
      "type": "n8n-nodes-base.noOp",
      "position": [
        880,
        160
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "every-40-min-weekdays-9-4",
      "name": "Every 40 min (weekdays 9-4)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        460
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "*/40 9-16 * * 1-5"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "get-leads-follow-up",
      "name": "Get leads (follow-up)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        220,
        460
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1tpCenP_1Qmj4f3DCQPO3iSd7Kz2fdz_G0m7BYuiQpio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "decide-follow-up",
      "name": "Decide follow-up",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        460
      ],
      "parameters": {
        "jsCode": "// Pick the next ACTIVE lead whose follow-up is due.\n// Shares the same daily ramp cap as the initial sender (counts sent_at == today).\nconst sd = $getWorkflowStaticData('global');\nconst now = new Date();\nif (!sd.startDate) sd.startDate = now.toISOString();\nconst dayNum = Math.floor((now - new Date(sd.startDate)) / 864e5) + 1;\n\nlet cap;\nif (dayNum <= 7) cap = 5;\nelse if (dayNum <= 14) cap = 8;\nelse if (dayNum <= 21) cap = 12;\nelse cap = 15;\n\nconst today = now.toISOString().slice(0, 10);\nconst rows = items.map(i => i.json);\nconst sentToday = rows.filter(r => String(r.sent_at || '').slice(0, 10) === today).length;\nif (sentToday >= cap) {\n  return [{ json: { proceed: false, reason: 'Daily cap reached', cap, sentToday } }];\n}\n\n// Days to wait: 3 days before email 2 (stage 1), 4 more before email 3 (stage 2).\nconst DELAY = { 1: 3, 2: 4 };\nconst daysSince = ts => {\n  if (!ts) return 999;\n  const d = new Date(String(ts).slice(0, 10));\n  return (now - d) / 864e5;\n};\n\nconst next = rows.find(r => {\n  const status = String(r.status || '').trim().toLowerCase();\n  const stage = Number(r.stage || 0);\n  const email = String(r['Email'] || '').trim();\n  return email.includes('@') && status === 'active' && (stage === 1 || stage === 2) && daysSince(r.sent_at) >= DELAY[stage];\n});\nif (!next) {\n  return [{ json: { proceed: false, reason: 'No follow-ups due', cap, sentToday } }];\n}\n\nreturn [{ json: {\n  proceed: true, cap, sentToday,\n  lead: next, row_number: next.row_number,\n  stage: Number(next.stage || 0),\n  message_id: next.message_id,\n  thread_id: next.thread_id,\n} }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "follow-up-due",
      "name": "Follow-up due?",
      "type": "n8n-nodes-base.if",
      "position": [
        660,
        460
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c1",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.proceed }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "build-follow-up",
      "name": "Build follow-up",
      "type": "n8n-nodes-base.code",
      "position": [
        880,
        460
      ],
      "parameters": {
        "jsCode": "// ====== SET YOUR NAME ======\nconst SENDER_NAME = 'Shafeel';\n// ===========================\n\nconst lead = $json.lead || {};\nconst stage = Number($json.stage);   // 1 => sending email 2; 2 => sending email 3 (final)\nconst fuNum = stage;\n\nconst system = `You are ${SENDER_NAME} writing a SHORT follow-up to a cold email the prospect did not reply to. This is follow-up number ${fuNum} of 2.\n\nRULES:\n- 1 to 3 sentences only. Shorter than the first email.\n- Friendly and low pressure. No guilt, no \"just bumping this\", no pushiness.\n- Add ONE new angle, a quick proof point, or a simple yes or no question. Do NOT repeat the whole pitch.\n- If this is follow-up number 2, make it a short, polite final check-in and offer to close the loop.\n- NEVER use em-dashes or double hyphens. No links, no images. Plain text.\n- Sign off on its own line with just: ${SENDER_NAME}\n\nOutput STRICT JSON only, no markdown, exactly:\n{\"subject\": \"\", \"body\": \"...\"}\nLeave subject as an empty string, the email is sent as a reply in the same thread.`;\n\nconst userMsg = `Prospect first name: ${lead['First Name'] || 'there'}\nCompany: ${lead['Company Name'] || ''}\nWhat they do: ${lead['Company Short Description'] || lead['Headline'] || 'unknown'}`;\n\nconst body = { model: 'claude-sonnet-4-6', max_tokens: 300, system, messages: [{ role: 'user', content: userMsg }] };\nreturn [{ json: { body } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "write-follow-up-claude",
      "name": "Write follow-up (Claude)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1100,
        460
      ],
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify($json.body) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "anthropicApi"
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "parse-follow-up",
      "name": "Parse follow-up",
      "type": "n8n-nodes-base.code",
      "position": [
        1320,
        460
      ],
      "parameters": {
        "jsCode": "// Parse the follow-up text and carry the IDs needed to reply + update the row.\nconst resp = $json;\nlet text = '';\ntry { text = resp.content && resp.content[0] && resp.content[0].text ? resp.content[0].text : ''; } catch (e) { text = ''; }\ntext = text.replace(/^```json\\s*/i, '').replace(/^```\\s*/i, '').replace(/```\\s*$/i, '').trim();\nlet p;\ntry { p = JSON.parse(text); } catch (e) { p = { subject: '', body: text || 'Just following up in case this is useful.' }; }\n\nconst d = $('Decide follow-up').item.json;\nreturn [{ json: {\n  message_id: d.message_id,\n  thread_id: d.thread_id,\n  row_number: d.row_number,\n  stage: d.stage,\n  next_stage: Number(d.stage) + 1,\n  body: String(p.body || '').trim(),\n} }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "send-reply",
      "name": "Send reply",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1540,
        460
      ],
      "parameters": {
        "message": "={{ $json.body }}",
        "options": {
          "appendAttribution": false
        },
        "emailType": "text",
        "messageId": "={{ $json.message_id }}",
        "operation": "reply"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "mark-stage-sent",
      "name": "Mark stage + sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1760,
        460
      ],
      "parameters": {
        "columns": {
          "value": {
            "stage": "={{ $('Parse follow-up').item.json.next_stage }}",
            "status": "={{ $('Parse follow-up').item.json.next_stage >= 3 ? 'done' : 'active' }}",
            "sent_at": "={{ $now.toFormat('yyyy-LL-dd HH:mm:ss') }}",
            "row_number": "={{ $('Parse follow-up').item.json.row_number }}"
          },
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ]
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1tpCenP_1Qmj4f3DCQPO3iSd7Kz2fdz_G0m7BYuiQpio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "pause-after-reply",
      "name": "Pause after reply",
      "type": "n8n-nodes-base.wait",
      "position": [
        1980,
        460
      ],
      "parameters": {
        "unit": "seconds",
        "amount": "={{ Math.floor(Math.random() * 90) + 30 }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "no-follow-up-due",
      "name": "No follow-up due",
      "type": "n8n-nodes-base.noOp",
      "position": [
        880,
        620
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "check-inbox-3x-daily",
      "name": "Check inbox 3x daily",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        920
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 10,13,16 * * 1-5"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "get-recent-inbox",
      "name": "Get recent inbox",
      "type": "n8n-nodes-base.gmail",
      "position": [
        220,
        920
      ],
      "parameters": {
        "limit": 100,
        "simple": true,
        "filters": {
          "q": "in:inbox newer_than:3d"
        },
        "operation": "getAll",
        "returnAll": false
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "get-inbox-senders",
      "name": "Get inbox senders",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        920
      ],
      "parameters": {
        "jsCode": "// Collect unique sender email addresses from recent inbox messages.\nconst emails = items.map(i => {\n  const f = i.json.from || i.json.From || '';\n  const m = String(f).match(/[\\w.+-]+@[\\w.-]+\\.[A-Za-z]{2,}/);\n  return m ? m[0].toLowerCase() : null;\n}).filter(Boolean);\nreturn [{ json: { senders: Array.from(new Set(emails)) } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "get-leads-replies",
      "name": "Get leads (replies)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        660,
        920
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1tpCenP_1Qmj4f3DCQPO3iSd7Kz2fdz_G0m7BYuiQpio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "match-repliers",
      "name": "Match repliers",
      "type": "n8n-nodes-base.code",
      "position": [
        880,
        920
      ],
      "parameters": {
        "jsCode": "// Any ACTIVE lead who shows up as an inbox sender has replied -> mark them 'replied'\n// so the follow-up flow stops emailing them.\nconst senders = $('Get inbox senders').item.json.senders || [];\nconst set = new Set(senders.map(s => String(s).toLowerCase()));\nconst rows = items.map(i => i.json);\n\nconst out = rows.filter(r => {\n  const email = String(r['Email'] || '').trim().toLowerCase();\n  const status = String(r.status || '').trim().toLowerCase();\n  return email && set.has(email) && status === 'active';\n}).map(r => ({ json: { row_number: r.row_number, status: 'replied' } }));\n\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "mark-replied",
      "name": "Mark replied",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1100,
        920
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "={{ $json.status }}",
            "row_number": "={{ $json.row_number }}"
          },
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ]
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1tpCenP_1Qmj4f3DCQPO3iSd7Kz2fdz_G0m7BYuiQpio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "overview",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        -140
      ],
      "parameters": {
        "width": 500,
        "height": 560,
        "content": "## AI cold email engine: sender + follow-ups + reply detection\n\nA complete cold outreach system on Gmail and Claude, built to protect the sending domain.\n\n### How it works\n- **Sender (top):** every 35 min on weekdays, picks the next un-emailed lead within a daily cap that ramps from 5 to 15 over four weeks, writes a personalized email with Claude, sends it, and marks the row active with its thread IDs.\n- **Follow-ups (middle):** chases leads who have not replied. Email 2 after 3 days, email 3 after 4 more, each as a reply in the same thread. After the third the lead is marked done.\n- **Reply detection (bottom):** scans the inbox a few times a day and marks anyone who replied so they leave the sequence.\nA shared daily cap across all three flows keeps total volume safe.\n\n### Setup\nConnect Gmail, Google Sheets, and an Anthropic credential. The Leads sheet needs an Apollo-style header row plus the columns status, sent_at, subject_sent, stage, thread_id, message_id."
      },
      "typeVersion": 1
    },
    {
      "id": "section-sender",
      "name": "Section - Sender",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 2480,
        "height": 380,
        "content": "## 1. Sender\nPicks the next new lead within the daily ramp cap, writes a personalized email, sends it, and records the thread IDs."
      },
      "typeVersion": 1
    },
    {
      "id": "section-follow-ups",
      "name": "Section - Follow-ups",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        300
      ],
      "parameters": {
        "color": 7,
        "width": 2260,
        "height": 380,
        "content": "## 2. Follow-ups\nReplies to non-repliers in the same thread: email 2 after 3 days, email 3 after 4 more."
      },
      "typeVersion": 1
    },
    {
      "id": "section-replies",
      "name": "Section - Replies",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        760
      ],
      "parameters": {
        "color": 7,
        "width": 1360,
        "height": 360,
        "content": "## 3. Reply detection\nMarks anyone who replied so they stop receiving follow-ups."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "timezone": "America/New_York",
    "executionOrder": "v1"
  },
  "connections": {
    "Get leads": {
      "main": [
        [
          {
            "node": "Pick next lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send email": {
      "main": [
        [
          {
            "node": "Build row update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send reply": {
      "main": [
        [
          {
            "node": "Mark stage + sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse email": {
      "main": [
        [
          {
            "node": "Send email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Follow-up due?": {
      "main": [
        [
          {
            "node": "Build follow-up",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No follow-up due",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Match repliers": {
      "main": [
        [
          {
            "node": "Mark replied",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pick next lead": {
      "main": [
        [
          {
            "node": "Within daily limit?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build follow-up": {
      "main": [
        [
          {
            "node": "Write follow-up (Claude)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse follow-up": {
      "main": [
        [
          {
            "node": "Send reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build row update": {
      "main": [
        [
          {
            "node": "Mark lead as sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decide follow-up": {
      "main": [
        [
          {
            "node": "Follow-up due?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get recent inbox": {
      "main": [
        [
          {
            "node": "Get inbox senders",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get inbox senders": {
      "main": [
        [
          {
            "node": "Get leads (replies)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark lead as sent": {
      "main": [
        [
          {
            "node": "Pause after send",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark stage + sent": {
      "main": [
        [
          {
            "node": "Pause after reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build email prompt": {
      "main": [
        [
          {
            "node": "Write email (Claude)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get leads (replies)": {
      "main": [
        [
          {
            "node": "Match repliers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Within daily limit?": {
      "main": [
        [
          {
            "node": "Build email prompt",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Nothing to send",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check inbox 3x daily": {
      "main": [
        [
          {
            "node": "Get recent inbox",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write email (Claude)": {
      "main": [
        [
          {
            "node": "Parse email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get leads (follow-up)": {
      "main": [
        [
          {
            "node": "Decide follow-up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write follow-up (Claude)": {
      "main": [
        [
          {
            "node": "Parse follow-up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every 35 min (weekdays 9-4)": {
      "main": [
        [
          {
            "node": "Get leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every 40 min (weekdays 9-4)": {
      "main": [
        [
          {
            "node": "Get leads (follow-up)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}