{
  "name": "Agent 6 (Tavily): Bounce Handler — finds alt contact on bounce",
  "description": null,
  "active": false,
  "nodes": [
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyX",
              "value": 1
            }
          ]
        },
        "filters": {
          "q": "in:inbox newer_than:30d (from:mailer-daemon OR from:postmaster OR subject:Undeliverable OR subject:\"Delivery Status Notification\" OR subject:\"failure notice\")"
        }
      },
      "id": "bh_trig",
      "name": "Poll Bounces (1h)",
      "type": "n8n-nodes-base.gmailTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        224
      ]
    },
    {
      "parameters": {
        "jsCode": "\nconst RE=/[a-z0-9._%+\\-]+@[a-z0-9.\\-]+\\.[a-z]{2,}/gi;\nreturn $input.all().map(it=>{\n  const m=it.json;\n  const hdr=(n)=>{ if(m[n])return m[n]; if(m.headers&&m.headers[n.toLowerCase()])return m.headers[n.toLowerCase()];\n    const hs=(m.payload&&m.payload.headers)||[]; const h=hs.find(x=>(x.name||'').toLowerCase()===n.toLowerCase()); return h?h.value:''; };\n  const blob=[(hdr('Subject')||m.subject||''),(m.snippet||m.text||''),(hdr('To')||'')].join(' ');\n  const cands=(blob.match(RE)||[]).map(e=>e.toLowerCase())\n     .filter(e=>!/(mailer-daemon|postmaster|@meraku\\.in|@google\\.com|@gmail\\.com|noreply|no-reply)/.test(e));\n  return {json:{failed_email:cands[0]||'', subject:hdr('Subject')||m.subject||'', snippet:(m.snippet||'').slice(0,300), msgId:m.id||''}};\n}).filter(x=>x.json.failed_email);\n"
      },
      "id": "bh_extract",
      "name": "Extract Failed Recipient",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        224,
        224
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "outreach_log",
        "limit": 1,
        "filters": {
          "conditions": [
            {
              "keyName": "to_email",
              "condition": "ilike",
              "keyValue": "={{ $json.failed_email }}"
            }
          ]
        }
      },
      "id": "bh_match",
      "name": "Match Outreach_Log",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        448,
        128
      ],
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            },
            {
              "id": "c2",
              "leftValue": "={{ $json.bounced }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "notEquals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "bh_if",
      "name": "Matched?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        672,
        128
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "tableId": "outreach_log",
        "filters": {
          "conditions": [
            {
              "keyName": "id",
              "condition": "eq",
              "keyValue": "={{ $json.id }}"
            }
          ]
        },
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "bounced",
              "fieldValue": "={{ true }}"
            },
            {
              "fieldId": "bounce_date",
              "fieldValue": "={{ new Date().toISOString().slice(0,10) }}"
            }
          ]
        }
      },
      "id": "bh_mark",
      "name": "Mark Bounced (Outreach_Log)",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        896,
        128
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "tableId": "activity_log",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "summary",
              "fieldValue": "=Bounce: {{ $json.company || $json.failed_email || 'invalid email' }}"
            },
            {
              "fieldId": "workflow",
              "fieldValue": "Bounce Handler"
            },
            {
              "fieldId": "action",
              "fieldValue": "bounce"
            },
            {
              "fieldId": "count",
              "fieldValue": 1
            },
            {
              "fieldId": "company",
              "fieldValue": "={{ $json.company || '' }}"
            },
            {
              "fieldId": "details",
              "fieldValue": "=Bounced {{ $json.failed_email || '' }}. Alt contact: {{ $json.alt_contact_name || '' }} ({{ $json.alt_contact_role || '' }}) {{ $json.best_email || '' }} {{ $json.alt_contact_linkedin || '' }}"
            }
          ]
        }
      },
      "id": "bh_log",
      "name": "Log → Activity_Log",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1792,
        704
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "prospects",
        "limit": 1,
        "filters": {
          "conditions": [
            {
              "keyName": "email",
              "condition": "ilike",
              "keyValue": "={{ $json.to_email }}"
            },
            {
              "keyName": "alt_email",
              "condition": "ilike",
              "keyValue": "={{ $json.to_email }}"
            }
          ]
        },
        "matchType": "anyFilter"
      },
      "id": "bh_find",
      "name": "Find Prospect",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        896,
        320
      ],
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all().filter(i => i.json && i.json.id);\nreturn items.map(item => {\n  const p = item.json;\n  const company = p.company || ''; const website = p.website || '';\n  const failed = (p.email || '').toLowerCase().trim();\n  const prompt = `A cold email to \"${company}\" (website: ${website}) bounced as undeliverable — the bad address was ${failed}.\nDo two things by searching the company website (contact/about/team/leadership pages), LinkedIn, Crunchbase, press and public sources:\n1) Find an alternative VALID email for the SAME or a senior decision-maker.\n2) Find a DIFFERENT reachable decision-maker (founder/CEO/owner/marketing or growth head) — give their name, role, a likely email, and their LinkedIn profile URL.\nReturn ONLY valid JSON, no prose:\n{\"primary_alt_email\":\"<best alternative email or ''>\",\"alt_contact\":{\"name\":\"\",\"role\":\"\",\"email\":\"\",\"linkedin\":\"<full https linkedin url or ''>\"},\"confidence\":\"high/medium/low\",\"source\":\"<where found>\"}`;\n  return { json: { prompt, company, website, prospect_id: p.id, failed_email: failed } };\n});"
      },
      "id": "bh_prompt",
      "name": "Build Alt Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1120,
        416
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://moviesbar1-resource.openai.azure.com/openai/v1/responses",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"model\":\"gpt-5.4\",\"input\":$json.prompt,\"max_output_tokens\":3000} }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 4,
              "batchInterval": 1000
            }
          },
          "timeout": 280000
        }
      },
      "id": "bh_kimi",
      "name": "GPT Extract — Alt Email",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1344,
        416
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 3000,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const __ext=(j)=>j.choices?.[0]?.message?.content || ((j.output||[]).flatMap(o=>o.content||[]).filter(c=>c.type==='output_text').map(c=>c.text).join('')) || '';\nconst items = $input.all();\nconst sources = $('Build Alt Prompt').all();\nconst out = [];\nfor (let i = 0; i < items.length; i++) {\n  const src = sources[i] ? sources[i].json : {};\n  let content = __ext(items[i].json) || '';\n  const f = content.match(/```(?:json)?\\s*([\\s\\S]*?)```/); if (f) content = f[1].trim();\n  let p = {}; try { p = JSON.parse(content); } catch(e) {}\n  const ac = p.alt_contact || {};\n  const primary = (p.primary_alt_email || '').toLowerCase().trim();\n  const altEmail = (ac.email || '').toLowerCase().trim();\n  const best = primary || altEmail || '';\n  out.push({ json: { ...src,\n    primary_alt_email: primary,\n    alt_contact_name: ac.name || '', alt_contact_role: ac.role || '',\n    alt_contact_email: altEmail, alt_contact_linkedin: ac.linkedin || '',\n    best_email: best, alt_conf: p.confidence || 'low', alt_source: p.source || '' } });\n}\nreturn out;"
      },
      "id": "bh_parse",
      "name": "Parse Alt Email",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1568,
        416
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "tableId": "prospects",
        "filters": {
          "conditions": [
            {
              "keyName": "id",
              "condition": "eq",
              "keyValue": "={{ $json.prospect_id }}"
            }
          ]
        },
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "alt_email",
              "fieldValue": "={{ $json.best_email }}"
            },
            {
              "fieldId": "alt_contact_name",
              "fieldValue": "={{ $json.alt_contact_name }}"
            },
            {
              "fieldId": "alt_contact_role",
              "fieldValue": "={{ $json.alt_contact_role }}"
            },
            {
              "fieldId": "alt_contact_email",
              "fieldValue": "={{ $json.alt_contact_email }}"
            },
            {
              "fieldId": "alt_contact_linkedin",
              "fieldValue": "={{ $json.alt_contact_linkedin }}"
            },
            {
              "fieldId": "email_status",
              "fieldValue": "={{ ($json.best_email || '') !== '' ? $json.alt_verify_status : 'bounced' }}"
            },
            {
              "fieldId": "email_valid",
              "fieldValue": "={{ ($json.best_email || '') !== '' && $json.alt_verify_status !== 'no_valid_alt' }}"
            },
            {
              "fieldId": "email",
              "fieldValue": "={{ $json.best_email || $json.failed_email }}"
            }
          ]
        }
      },
      "id": "bh_write_pros",
      "name": "Write Alt → Prospect",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1792,
        128
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "update",
        "tableId": "outreach_log",
        "filters": {
          "conditions": [
            {
              "keyName": "prospect_id",
              "condition": "eq",
              "keyValue": "={{ $json.prospect_id }}"
            }
          ]
        },
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "alt_email_found",
              "fieldValue": "={{ $json.best_email }}"
            }
          ]
        }
      },
      "id": "bh_write_out",
      "name": "Write Alt → Outreach_Log",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1792,
        320
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "30 */3 * * *"
            }
          ]
        }
      },
      "id": "sweep_trig",
      "name": "Sweep Invalid (every 3h)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        448,
        704
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "prospects",
        "returnAll": true,
        "filters": {
          "conditions": [
            {
              "keyName": "email_valid",
              "condition": "eq",
              "keyValue": "false"
            }
          ]
        }
      },
      "id": "fetch_invalid",
      "name": "Fetch Invalid Prospects",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        672,
        704
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "// keep only invalid prospects that still have NO alternate email, cap per run.\n// staticData tracks attempts so the same prospect isn't re-searched every 3h sweep forever (cost guard).\nconst CAP = 10, MAX_ATTEMPTS = 2, COOLDOWN_MS = 72 * 3600 * 1000;\nconst sd = $getWorkflowStaticData('global');\nif (typeof sd.altAttempts !== 'object' || !sd.altAttempts) sd.altAttempts = {};\nconst now = Date.now();\nconst rows = $input.all().map(i => i.json).filter(p => {\n  if (!p || !p.id) return false;\n  if (p.alt_email && String(p.alt_email).trim()) return false;\n  const a = sd.altAttempts[p.id];\n  if (a && (a.n >= MAX_ATTEMPTS || now - a.last < COOLDOWN_MS)) return false;\n  return true;\n}).slice(0, CAP);\nfor (const p of rows) { const a = sd.altAttempts[p.id] || { n: 0, last: 0 }; a.n++; a.last = now; sd.altAttempts[p.id] = a; }\nfor (const k of Object.keys(sd.altAttempts)) { if (now - sd.altAttempts[k].last > 30 * 24 * 3600 * 1000) delete sd.altAttempts[k]; }\nreturn rows.map(json => ({ json }));\n"
      },
      "id": "filter_noalt",
      "name": "Filter No-Alt & Cap",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        896,
        704
      ]
    },
    {
      "parameters": {
        "jsCode": "\n// Only register a real alternate email as a new candidate row.\nconst out=[];\nfor (const item of $input.all()){\n  const d=item.json; const email=(d.best_email||'').toLowerCase().trim();\n  if(!email || !email.includes('@')) continue;\n  out.push({ json:{\n    prospect_id: String(d.prospect_id || ''),\n    email,\n    person_name: d.alt_contact_name || '',\n    person_role: d.alt_contact_role || '',\n    linkedin_url: d.alt_contact_linkedin || '',\n    confidence: (d.alt_conf||'medium'),\n    source: 'bounce-alt (Agent 6)',\n    status: 'pending'\n  }});\n}\nreturn out;\n"
      },
      "id": "pe6_prep_alt",
      "name": "Prep Alt Candidate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1792,
        512
      ]
    },
    {
      "parameters": {
        "tableId": "prospect_emails",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "prospect_id",
              "fieldValue": "={{ $json.prospect_id }}"
            },
            {
              "fieldId": "email",
              "fieldValue": "={{ $json.email }}"
            },
            {
              "fieldId": "person_name",
              "fieldValue": "={{ $json.person_name }}"
            },
            {
              "fieldId": "person_role",
              "fieldValue": "={{ $json.person_role }}"
            },
            {
              "fieldId": "linkedin_url",
              "fieldValue": "={{ $json.linkedin_url }}"
            },
            {
              "fieldId": "rank",
              "fieldValue": "={{ 2 }}"
            },
            {
              "fieldId": "confidence",
              "fieldValue": "={{ $json.confidence }}"
            },
            {
              "fieldId": "source",
              "fieldValue": "={{ $json.source }}"
            },
            {
              "fieldId": "status",
              "fieldValue": "={{ $json.status }}"
            }
          ]
        }
      },
      "id": "pe6_register_alt",
      "name": "Register Alt → prospect_emails",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        2016,
        512
      ],
      "continueOnFail": true
    },
    {
      "parameters": {
        "resource": "label",
        "operation": "create",
        "name": "BOUNCED",
        "options": {}
      },
      "id": "bh_lbl_ensure",
      "name": "Ensure 'BOUNCED' Label",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        448,
        512
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "resource": "label",
        "returnAll": true
      },
      "id": "bh_lbl_get",
      "name": "Get Gmail Labels (BH)",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        672,
        512
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "addLabels",
        "messageId": "={{ $('Extract Failed Recipient').item.json.msgId }}",
        "labelIds": [
          "={{ $('Get Gmail Labels (BH)').all().find(l => (l.json.name||'').toLowerCase()==='bounced')?.json.id }}"
        ]
      },
      "id": "bh_lbl_apply",
      "name": "Apply 'BOUNCED' Label",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        896,
        512
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "15 8 * * *"
            }
          ]
        }
      },
      "id": "bh_catchup_trig",
      "name": "Daily Catch-up 8:15 AM IST",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        444
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "getAll",
        "returnAll": false,
        "limit": 50,
        "simple": true,
        "filters": {
          "q": "in:inbox newer_than:2d (from:mailer-daemon OR from:postmaster OR subject:Undeliverable OR subject:\"Delivery Status Notification\" OR subject:\"failure notice\")"
        }
      },
      "id": "bh_catchup_search",
      "name": "Search Recent Bounces",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        200,
        444
      ],
      "alwaysOutputData": true,
      "onError": "continueRegularOutput",
      "executeOnce": true
    },
    {
      "name": "Tavily — Find Alt Email",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        636
      ],
      "parameters": {
        "jsCode": "const TAVILY_KEYS=[\"YOUR_TAVILY_API_KEY_1\",\"YOUR_TAVILY_API_KEY_2\",\"YOUR_TAVILY_API_KEY_3\"];\nconst __sd=$getWorkflowStaticData('global');\nif(typeof __sd.tavilyExhausted!=='object'||!__sd.tavilyExhausted) __sd.tavilyExhausted={};\nconst __sleep=(ms)=>new Promise(r=>setTimeout(r,ms));\nconst tavilySearch=async(query,maxResults)=>{\n  for(const k of TAVILY_KEYS){\n    const ex=__sd.tavilyExhausted[k];\n    if(ex && Date.now()-ex<24*3600*1000) continue;\n    let attempts=0;\n    while(attempts<3){\n      attempts++;\n      try{\n        const res=await this.helpers.httpRequest({method:'POST',url:'https://api.tavily.com/search',\n          headers:{'Authorization':'Bearer '+k,'Content-Type':'application/json'},\n          body:{query:String(query||'').slice(0,390),search_depth:'advanced',max_results:maxResults,include_answer:true},\n          json:true,returnFullResponse:true,ignoreHttpStatusErrors:true,timeout:90000});\n        const code=Number(res.statusCode||0);\n        if(code===429){ if(attempts<3){await __sleep(1500*attempts);continue;} break; }\n        if(code===432||code===433||code===401||code===403){ __sd.tavilyExhausted[k]=Date.now(); break; }\n        if(code<200||code>=300){ break; }\n        return res.body||{results:[]};\n      }catch(e){ break; }\n    }\n  }\n  try{ await this.helpers.httpRequest({method:'GET',url:'https://your-n8n-instance.com/webhook/yourbrand-tavily-exhausted',timeout:10000}); }catch(e){}\n  return {results:[],answer:'',tavily_error:'all_keys_exhausted'};\n};\nconst out=[];\nfor(const it of $input.all()){\n  const d=it.json||{};\n  out.push({json: await tavilySearch(((d.company || \"\") + \" \" + (d.website || \"\") + \" founder CEO owner contact email\"), 8)});\n  await __sleep(400);\n}\nreturn out;"
      },
      "id": "c1e280c8-4f2c-4407-a13c-0056e8e0c713"
    },
    {
      "name": "Attach Web Context — Find Alt Email",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1400,
        636
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst srcs = $('Build Alt Prompt').all();\nreturn items.map((it, i) => {\n  const src = srcs[i] ? srcs[i].json : {};\n  const t = it.json || {};\n  const results = (t.results || []).map((r, n) => `[${n+1}] ${r.title || ''}\\nURL: ${r.url || ''}\\n${(r.content || '').slice(0, 1200)}`).join('\\n\\n');\n  const ctx = (t.answer ? `Search answer: ${t.answer}\\n\\n` : '') + (results || 'No web results found.');\n  const prompt = (src.prompt || '') + `\\n\\n---\\nWEB SEARCH RESULTS below — use these as your ONLY research data. Extract what is asked from them; do not invent facts:\\n\\n` + ctx;\n  return { json: { ...src, prompt } };\n});"
      },
      "id": "2bad3815-bcd1-4d27-b002-74c6abd66ed3"
    },
    {
      "name": "Verify Alt Email (AbstractAPI)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1748,
        596
      ],
      "parameters": {
        "jsCode": "const ABSTRACT_KEYS=[\"YOUR_ABSTRACTAPI_KEY_1\",\"YOUR_ABSTRACTAPI_KEY_2\",\"YOUR_ABSTRACTAPI_KEY\",\"YOUR_ABSTRACTAPI_KEY\"];\nconst __sd=$getWorkflowStaticData('global');\nif(typeof __sd.abstractExhausted!=='object'||!__sd.abstractExhausted) __sd.abstractExhausted={};\nif(typeof __sd.abstractCache!=='object'||!__sd.abstractCache) __sd.abstractCache={};\nconst __sleep=(ms)=>new Promise(r=>setTimeout(r,ms));\nconst verifyEmail=async(email)=>{\n  email=String(email||'').toLowerCase().trim();\n  if(!email||!email.includes('@')) return 'undeliverable';\n  const cached=__sd.abstractCache[email];\n  if(cached && Date.now()-cached.ts < 30*24*3600*1000) return cached.status;\n  const KEY_COOLDOWN=24*3600*1000;\n  for(const key of ABSTRACT_KEYS){\n    const ex=__sd.abstractExhausted[key];\n    if(ex && Date.now()-ex<KEY_COOLDOWN) continue;\n    let attempts=0;\n    while(attempts<5){\n      attempts++;\n      try{\n        await __sleep(1400+Math.floor(Math.random()*800));\n        const res=await this.helpers.httpRequest({method:'GET',\n          url:'https://emailreputation.abstractapi.com/v1/?api_key='+key+'&email='+encodeURIComponent(email),\n          json:true, returnFullResponse:true, ignoreHttpStatusErrors:true, timeout:20000});\n        const code=Number(res.statusCode||0);\n        if(code===422){\n          const msg422=JSON.stringify(res.body||'').toLowerCase();\n          if(msg422.includes('quota')){ __sd.abstractExhausted[key]=Date.now(); break; }\n          return 'error';\n        }\n        if(code===429){\n          const msg=JSON.stringify(res.body||'').toLowerCase();\n          const isQuota = msg.includes('quota') && !msg.includes('per second');\n          if(isQuota){ __sd.abstractExhausted[key]=Date.now(); break; }\n          if(attempts<5){ await __sleep(1200*attempts+500); continue; }\n          return 'error';\n        }\n        if(code===401||code===402||code===403){ __sd.abstractExhausted[key]=Date.now(); break; }\n        if(code<200||code>=300) return 'error';\n        const status=String((res.body&&res.body.email_deliverability&&res.body.email_deliverability.status)||'unknown').toLowerCase();\n        const final=(status==='deliverable')?'deliverable':(status==='undeliverable'?'undeliverable':'unknown');\n        __sd.abstractCache[email]={status:final,ts:Date.now()};\n        return final;\n      }catch(e){ return 'error'; }\n    }\n  }\n  return 'quota_exhausted';\n};\n\nconst out=[];\nfor(const item of $input.all()){\n  const d={...item.json};\n  let best='', status='no_valid_alt';\n  for(const cand of [d.primary_alt_email, d.alt_contact_email]){\n    const em=String(cand||'').toLowerCase().trim();\n    if(!em||!em.includes('@')) continue;\n    const st=await verifyEmail(em);\n    if(st==='quota_exhausted'){ best=em; status='alt_unverified'; break; }\n    if(st==='deliverable'){ best=em; status='alt_verified'; break; }\n    if((st==='unknown'||st==='error')&&!best){ best=em; status='alt_unknown'; }\n  }\n  d.best_email=best;\n  d.alt_verify_status=status;\n  out.push({json:d});\n}\nreturn out;"
      },
      "id": "224cf576-881c-4b9d-83d1-7650522120c3"
    },
    {
      "name": "Alt Found?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1928,
        736
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "alt-found",
              "leftValue": "={{ ($json.best_email || '') !== '' }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "b92dd16e-c5bc-41ce-a1c2-fe12e3a9f373"
    },
    {
      "name": "Update Pending Draft To-Email",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        2108,
        736
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "operation": "update",
        "tableId": "outreach_log",
        "matchType": "allFilters",
        "filters": {
          "conditions": [
            {
              "keyName": "prospect_id",
              "condition": "eq",
              "keyValue": "={{ $json.prospect_id }}"
            },
            {
              "keyName": "approval_status",
              "condition": "eq",
              "keyValue": "pending"
            }
          ]
        },
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "to_email",
              "fieldValue": "={{ $json.best_email }}"
            }
          ]
        }
      },
      "id": "bc12bf13-6b2b-4395-ae1e-7a87f3ffb4c4"
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyX",
              "value": 1
            }
          ]
        },
        "filters": {
          "q": "in:inbox newer_than:30d (from:mailer-daemon OR from:postmaster OR subject:Undeliverable OR subject:\"Delivery Status Notification\" OR subject:\"failure notice\")"
        }
      },
      "id": "bh_trig",
      "name": "Poll Bounces B (1h)",
      "type": "n8n-nodes-base.gmailTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        524
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "getAll",
        "returnAll": false,
        "limit": 50,
        "simple": true,
        "filters": {
          "q": "in:inbox newer_than:2d (from:mailer-daemon OR from:postmaster OR subject:Undeliverable OR subject:\"Delivery Status Notification\" OR subject:\"failure notice\")"
        }
      },
      "id": "bh_catchup_search",
      "name": "Search Recent Bounces B",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        200,
        744
      ],
      "alwaysOutputData": true,
      "onError": "continueRegularOutput",
      "executeOnce": true
    }
  ],
  "connections": {
    "Extract Failed Recipient": {
      "main": [
        [
          {
            "node": "Match Outreach_Log",
            "type": "main",
            "index": 0
          },
          {
            "node": "Ensure 'BOUNCED' Label",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Match Outreach_Log": {
      "main": [
        [
          {
            "node": "Matched?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Matched?": {
      "main": [
        [
          {
            "node": "Mark Bounced (Outreach_Log)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Find Prospect",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Find Prospect": {
      "main": [
        [
          {
            "node": "Build Alt Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Alt Prompt": {
      "main": [
        [
          {
            "node": "Tavily — Find Alt Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT Extract — Alt Email": {
      "main": [
        [
          {
            "node": "Parse Alt Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Alt Email": {
      "main": [
        [
          {
            "node": "Verify Alt Email (AbstractAPI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll Bounces (1h)": {
      "main": [
        [
          {
            "node": "Extract Failed Recipient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sweep Invalid (every 3h)": {
      "main": [
        [
          {
            "node": "Fetch Invalid Prospects",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Invalid Prospects": {
      "main": [
        [
          {
            "node": "Filter No-Alt & Cap",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter No-Alt & Cap": {
      "main": [
        [
          {
            "node": "Build Alt Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep Alt Candidate": {
      "main": [
        [
          {
            "node": "Register Alt → prospect_emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ensure 'BOUNCED' Label": {
      "main": [
        [
          {
            "node": "Get Gmail Labels (BH)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Gmail Labels (BH)": {
      "main": [
        [
          {
            "node": "Apply 'BOUNCED' Label",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Catch-up 8:15 AM IST": {
      "main": [
        [
          {
            "node": "Search Recent Bounces",
            "type": "main",
            "index": 0
          },
          {
            "node": "Search Recent Bounces B",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Recent Bounces": {
      "main": [
        [
          {
            "node": "Extract Failed Recipient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tavily — Find Alt Email": {
      "main": [
        [
          {
            "node": "Attach Web Context — Find Alt Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Attach Web Context — Find Alt Email": {
      "main": [
        [
          {
            "node": "GPT Extract — Alt Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify Alt Email (AbstractAPI)": {
      "main": [
        [
          {
            "node": "Write Alt → Prospect",
            "type": "main",
            "index": 0
          },
          {
            "node": "Write Alt → Outreach_Log",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log → Activity_Log",
            "type": "main",
            "index": 0
          },
          {
            "node": "Prep Alt Candidate",
            "type": "main",
            "index": 0
          },
          {
            "node": "Alt Found?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alt Found?": {
      "main": [
        [
          {
            "node": "Update Pending Draft To-Email",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Poll Bounces B (1h)": {
      "main": [
        [
          {
            "node": "Extract Failed Recipient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Recent Bounces B": {
      "main": [
        [
          {
            "node": "Extract Failed Recipient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  }
}