{
  "name": "Agent 3: Email Writer — drafts cold emails 2x/day",
  "description": null,
  "active": false,
  "nodes": [
    {
      "parameters": {},
      "id": "a3_man",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -704,
        528
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "30 8 * * 1-5"
            },
            {
              "field": "cronExpression",
              "expression": "0 18 * * 1-5"
            }
          ]
        }
      },
      "id": "a3_sched",
      "name": "Twice Daily — 8:30 AM & 6 PM IST",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        -704,
        720
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "prospects",
        "returnAll": true,
        "matchType": "allFilters",
        "filters": {
          "conditions": [
            {
              "keyName": "status",
              "condition": "eq",
              "keyValue": "enriched"
            },
            {
              "keyName": "premium_fit",
              "condition": "in",
              "keyValue": "(high,medium)"
            },
            {
              "keyName": "email_valid",
              "condition": "eq",
              "keyValue": "true"
            }
          ]
        }
      },
      "id": "a3_fetch",
      "name": "Fetch Enriched Prospects",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        -480,
        672
      ]
    },
    {
      "parameters": {
        "jsCode": "\nreturn $input.all().map(item=>{const f=item.json;\nreturn {json:{id:f.id,company:f.company||'',niche:f.niche||'',city:f.city||'',\n website:f.website||'',founder_name:f.founder_name||'',email:f.email||'',key_people:f.key_people||'[]',\n raw_website_data:f.raw_website_data||'',premium_signals:f.premium_signals||''}};});\n"
      },
      "id": "a3_norm",
      "name": "Normalize Prospects",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        416,
        240
      ]
    },
    {
      "parameters": {
        "jsCode": "return $input.all().map(item=>{const d=item.json;\nconst fn=(d.founder_name||'').trim().replace(/^(dr|mr|mrs|ms|prof|shri|smt)\\.?\\s+/i,'');\nconst first=fn.split(/\\s+/)[0];\nconst useName=first && /^[A-Za-z][A-Za-z.'-]+$/.test(first) && !/^(the|team|info|admin|hello|support|care|sales)$/i.test(first);\nconst greeting=useName?('Hi '+first+',') : 'Hello,';\nconst city=d.city||'their city';\nconst seedStr=String(d.id||d.company||'x');\nlet seed=0;for(let i=0;i<seedStr.length;i++){seed=(seed*31+seedStr.charCodeAt(i))>>>0;}\nconst NP=(t)=>{t=String(t||'').toLowerCase();\n if(/timeshare/.test(t)) return {who:'timeshare owners trying to exit their contracts',svc:'timeshare exit help',term:'owners'};\n if(/rehab|addiction|detox|treatment|recovery|dual-diagnosis/.test(t)) return {who:'families searching for private addiction treatment',svc:'private treatment',term:'families'};\n if(/tax/.test(t)) return {who:'people dealing with tax debt',svc:'tax relief help',term:'people'};\n if(/dental|dentist|veneer|hair transplant|bariatric|ivf|fertility|medical tourism|cosmetic/.test(t)) return {who:'patients comparing clinics for this procedure',svc:'this treatment',term:'patients'};\n if(/financ|leasing|equipment/.test(t)) return {who:'business owners looking to finance equipment',svc:'equipment financing',term:'business owners'};\n if(/citizenship|golden visa|residency|passport/.test(t)) return {who:'high net worth families researching second residency',svc:'residency and citizenship services',term:'wealthy families'};\n return {who:'families researching senior care',svc:'senior living',term:'families'};\n};\nconst P=NP(d.niche);\nconst SHORTNAME=(()=>{const nm=String(d.company||'').trim();\n if(nm.length<=24) return nm;\n const at=nm.split(/\\s+at\\s+/i); if(at.length>1 && at[at.length-1].length>=4) return at[at.length-1].trim();\n return nm.split(/\\s+/).slice(0,3).join(' ').replace(/\\s+(of|at|in|the|for|and|on)$/i,'');})();\nconst SUBJECT_PRESETS=[\n \"Is {c} in ChatGPT's answers?\",\"{c} on Google vs. AI search\",\"Can AI find {c}?\",\n \"What AI tells families about {c}\",\"Quick question about {c} and AI search\",\n \"Where {c} ranks when families ask AI\",\"{c}, invisible to AI search?\"\n].map(s=>s.replace(/\\{c\\}/g,SHORTNAME).replace(/families/g,P.term));\nconst assignedSubject=SUBJECT_PRESETS[(seed>>>10)%SUBJECT_PRESETS.length];\nconst INTROS=[\n \"I'm Manvendra Singh, I run Meraku Media, and I've helped several premium high-ticket service businesses grow their inquiries from Google.\",\n \"I'm Manvendra Singh, founder of Meraku Media, where I help premium service businesses win more inquiries from Google and AI search.\",\n \"Quick intro: I'm Manvendra Singh, and my agency Meraku Media works with premium high-ticket service businesses on exactly this.\",\n \"I'm Manvendra Singh. I run Meraku Media, and this kind of search problem is what my agency fixes for premium service businesses.\"\n];\nconst PROOFS=[\n \"Last year I helped a premium senior living brand triple its inquiries from Google.\",\n \"I did this for a premium senior living brand and their Google traffic grew almost 5x in a year.\",\n \"I helped a similar premium brand reach the top of Google for the searches its customers actually use.\",\n \"For a premium senior care brand in a different industry, this cut their cost per new inquiry roughly in half.\"\n];\nconst CTAS=[\n 'Worth a 15-minute call? I will show you exactly what people see when they search.',\n 'If I am right about this, worth 15 minutes?',\n 'No pitch. Just what I found and what I would do about it. 15 minutes?'\n];\nconst intro_line=INTROS[(seed>>>2)%INTROS.length];\nconst proof_line=PROOFS[(seed>>>4)%PROOFS.length];\nconst cta_seed=CTAS[(seed>>>7)%CTAS.length];\nconst ANGLES=[\n 'When '+P.who+' in '+city+' search on Google, a directory or competitor shows up before '+(d.company||'them')+'.',\n 'People now ask ChatGPT and Google AI who to trust for '+P.svc+' in '+city+', and '+(d.company||'they')+' may not be in those answers.',\n 'There is a specific question '+P.who+' search before choosing '+P.svc+' in '+city+' that their website never answers.',\n 'On Google Maps results for '+P.svc+' in '+city+', they are easy to miss.',\n 'A competitor or directory in '+city+' is quietly collecting the '+P.who+' who should find '+(d.company||'them')+' first.',\n 'Something specific on their own website is costing them Google visibility (pick it from the research).'\n];\nconst angle=ANGLES[seed%ANGLES.length];\nconst prompt=`You are Manvendra Singh, an SEO agency founder writing ONE personalized cold email. You only need to write THREE short parts plus a CTA rewrite. Everything else is assembled automatically.\n\nTHE PROSPECT: ${d.company} | ${city} | ${d.website} | niche: ${d.niche||''}\nTHEIR CUSTOMERS: ${P.who} (call them \"${P.term}\")\nTHE ANGLE (build all three parts around this ONE problem): ${angle}\nTHE SUBJECT LINE (already chosen, write the opener to match its channel): \"${assignedSubject}\"\n\nRESEARCH (specifics MUST come from here, never invent):\n${d.raw_website_data||''} ${d.premium_signals||''} ${d.seo_pain_points||''}\n\nWRITE THESE FIELDS:\n1. \"opener\": ONE sentence, max 15 words. A concrete observation about ${d.company} from the research. If the subject mentions ChatGPT/AI, describe what AI answers show; if it mentions Google, describe the Google result.\n2. \"why\": ONE or TWO short sentences. Why this costs THEM: their ${P.term}, their missed inquiries. Plain words a 12-year-old understands. NEVER use the word \"families\" unless \"${P.term}\" includes families.\n3. \"what\": EXACTLY TWO short sentences starting with \"I would\". Name the concrete pages/searches you would build or fix for ${d.company}, using real search phrases their ${P.term} type.\n4. \"cta\": rewrite this ask in your own fresh words, same meaning, ending with a question mark: \"${cta_seed}\"\n5. \"pain_points\": 3 short bullets specific to ${d.company}.\n6. \"linkedin_message\": under 300 chars, casual, same angle.\n\nSTYLE: short sentences, no jargon (banned: leverage, optimize, organic traffic, keywords, search intent, visibility), no em dashes, no exclamation marks, no emojis, never name a past client.\nReturn ONLY JSON: {\"opener\":\"...\",\"why\":\"...\",\"what\":\"...\",\"cta\":\"...\",\"pain_points\":[\"..\",\"..\",\"..\"],\"linkedin_message\":\"...\"}`;\nreturn {json:{...d,prompt,email_angle:angle,assigned_subject:assignedSubject,intro_line,proof_line,greeting_line:greeting}};});"
      },
      "id": "a3_prompt",
      "name": "Build Combined Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        640,
        240
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const src=$('Build Combined Prompt').item.json;\nlet c=$json.choices?.[0]?.message?.content||'{}';\nconst f=c.match(/```(?:json)?\\s*([\\s\\S]*?)```/);if(f)c=f[1].trim();\nlet p={};try{const m=c.match(/\\{[\\s\\S]*\\}/);p=JSON.parse(m?m[0]:c);}catch(e){}\nconst clean=(s)=>String(s||'').replace(/\\s*[—–]\\s*/g,', ').replace(/\\{\\{[^}]*\\}\\}/g,'').trim();\nconst sentSplit=(s)=>clean(s).split(/(?<=[.?!])\\s+(?=[A-Z0-9\"'])/g).map(x=>x.trim()).filter(Boolean);\nconst paras=[];\nparas.push(src.greeting_line||'Hello,');\nif(clean(p.opener)) paras.push(clean(p.opener));\nfor(const s of sentSplit(p.why)) paras.push(s);\nfor(const s of sentSplit(p.what)) paras.push(s);\nparas.push(src.intro_line);\nparas.push(src.proof_line);\nlet cta=clean(p.cta);\nif(!/\\?\\s*$/.test(cta)) cta=(cta?cta.replace(/[.\\s]*$/,''):'Worth 15 minutes?')+'?';\nparas.push(cta);\nconst body=paras.map(x=>'<p>'+x+'</p>').join('')+'<p>Warm regards,<br>Manvendra</p>';\nconst subj=String(src.assigned_subject||'').trim();\nconst pp=Array.isArray(p.pain_points)?p.pain_points:(p.pain_points?[p.pain_points]:[]);\nlet li=''; try{const a=JSON.parse(src.key_people||'[]'); li=(a[0]&&(a[0].linkedin_url||a[0].linkedin))||'';}catch(e){}\nreturn {json:{...src, email_subject:subj, email_body:body,\n email_opener:clean(p.opener), email_why:clean(p.why), email_what:clean(p.what),\n pain_points_str:pp.map(x=>'• '+x).join('\\n'),\n linkedin_message:p.linkedin_message||'', linkedin_url:li, today:new Date().toISOString().slice(0,10)}};"
      },
      "id": "a3_parse",
      "name": "Parse Email + Pain Points",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1088,
        240
      ]
    },
    {
      "parameters": {
        "jsCode": "return $input.all().map(item=>{const d=item.json;\nconst prompt=`Score 1-10 how well these three PERSONALIZED parts of a cold email fit the prospect. The email structure, subject, trust line, proof and CTA are machine-guaranteed, so judge ONLY the writing below.\nPROSPECT: ${d.company} (${d.niche||'service business'}) in ${d.city||''}\nANGLE it should express: ${d.email_angle||''}\nOPENER: ${d.email_opener||''}\nWHY IT MATTERS: ${d.email_why||''}\nWHAT I WOULD DO: ${d.email_what||''}\n9-10: opener cites a concrete, plausible observation about THIS company; why speaks to this niche's actual customers; what names real, niche-appropriate searches/pages.\n7-8: solid and specific enough to send.\n4-6: generic enough to fit any company, wrong-niche wording, or empty phrases.\n1-3: invented facts, placeholders, or wrong industry.\nIf all three parts are present and reference the company or its niche concretely, score at least 7.\nReturn ONLY JSON: {\"score\": <1-10>, \"feedback\": \"one sentence on the weakest part\"}`;\nreturn {json:{...d,prompt}};});"
      },
      "id": "a3_score1p",
      "name": "Build Score Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1312,
        240
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://moviesbar1-resource.openai.azure.com/openai/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"model\":\"gpt-5.4-2\",\"messages\":[{\"role\":\"user\",\"content\":$json.prompt}],\"max_completion_tokens\":4096} }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 2000
            }
          },
          "timeout": 280000
        }
      },
      "id": "a3_score1k",
      "name": "GPT — Score #1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1536,
        240
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "\nconst src=$('Build Score Prompt').item.json;\nlet c=$json.choices?.[0]?.message?.content||'{}';\nconst f=c.match(/```(?:json)?\\s*([\\s\\S]*?)```/);if(f)c=f[1].trim();\nlet p={};try{p=JSON.parse(c);}catch(e){}\nreturn {json:{...src, score:Number(p.score)||7, score_feedback:p.feedback||''}};\n"
      },
      "id": "a3_score1parse",
      "name": "Parse Score #1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1760,
        240
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.score }}",
              "rightValue": 7,
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "a3_if1",
      "name": "Score > 8?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1984,
        240
      ]
    },
    {
      "parameters": {
        "jsCode": "return $input.all().map(item=>{const d=item.json;\nconst prompt=`Improve three parts of a cold email to ${d.company} (${d.niche||''}). Previous attempt scored ${d.score}/10: ${d.score_feedback}\nANGLE: ${d.email_angle||''}\nCurrent opener: ${d.email_opener||''}\nCurrent why: ${d.email_why||''}\nCurrent what: ${d.email_what||''}\nRewrite all three, more concrete and specific to ${d.company}. Same rules: opener max 15 words matching the subject channel (\"${d.assigned_subject||''}\"); why 1-2 plain sentences about their customers; what = exactly two \"I would\" sentences with real searches. No jargon, no em dashes.\nReturn ONLY JSON: {\"opener\":\"...\",\"why\":\"...\",\"what\":\"...\",\"cta\":\"${(d.email_body||'').includes('15 minute') ? 'keep' : 'keep'}\",\"pain_points\":[],\"linkedin_message\":\"\"}`;\nreturn {json:{...d,prompt}};});"
      },
      "id": "a3_retryp",
      "name": "Build Retry Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2208,
        504
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://moviesbar1-resource.openai.azure.com/openai/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"model\":\"gpt-5.4-2\",\"messages\":[{\"role\":\"user\",\"content\":$json.prompt}],\"max_completion_tokens\":4096} }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 2000
            }
          },
          "timeout": 280000
        }
      },
      "id": "a3_retryk",
      "name": "GPT — Retry Email",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2432,
        504
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const src=$('Build Retry Prompt').item.json;\nlet c=$json.choices?.[0]?.message?.content||'{}';\nconst f=c.match(/```(?:json)?\\s*([\\s\\S]*?)```/);if(f)c=f[1].trim();\nlet p={};try{const m=c.match(/\\{[\\s\\S]*\\}/);p=JSON.parse(m?m[0]:c);}catch(e){}\nconst clean=(s)=>String(s||'').replace(/\\s*[—–]\\s*/g,', ').replace(/\\{\\{[^}]*\\}\\}/g,'').trim();\nconst sentSplit=(s)=>clean(s).split(/(?<=[.?!])\\s+(?=[A-Z0-9\"'])/g).map(x=>x.trim()).filter(Boolean);\nconst opener=clean(p.opener)||src.email_opener, why=clean(p.why)||src.email_why, what=clean(p.what)||src.email_what;\nconst oldBody=String(src.email_body||'');\nconst ctaMatch=oldBody.match(/<p>([^<]*\\?)<\\/p><p>Warm regards/);\nconst cta=ctaMatch?ctaMatch[1]:'Worth 15 minutes?';\nconst paras=[src.greeting_line||'Hello,'];\nif(opener) paras.push(opener);\nfor(const s of sentSplit(why)) paras.push(s);\nfor(const s of sentSplit(what)) paras.push(s);\nparas.push(src.intro_line); paras.push(src.proof_line); paras.push(cta);\nconst body=paras.map(x=>'<p>'+x+'</p>').join('')+'<p>Warm regards,<br>Manvendra</p>';\nreturn {json:{...src, email_body:body, email_opener:opener, email_why:why, email_what:what}};"
      },
      "id": "a3_retryparse",
      "name": "Parse Retry Email",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2656,
        504
      ]
    },
    {
      "parameters": {
        "jsCode": "return $input.all().map(item=>{const d=item.json;\nconst prompt=`Score 1-10 how well these three PERSONALIZED parts of a cold email fit the prospect. The email structure, subject, trust line, proof and CTA are machine-guaranteed, so judge ONLY the writing below.\nPROSPECT: ${d.company} (${d.niche||'service business'}) in ${d.city||''}\nANGLE it should express: ${d.email_angle||''}\nOPENER: ${d.email_opener||''}\nWHY IT MATTERS: ${d.email_why||''}\nWHAT I WOULD DO: ${d.email_what||''}\n9-10: opener cites a concrete, plausible observation about THIS company; why speaks to this niche's actual customers; what names real, niche-appropriate searches/pages.\n7-8: solid and specific enough to send.\n4-6: generic enough to fit any company, wrong-niche wording, or empty phrases.\n1-3: invented facts, placeholders, or wrong industry.\nIf all three parts are present and reference the company or its niche concretely, score at least 7.\nReturn ONLY JSON: {\"score\": <1-10>, \"feedback\": \"one sentence on the weakest part\"}`;\nreturn {json:{...d,prompt}};});"
      },
      "id": "a3_score2p",
      "name": "Build Re-Score Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2880,
        504
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://moviesbar1-resource.openai.azure.com/openai/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"model\":\"gpt-5.4-2\",\"messages\":[{\"role\":\"user\",\"content\":$json.prompt}],\"max_completion_tokens\":4096} }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 2000
            }
          },
          "timeout": 280000
        }
      },
      "id": "a3_score2k",
      "name": "GPT — Re-Score #2",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3104,
        504
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "\nconst src=$('Build Re-Score Prompt').item.json;\nlet c=$json.choices?.[0]?.message?.content||'{}';\nconst f=c.match(/```(?:json)?\\s*([\\s\\S]*?)```/);if(f)c=f[1].trim();\nlet p={};try{p=JSON.parse(c);}catch(e){}\nreturn {json:{...src, score:Number(p.score)||7, score_feedback:p.feedback||''}};\n"
      },
      "id": "a3_score2parse",
      "name": "Parse Re-Score #2",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3328,
        504
      ]
    },
    {
      "parameters": {
        "tableId": "outreach_log",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "prospect_id",
              "fieldValue": "={{ $json.id }}"
            },
            {
              "fieldId": "company",
              "fieldValue": "={{ $json.company }}"
            },
            {
              "fieldId": "website",
              "fieldValue": "={{ $json.website }}"
            },
            {
              "fieldId": "linkedin",
              "fieldValue": "={{ $json.linkedin_url }}"
            },
            {
              "fieldId": "founder_name",
              "fieldValue": "={{ $json.founder_name }}"
            },
            {
              "fieldId": "to_email",
              "fieldValue": "={{ $json.email }}"
            },
            {
              "fieldId": "city",
              "fieldValue": "={{ $json.city }}"
            },
            {
              "fieldId": "pain_points",
              "fieldValue": "={{ $json.pain_points_str }}"
            },
            {
              "fieldId": "email_subject",
              "fieldValue": "={{ $json.email_subject }}"
            },
            {
              "fieldId": "email_body",
              "fieldValue": "={{ $json.email_body }}"
            },
            {
              "fieldId": "linkedin_message",
              "fieldValue": "={{ $json.linkedin_message }}"
            },
            {
              "fieldId": "quality_score",
              "fieldValue": "={{ $json.score }}"
            },
            {
              "fieldId": "approval_status",
              "fieldValue": "pending"
            },
            {
              "fieldId": "follow_up_count",
              "fieldValue": 0
            }
          ]
        }
      },
      "id": "a3_log",
      "name": "Airtable — Create Log",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        4224,
        -96
      ],
      "retryOnFail": true,
      "maxTries": 2,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "update",
        "tableId": "prospects",
        "filters": {
          "conditions": [
            {
              "keyName": "id",
              "condition": "eq",
              "keyValue": "={{ $json.prospect_id }}"
            }
          ]
        },
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "status",
              "fieldValue": "approval_sent"
            }
          ]
        }
      },
      "id": "a3_status",
      "name": "Airtable — Status: approval_sent",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        4448,
        -96
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const prospects = $('Fetch Enriched Prospects').all();\nconst rows = $input.all().map(i=>i.json).filter(r=>r&&r.id);\n// A row BLOCKS drafting if it's pending, sent, or replied. Expired-unsent rows do NOT\n// block (that's the deliberate rewrite path). Blocking is matched by prospect id,\n// exact email, AND corporate domain — so duplicate prospect records of the same\n// company can never produce a second cold email.\nconst FREE=new Set(['gmail.com','googlemail.com','outlook.com','hotmail.com','live.com','yahoo.com','yahoo.in','icloud.com','rediffmail.com','protonmail.com']);\nconst dom=e=>String(e||'').toLowerCase().split('@')[1]||'';\nconst blocking=rows.filter(r=>r.sent_date||r.reply_received||r.approval_status==='pending');\nconst blockedPid=new Set(blocking.map(r=>String(r.prospect_id)).filter(Boolean));\nconst blockedEmail=new Set(), blockedDom=new Set();\nfor(const r of blocking){\n  const e=String(r.to_email||'').toLowerCase(); if(!e) continue;\n  blockedEmail.add(e);\n  const dm=dom(e); if(dm && !FREE.has(dm)) blockedDom.add(dm);\n}\nconst pending=rows.filter(r=>r.approval_status==='pending'&&!r.sent_date);\nconst CAP=50;\nconst headroom=Math.max(0,CAP-pending.length);\nreturn prospects.filter(it=>{\n  const p=it.json; const e=String(p.email||'').toLowerCase(); const dm=dom(e);\n  if(blockedPid.has(String(p.id))) return false;\n  if(e && blockedEmail.has(e)) return false;\n  if(dm && !FREE.has(dm) && blockedDom.has(dm)) return false;\n  return true;\n}).slice(0,headroom);"
      },
      "id": "a3_guard",
      "name": "Dedup Guard",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -32,
        672
      ]
    },
    {
      "parameters": {
        "jsCode": "const splitSentences=(html)=>String(html||'').replace(/<p([^>]*)>([\\s\\S]*?)<\\/p>/gi,(m,attrs,inner)=>{\n  if(/<(table|img|ul|ol|br)/i.test(inner)) return m;\n  const parts=inner.split(/(?<=[.?!])\\s+(?=[A-Z0-9\"'])/g).map(s=>s.trim()).filter(Boolean);\n  if(parts.length<2) return m;\n  return parts.map(s=>'<p'+attrs+'>'+s+'</p>').join('');\n});\nconst MERAKU_SIG='<p style=\"margin:16px 0 2px 0\">Warm regards,</p>'\n +'<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"font-family:Arial,Helvetica,sans-serif;border-collapse:collapse;margin-top:6px\"><tr>'\n +'<td style=\"vertical-align:middle;padding-right:12px\"><img src=\"https://yourdomain.com/your-headshot.jpg\" alt=\"Manvendra Singh\" width=\"56\" height=\"56\" style=\"display:block;width:56px;height:56px;border-radius:50%\"></td>'\n +'<td style=\"vertical-align:middle;border-left:3px solid #2e7d5b;padding-left:12px\">'\n +'<div style=\"font-size:14px;font-weight:bold;color:#1a1a1a;line-height:1.4\">Manvendra Singh</div>'\n +'<div style=\"font-size:12px;color:#5f6368;line-height:1.5\">Founder, Meraku Media</div>'\n +'<div style=\"font-size:12px;color:#5f6368;line-height:1.5;padding-top:3px\">[YOUR PHONE NUMBER]</div>'\n +'</td></tr></table>';\nconst stripOldSig=(b)=>String(b||'')\n .replace(/<p[^>]*>\\s*(warm\\s+regards|best\\s+regards|kind\\s+regards|regards|best|thanks|thank\\s+you|cheers)[\\s\\S]{0,500}?manvendra[\\s\\S]*$/i,'')\n .replace(/(warm\\s+regards|best\\s+regards|kind\\s+regards|thanks|thank\\s+you|cheers)[,!]?[\\s\\S]{0,500}?manvendra[\\s\\S]*$/i,'')\n .trim();\n\n// HARD GATE — deterministic quality checks + auto-fix before an email can be logged/sent.\nconst NAME_PH=/\\{\\{?\\s*(first[_ ]?name|name|firstname|fname)\\s*\\}?\\}|\\[\\s*(first[_ ]?name|name|firstname|company)\\s*\\]/gi;\nconst ANY_PH=/\\{\\{[^}]*\\}\\}|\\[[A-Za-z _]{2,30}\\]|\\b(first_?name|firstname|lorem ipsum|x{4,}|your name here|insert .* here)\\b/i;\nconst unesc=s=>(s||'').replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&quot;/g,'\"').replace(/&#39;/g,\"'\").replace(/&amp;/g,'&');\nreturn $input.all().map(item=>{\n  const d=item.json;\n  let subj=(d.email_subject||'').replace(/[\\u{1F000}-\\u{1FFFF}\\u{2600}-\\u{27BF}\\u{2190}-\\u{21FF}\\u{2B00}-\\u{2BFF}]/gu,'').trim();\n  let body=d.email_body||'';\n  // 1) fix double-escaped HTML so tags render instead of showing as text\n  if(/&lt;|&gt;|&amp;lt;/.test(body)) body=unesc(unesc(body));\n  // 2) resolve name placeholders with the REAL contact first name (fallback \"there\")\n  const fn=(d.founder_name||'').trim().replace(/^(dr|mr|mrs|ms|prof|shri|smt)\\.?\\s+/i,'');\n  const first=(fn && /^[A-Za-z][A-Za-z.'’-]+$/.test(fn.split(/\\s+/)[0])) ? fn.split(/\\s+/)[0] : '';\n  body=body.replace(NAME_PH, first||'there');\n  subj=subj.replace(NAME_PH, first||(d.company||'')).trim();\n  // 2b) personalize greeting: generic Hello/Hi/Dear -> \"Hi <First>,\" when a real name exists\n  if(first){\n    const wrapped=/^(\\s*<p>\\s*)(hello|hi|hey|dear)\\b[^<]*?(\\s*<\\/p>)/i;\n    const plain=/^(\\s*)(hello|hi|hey|dear)\\b[^\\n,!<]*[,!]/i;\n    if(wrapped.test(body)) body=body.replace(wrapped, `$1Hi ${first},$3`);\n    else if(plain.test(body)) body=body.replace(plain, `$1Hi ${first},`);\n  }\n\n  body = splitSentences(body);\n  // 3b) canonical signature: strip whatever signoff the model wrote, append the real block\n  body = stripOldSig(body) + MERAKU_SIG;\n  // 3a) never let an em/en dash through\n  body = body.replace(/\\s*[\\u2014\\u2013]\\s*/g, ', ');\n  subj = subj.replace(/\\s*[\\u2014\\u2013]\\s*/g, ', ');\n  // 3) checks (after auto-fix)\n  const txt=(body||'').replace(/<[^>]+>/g,' ').replace(/\\s+/g,' ').trim();\n  const issues=[];\n  if(ANY_PH.test(subj+' '+body)) issues.push('unresolved placeholder');\n  if(/\"body_html\"\\s*:|^\\s*\\{/.test(txt)) issues.push('raw JSON leaked');\n  if(/\\baurum\\b|\\bgracias\\b/i.test(body)) issues.push('names a past client (ClientA/ClientB)');\n  if(!/meraku/i.test(body)||!/manvendra/i.test(body)) issues.push('missing signature');\n  if(!subj) issues.push('empty subject');\n  if(subj.length>90) issues.push('subject too long');\n  const words = txt ? txt.split(' ').filter(Boolean).length : 0;\n  if(words < 70) issues.push('body too short');\n  if(words > 190) issues.push('body too long');\n  if(/&lt;|&gt;/.test(body)) issues.push('escaped HTML remains');\n  if(!/send it|reply|15|call/i.test(txt)) issues.push('no call-to-action');\n  const ps=body.match(/<p>([\\s\\S]*?)<\\/p>/g)||[];\n  const opener=(ps[1]||'').replace(/<[^>]+>/g,' ').replace(/\\s+/g,' ').trim();\n  const ow=opener?opener.split(' ').filter(Boolean).length:0;\n  if(ow>24) issues.push('opener too long ('+ow+'w)');\n  if(/\\b(may|might|potentially)\\b|as much as/i.test(opener)) issues.push('hedgy opener');\n  const gate_pass = issues.length===0;\n  return {json:{...d, email_subject:subj, email_body:body, gate_pass, gate_issues:issues.join('; '), gate_checked:true}};\n});\n"
      },
      "id": "ew_gate",
      "name": "Hard Gate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3776,
        168
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "g1",
              "leftValue": "={{ $json.gate_pass }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ]
        },
        "options": {}
      },
      "id": "ew_gate_if",
      "name": "Gate Pass?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        4000,
        48
      ]
    },
    {
      "parameters": {
        "jsCode": "return $input.all().map(item=>{const d=item.json;\nconst prompt=`Fix this cold email to ${d.company}. It FAILED a mechanical quality gate for: ${d.gate_issues||'quality issues'}.\nRepair ONLY what was flagged; keep the email's observation, angle and voice. Do NOT make it longer or more formal.\nREQUIREMENTS: greeting from name \"${String(d.founder_name||'').replace(/^(dr|mr|mrs|ms|prof|shri|smt)\\.?\\s+/i,'')}\" (real first name -> \"Hi <first>,\"; else \"Hello,\"; never placeholders); 100-140 words; short plain sentences; no bullet lists; an about-me line (Manvendra Singh, runs a growth agency for premium high-ticket service businesses) plus ONE proof sentence; simple question CTA; no emojis; never name a past client (no ClientA/ClientB); one <p> per paragraph, no escaped HTML; end with <p>Warm regards,<br>Manvendra</p> and nothing after; subject under 50 chars.\nCurrent subject: ${d.email_subject}\nCurrent body: ${d.email_body}\nNever use an em dash or en dash; commas or periods only. The CTA must ask for a short call.\nReturn ONLY JSON: {\"subject\":\"...\",\"body_html\":\"...\"}`;\nreturn {json:{...d,prompt,gate_attempts:(d.gate_attempts||0)+1}};});"
      },
      "id": "ew_grw_prompt",
      "name": "Build Gate-Rewrite Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        4448,
        96
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://moviesbar1-resource.openai.azure.com/openai/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"model\":\"gpt-5.4-2\",\"messages\":[{\"role\":\"user\",\"content\":$json.prompt}],\"max_completion_tokens\":4096} }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 2000
            }
          },
          "timeout": 280000
        }
      },
      "id": "ew_grw_kimi",
      "name": "GPT — Gate Rewrite",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        4672,
        96
      ],
      "retryOnFail": true,
      "maxTries": 2,
      "waitBetweenTries": 3000,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const splitSentences=(html)=>String(html||'').replace(/<p([^>]*)>([\\s\\S]*?)<\\/p>/gi,(m,attrs,inner)=>{\n  if(/<(table|img|ul|ol|br)/i.test(inner)) return m;\n  const parts=inner.split(/(?<=[.?!])\\s+(?=[A-Z0-9\"'])/g).map(s=>s.trim()).filter(Boolean);\n  if(parts.length<2) return m;\n  return parts.map(s=>'<p'+attrs+'>'+s+'</p>').join('');\n});\nconst MERAKU_SIG='<p style=\"margin:16px 0 2px 0\">Warm regards,</p>'\n +'<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"font-family:Arial,Helvetica,sans-serif;border-collapse:collapse;margin-top:6px\"><tr>'\n +'<td style=\"vertical-align:middle;padding-right:12px\"><img src=\"https://yourdomain.com/your-headshot.jpg\" alt=\"Manvendra Singh\" width=\"56\" height=\"56\" style=\"display:block;width:56px;height:56px;border-radius:50%\"></td>'\n +'<td style=\"vertical-align:middle;border-left:3px solid #2e7d5b;padding-left:12px\">'\n +'<div style=\"font-size:14px;font-weight:bold;color:#1a1a1a;line-height:1.4\">Manvendra Singh</div>'\n +'<div style=\"font-size:12px;color:#5f6368;line-height:1.5\">Founder, Meraku Media</div>'\n +'<div style=\"font-size:12px;color:#5f6368;line-height:1.5;padding-top:3px\">[YOUR PHONE NUMBER]</div>'\n +'</td></tr></table>';\nconst stripOldSig=(b)=>String(b||'')\n .replace(/<p[^>]*>\\s*(warm\\s+regards|best\\s+regards|kind\\s+regards|regards|best|thanks|thank\\s+you|cheers)[\\s\\S]{0,500}?manvendra[\\s\\S]*$/i,'')\n .replace(/(warm\\s+regards|best\\s+regards|kind\\s+regards|thanks|thank\\s+you|cheers)[,!]?[\\s\\S]{0,500}?manvendra[\\s\\S]*$/i,'')\n .trim();\n\nconst src=$('Build Gate-Rewrite Prompt').item.json;\nlet c=$json.choices?.[0]?.message?.content||'{}';\nconst f=c.match(/```(?:json)?\\s*([\\s\\S]*?)```/);if(f)c=f[1].trim();\nlet p={};try{p=JSON.parse(c);}catch(e){}\nconst NAME_PH=/\\{\\{?\\s*(first[_ ]?name|name|firstname|fname)\\s*\\}?\\}|\\[\\s*(first[_ ]?name|name|firstname|company)\\s*\\]/gi;\nconst unesc=s=>(s||'').replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&quot;/g,'\"').replace(/&#39;/g,\"'\").replace(/&amp;/g,'&');\nlet subj=(p.subject||src.email_subject||'').replace(/[\\u{1F000}-\\u{1FFFF}\\u{2600}-\\u{27BF}\\u{2190}-\\u{21FF}\\u{2B00}-\\u{2BFF}]/gu,'').trim();\nlet body=p.body_html||p.body||src.email_body||'';\nif(/&lt;|&gt;/.test(body)) body=unesc(unesc(body));\nconst fn=(src.founder_name||'').trim().replace(/^(dr|mr|mrs|ms|prof|shri|smt)\\.?\\s+/i,'');\nconst first=(fn && /^[A-Za-z][A-Za-z.'’-]+$/.test(fn.split(/\\s+/)[0]))?fn.split(/\\s+/)[0]:'';\nbody=body.replace(NAME_PH, first||'there');\nsubj=subj.replace(NAME_PH, first||(src.company||'')).trim();\n// final safety: never let a placeholder through\nbody=body.replace(/\\{\\{[^}]*\\}\\}/g, first||'there');\n  body = body.replace(/\\s*[\\u2014\\u2013]\\s*/g, ', ');\n  subj = subj.replace(/\\s*[\\u2014\\u2013]\\s*/g, ', ');\nbody = splitSentences(body);\nbody = stripOldSig(body) + MERAKU_SIG;\nreturn {json:{...src, email_subject:subj, email_body:body, gate_pass:true, gate_issues:'', gate_rewritten:true}};\n"
      },
      "id": "ew_grw_parse",
      "name": "Parse Gate-Rewrite",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        4896,
        264
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "r1",
              "leftValue": "={{ $json.score }}",
              "rightValue": 7,
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "ew_rescore_if",
      "name": "Re-Score > 8?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        3552,
        504
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "tableId": "prospects",
        "filters": {
          "conditions": [
            {
              "keyName": "id",
              "condition": "eq",
              "keyValue": "={{ $json.id }}"
            }
          ]
        },
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "status",
              "fieldValue": "low_score"
            }
          ]
        }
      },
      "id": "ew_park",
      "name": "Park Low-Score",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        4448,
        288
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "outreach_log",
        "returnAll": true
      },
      "id": "ew_fetch_pending",
      "name": "Fetch Pending Prospect IDs",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        -256,
        672
      ],
      "executeOnce": true,
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "a3_loop",
      "name": "Loop One-by-One",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        192,
        672
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "ra1",
              "leftValue": "={{ $json.gate_attempts || 0 }}",
              "rightValue": 1,
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "a3_rewrite_attempted",
      "name": "Rewrite Attempted?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        4224,
        144
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://moviesbar1-resource.openai.azure.com/openai/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"model\":\"gpt-5.4-2\",\"messages\":[{\"role\":\"user\",\"content\":$json.prompt}],\"max_completion_tokens\":4096} }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 2000
            }
          },
          "timeout": 280000
        }
      },
      "id": "a3_kimi",
      "name": "Azure — Write Email",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        864,
        240
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "name": "Draft Run Report",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        392,
        472
      ],
      "parameters": {
        "jsCode": "const drafted=(()=>{try{return $('Airtable — Create Log').all().filter(i=>i.json&&!i.json._none).length;}catch(e){return 0;}})();\nconst parked=(()=>{try{return $('Park Low-Score').all().length;}catch(e){return 0;}})();\nconst sd=$getWorkflowStaticData('global');\nconst last=Number(sd.lastZeroDraftAlert||0);\nconst need=(drafted===0 && parked>=3) && (Date.now()-last)>20*3600*1000;\nif(need) sd.lastZeroDraftAlert=Date.now();\nreturn [{json:{drafted, parked, _send_alert:need}}];"
      },
      "id": "4d20d258-ddc1-437a-9f3d-646165464bcc"
    },
    {
      "name": "Zero Drafts?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        572,
        472
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "zd",
              "leftValue": "={{ $json._send_alert === true }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "e0e9d7ae-415c-476b-97e4-ad48be017427"
    },
    {
      "name": "Gmail — Zero Drafts Alert",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        752,
        472
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "sendTo": "you@yourdomain.com",
        "subject": "[ALERT] Agent 3 drafted ZERO emails this run",
        "message": "={{ '<p><b>Agent 3 processed prospects but drafted nothing.</b></p><p>Parked: '+$json.parked+' | Drafted: 0</p><p>The writer/scorer pair likely needs attention. Ask Claude to investigate the latest Agent 3 execution.</p>' }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "873d7e33-2004-4e91-a3f8-2636f7ecb420"
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Fetch Enriched Prospects",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Enriched Prospects": {
      "main": [
        [
          {
            "node": "Fetch Pending Prospect IDs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Prospects": {
      "main": [
        [
          {
            "node": "Build Combined Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Combined Prompt": {
      "main": [
        [
          {
            "node": "Azure — Write Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Email + Pain Points": {
      "main": [
        [
          {
            "node": "Build Score Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Score Prompt": {
      "main": [
        [
          {
            "node": "GPT — Score #1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT — Score #1": {
      "main": [
        [
          {
            "node": "Parse Score #1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Score #1": {
      "main": [
        [
          {
            "node": "Score > 8?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Retry Prompt": {
      "main": [
        [
          {
            "node": "GPT — Retry Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT — Retry Email": {
      "main": [
        [
          {
            "node": "Parse Retry Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Retry Email": {
      "main": [
        [
          {
            "node": "Build Re-Score Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Re-Score Prompt": {
      "main": [
        [
          {
            "node": "GPT — Re-Score #2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT — Re-Score #2": {
      "main": [
        [
          {
            "node": "Parse Re-Score #2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Re-Score #2": {
      "main": [
        [
          {
            "node": "Re-Score > 8?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Airtable — Create Log": {
      "main": [
        [
          {
            "node": "Airtable — Status: approval_sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedup Guard": {
      "main": [
        [
          {
            "node": "Loop One-by-One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hard Gate": {
      "main": [
        [
          {
            "node": "Gate Pass?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gate Pass?": {
      "main": [
        [
          {
            "node": "Airtable — Create Log",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Rewrite Attempted?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Gate-Rewrite Prompt": {
      "main": [
        [
          {
            "node": "GPT — Gate Rewrite",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT — Gate Rewrite": {
      "main": [
        [
          {
            "node": "Parse Gate-Rewrite",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Gate-Rewrite": {
      "main": [
        [
          {
            "node": "Hard Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Score > 8?": {
      "main": [
        [
          {
            "node": "Hard Gate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Retry Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Re-Score > 8?": {
      "main": [
        [
          {
            "node": "Hard Gate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Park Low-Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Park Low-Score": {
      "main": [
        [
          {
            "node": "Loop One-by-One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Twice Daily — 8:30 AM & 6 PM IST": {
      "main": [
        [
          {
            "node": "Fetch Enriched Prospects",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Pending Prospect IDs": {
      "main": [
        [
          {
            "node": "Dedup Guard",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop One-by-One": {
      "main": [
        [
          {
            "node": "Draft Run Report",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Normalize Prospects",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Airtable — Status: approval_sent": {
      "main": [
        [
          {
            "node": "Loop One-by-One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rewrite Attempted?": {
      "main": [
        [
          {
            "node": "Park Low-Score",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Gate-Rewrite Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Azure — Write Email": {
      "main": [
        [
          {
            "node": "Parse Email + Pain Points",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Draft Run Report": {
      "main": [
        [
          {
            "node": "Zero Drafts?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Zero Drafts?": {
      "main": [
        [
          {
            "node": "Gmail — Zero Drafts Alert",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false,
    "binaryMode": "separate"
  }
}