{
  "name": "04c — Outreach Touch 3 (Day +9, secondary contact)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "10 9 * * 1-5"
            }
          ]
        }
      },
      "id": "wf04c-0001",
      "name": "Weekdays 09:10",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT l.*, \n       lc_sec.id as contact_id, lc_sec.full_name, lc_sec.role_title, lc_sec.email,\n       es1.body as touch1_body, es1.subject as touch1_subject\nFROM leads.leads l\nJOIN leads.lead_contacts lc_sec ON lc_sec.lead_id = l.id AND lc_sec.is_primary = false AND lc_sec.email IS NOT NULL AND lc_sec.email_status IN ('verified_valid','catch_all','risky')\nJOIN leads.email_sends es1 ON es1.lead_id = l.id AND es1.touch_number = 1\nWHERE l.state = 'contacted'\n  AND es1.sent_at <= NOW() - INTERVAL '9 days'\n  AND NOT EXISTS (\n    SELECT 1 FROM leads.email_sends es3 WHERE es3.lead_id = l.id AND es3.touch_number = 3\n  )\nORDER BY es1.sent_at ASC LIMIT 10",
        "additionalFields": {}
      },
      "id": "wf04c-0002",
      "name": "Fetch Touch 3 Ready Leads",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "batchSize": 1,
        "options": {}
      },
      "id": "wf04c-0003",
      "name": "Split Leads",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const l = $json;\nconst ra = l.research_artifact || {};\nconst senderName = $env.SENDER_NAME || 'Artenis';\n\nconst systemPrompt = `You are ${senderName}. Write a value-sharing follow-up — still NOT a sales pitch.\n\nRecipient: ${l.full_name}, ${l.role_title} at ${l.company_name}\nTheir manual workflow: ${ra.the_specific_workflow}\nWhat the automated version looks like: ${ra.what_good_looks_like}\nTwo previous emails, no reply.\n\nRULES:\n- Under 60 words total\n- One paragraph\n- Share one specific, concrete insight about what similar businesses have done to solve this exact workflow\n- Describe the solution in operational terms (what happens step by step), not vague outcomes\n- Frame it as \"something worth knowing\" or \"in case it's useful\" — not a pitch\n- ZERO mention of your services, that you built it, or anything you sell\n- NO link, NO call to action\n- Sign off: just your first name\n\nSUBJECT: \"Re: \" + 3-5 words matching the thread topic\n\nBANNED: I noticed, I help, I build, streamline, automate your, solution, my service, reach out, touch base\n\nOUTPUT: valid JSON only — {\"subject\":\"...\",\"body\":\"...\"}\nUse \\\\n for line breaks.`;\n\nreturn [{ json: { ...l, groq_system: systemPrompt, groq_user: 'Write the insight email. JSON only. Use \\\\n not real newlines.', _regen_count: 0 } }];"
      },
      "id": "wf04c-0004",
      "name": "Build Touch 3 Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.groq.com/openai/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'llama-3.3-70b-versatile', messages: [{ role: 'system', content: $json.groq_system }, { role: 'user', content: $json.groq_user }], temperature: 0.4, max_tokens: 400 }) }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "wf04c-0005",
      "name": "Generate Touch 3",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const response = $json;\nconst prev = $('Build Touch 3 Prompt').item.json;\nconst raw = response.choices?.[0]?.message?.content || '';\n\nfunction fixAndParse(s) {\n  let inStr = false, esc = false, out = [];\n  for (const c of s) {\n    const code = c.charCodeAt(0);\n    if (esc) { out.push(c); esc = false; }\n    else if (code === 92) { out.push(c); esc = true; }\n    else if (code === 34) { inStr = !inStr; out.push(c); }\n    else if (code === 10 && inStr) { out.push('\\\\n'); }\n    else { out.push(c); }\n  }\n  return JSON.parse(out.join(''));\n}\n\nlet subject = '', body = '';\ntry {\n  const m = raw.match(/\\{[\\s\\S]*?\"subject\"[\\s\\S]*?\"body\"[\\s\\S]*?\\}/);\n  if (m) { const p = fixAndParse(m[0]); subject = p.subject.trim(); body = p.body.replace(/\\\\n/g,'\\n').trim(); }\n} catch(e) {}\n\nif (!subject || !body) {\n  const lines = raw.trim().split('\\n');\n  subject = lines[0].replace(/^subject:\\s*/i,'').trim();\n  body = lines.slice(1).join('\\n').trim();\n}\n\nreturn [{ json: { ...prev, generated_subject: subject, generated_body: body, raw_output: raw } }];"
      },
      "id": "wf04c-0006",
      "name": "Parse Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1340,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "conditions": [
            {
              "id": "nh",
              "leftValue": "={{ $json.generated_body }}",
              "rightValue": "NEEDS_HUMAN",
              "operator": {
                "type": "string",
                "operation": "equal"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "wf04c-0007",
      "name": "NEEDS_HUMAN?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1560,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const body = $json.generated_body || '';\nconst subject = $json.generated_subject || '';\nconst combined = subject + ' ' + body;\nconst moneyRegex = /\\b(price|pricing|cost|costs|budget|rate|rates|fee|fees|hour|hourly|package|affordable|cheap)\\b|[€$£]|\\b\\d{2,}\\s*(eur|usd|gbp|lek)\\b/i;\nconst aiTellsRegex = /\\b(I hope this (email|message) finds you well|just (touching|checking|circling) (in|back)|leverage|synergy|streamline your|revolutionize|game[- ]?changer|cutting[- ]?edge|harness the power|unlock the potential|in today's (fast-paced|digital|competitive)|delve into|tapestry)\\b/i;\nreturn [{ json: { ...$json, validation_passed: !moneyRegex.test(combined) && !aiTellsRegex.test(combined) } }];"
      },
      "id": "wf04c-0008",
      "name": "Validate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1780,
        420
      ]
    },
    {
      "parameters": {
        "conditions": {
          "conditions": [
            {
              "id": "v",
              "leftValue": "={{ $json.validation_passed }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equal"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "wf04c-0009",
      "name": "Validation Passed?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2000,
        420
      ]
    },
    {
      "parameters": {
        "jsCode": "const senderName = $env.SENDER_NAME || 'Your Name';\nconst senderEmail = $env.SENDER_EMAIL || 'you@example.com';\nconst body = $json.generated_body;\nconst signature = `\n\n${senderName}\n${senderEmail}`;\nreturn [{ json: { ...$json, final_body: body + signature } }];"
      },
      "id": "wf04c-0010",
      "name": "Append Signature",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2220,
        560
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $json.email }}",
        "subject": "={{ $json.generated_subject }}",
        "emailType": "text",
        "message": "={{ $json.final_body }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "wf04c-0011",
      "name": "Send via Gmail",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        2440,
        560
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO leads.email_sends (lead_id, contact_id, touch_number, subject, body, sent_at) VALUES ($1, $2, 3, $3, $4, NOW())",
        "additionalFields": {
          "queryParams": "={{ JSON.stringify([$json.id, $json.contact_id, $json.generated_subject, $json.final_body]) }}"
        }
      },
      "id": "wf04c-0012",
      "name": "Insert email_sends",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        2660,
        560
      ]
    }
  ],
  "connections": {
    "Weekdays 09:10": {
      "main": [
        [
          {
            "node": "Fetch Touch 3 Ready Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Touch 3 Ready Leads": {
      "main": [
        [
          {
            "node": "Split Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Leads": {
      "main": [
        [],
        [
          {
            "node": "Build Touch 3 Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Touch 3 Prompt": {
      "main": [
        [
          {
            "node": "Generate Touch 3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Touch 3": {
      "main": [
        [
          {
            "node": "Parse Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Output": {
      "main": [
        [
          {
            "node": "NEEDS_HUMAN?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "NEEDS_HUMAN?": {
      "main": [
        [
          {
            "node": "Split Leads",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Validate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate": {
      "main": [
        [
          {
            "node": "Validation Passed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validation Passed?": {
      "main": [
        [
          {
            "node": "Split Leads",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Append Signature",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Signature": {
      "main": [
        [
          {
            "node": "Send via Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send via Gmail": {
      "main": [
        [
          {
            "node": "Insert email_sends",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert email_sends": {
      "main": [
        [
          {
            "node": "Split Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
