{
  "name": "HEP Lead Gen — Google Maps Scraper + AI Outreach",
  "nodes": [
    {
      "parameters": {},
      "id": "trigger-manual",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [200, 300]
    },
    {
      "parameters": {
        "values": {
          "string": [
            {
              "name": "business_type",
              "value": "HVAC contractor"
            },
            {
              "name": "location",
              "value": "Fort Wayne, Indiana"
            },
            {
              "name": "radius_miles",
              "value": "30"
            },
            {
              "name": "result_count",
              "value": "50"
            },
            {
              "name": "service_offered",
              "value": "Google Ads management and SEO for local service businesses"
            }
          ]
        }
      },
      "id": "set-search-params",
      "name": "Set Search Parameters",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [400, 300]
    },
    {
      "parameters": {
        "url": "https://google.serper.dev/maps",
        "method": "POST",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "serperApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"q\": \"{{ $json.business_type }}\",\n  \"location\": \"{{ $json.location }}\",\n  \"num\": {{ $json.result_count }}\n}"
      },
      "id": "serper-maps-search",
      "name": "Serper Maps Search",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [600, 300],
      "notes": "Requires Serper API credential. Get key at serper.dev. ~$0.20 per 1,000 results."
    },
    {
      "parameters": {
        "fieldToSplitOut": "places",
        "include": "allOtherFields"
      },
      "id": "split-places",
      "name": "Split Out Each Lead",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [800, 300]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.website }}",
              "operation": "isNotEmpty"
            }
          ]
        }
      },
      "id": "filter-has-website",
      "name": "Has Website?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [1000, 300]
    },
    {
      "parameters": {
        "url": "https://api.firecrawl.dev/v1/scrape",
        "method": "POST",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "firecrawlApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"url\": \"{{ $json.website }}\",\n  \"formats\": [\"markdown\"],\n  \"onlyMainContent\": true\n}",
        "options": {
          "timeout": 30000,
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "firecrawl-scrape",
      "name": "Scrape Website",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [1200, 250],
      "notes": "Requires Firecrawl API credential. Free tier covers 500 scrapes/mo."
    },
    {
      "parameters": {
        "model": "claude-sonnet-4-5",
        "messages": {
          "messageValues": [
            {
              "role": "system",
              "content": "You are a friendly, professional outreach specialist writing cold emails to local service businesses. Your goal: get a reply, not close a sale.\n\nRules:\n- Maximum 4 sentences\n- Reference something SPECIFIC from their website or Google listing\n- Identify ONE specific problem they likely have\n- Offer a low-commitment next step (audit, free tool, single call)\n- Sound like a human, not a template\n- NEVER use: 'I hope this email finds you well', 'amazing', 'game-changing', 'in today's fast-paced world'\n- ALWAYS include the business owner's first name if available, otherwise the business name\n\nOutput format (JSON):\n{\n  \"subject\": \"5-8 word personalized subject line\",\n  \"body\": \"the email body, 3-4 sentences\"\n}"
            },
            {
              "role": "user",
              "content": "=Lead details:\n- Business: {{ $('Split Out Each Lead').item.json.title }}\n- Category: {{ $('Split Out Each Lead').item.json.category }}\n- Address: {{ $('Split Out Each Lead').item.json.address }}\n- Rating: {{ $('Split Out Each Lead').item.json.rating }} stars ({{ $('Split Out Each Lead').item.json.ratingCount }} reviews)\n- Website: {{ $('Split Out Each Lead').item.json.website }}\n- Phone: {{ $('Split Out Each Lead').item.json.phoneNumber }}\n\nWebsite content (first 2000 chars):\n{{ $json.data.markdown.substring(0, 2000) }}\n\nMy service: {{ $('Set Search Parameters').item.json.service_offered }}\n\nWrite the cold outreach email."
            }
          ]
        },
        "options": {
          "responseFormat": "json"
        }
      },
      "id": "ai-outreach",
      "name": "Generate Outreach Email",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1,
      "position": [1400, 250],
      "notes": "Replace with OpenAI or Gemini node if preferred. Claude Sonnet 4.5 recommended for quality."
    },
    {
      "parameters": {
        "values": {
          "string": [
            {
              "name": "outreach_subject",
              "value": "={{ $json.subject }}"
            },
            {
              "name": "outreach_body",
              "value": "={{ $json.body }}"
            }
          ]
        },
        "options": {}
      },
      "id": "merge-outreach",
      "name": "Merge Outreach Fields",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [1600, 250]
    },
    {
      "parameters": {
        "values": {
          "string": [
            {
              "name": "outreach_subject",
              "value": "[NO WEBSITE - manual outreach needed]"
            },
            {
              "name": "outreach_body",
              "value": "This lead has no website. Skip AI outreach and call directly using the phone number."
            }
          ]
        },
        "options": {}
      },
      "id": "no-website-default",
      "name": "No Website Default Message",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [1200, 400]
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": "PLACEHOLDER_GOOGLE_SHEETS_ID",
        "sheetName": "Leads",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "business_name": "={{ $('Split Out Each Lead').item.json.title }}",
            "category": "={{ $('Split Out Each Lead').item.json.category }}",
            "address": "={{ $('Split Out Each Lead').item.json.address }}",
            "phone": "={{ $('Split Out Each Lead').item.json.phoneNumber }}",
            "website": "={{ $('Split Out Each Lead').item.json.website }}",
            "rating": "={{ $('Split Out Each Lead').item.json.rating }}",
            "review_count": "={{ $('Split Out Each Lead').item.json.ratingCount }}",
            "outreach_subject": "={{ $json.outreach_subject }}",
            "outreach_body": "={{ $json.outreach_body }}",
            "status": "Not Contacted",
            "first_contact_date": "",
            "response_date": "",
            "meeting_date": "",
            "close_date": "",
            "monthly_revenue": "",
            "added_date": "={{ $now.toFormat('yyyy-LL-dd') }}"
          }
        }
      },
      "id": "save-to-sheets",
      "name": "Save to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [1800, 300],
      "notes": "Replace PLACEHOLDER_GOOGLE_SHEETS_ID with your actual Sheet ID. Sheet must have a 'Leads' tab with the columns listed in this node."
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [[{"node": "Set Search Parameters", "type": "main", "index": 0}]]
    },
    "Set Search Parameters": {
      "main": [[{"node": "Serper Maps Search", "type": "main", "index": 0}]]
    },
    "Serper Maps Search": {
      "main": [[{"node": "Split Out Each Lead", "type": "main", "index": 0}]]
    },
    "Split Out Each Lead": {
      "main": [[{"node": "Has Website?", "type": "main", "index": 0}]]
    },
    "Has Website?": {
      "main": [
        [{"node": "Scrape Website", "type": "main", "index": 0}],
        [{"node": "No Website Default Message", "type": "main", "index": 0}]
      ]
    },
    "Scrape Website": {
      "main": [[{"node": "Generate Outreach Email", "type": "main", "index": 0}]]
    },
    "Generate Outreach Email": {
      "main": [[{"node": "Merge Outreach Fields", "type": "main", "index": 0}]]
    },
    "Merge Outreach Fields": {
      "main": [[{"node": "Save to Google Sheets", "type": "main", "index": 0}]]
    },
    "No Website Default Message": {
      "main": [[{"node": "Save to Google Sheets", "type": "main", "index": 0}]]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "HEP Vault",
      "id": "hep-vault"
    },
    {
      "name": "Pillar 7",
      "id": "pillar-7"
    }
  ],
  "meta": {
    "description": "Google Maps lead scraper + AI outreach generator. Part of the Highly Evolved Plant Vault, Pillar 7. See pillar-7 documentation at https://vault.highlyevolvedplant.com/pillars/07-lead-gen-agent for full setup instructions.",
    "instructions": "SETUP STEPS: 1) Import this JSON into n8n (Add Workflow → Import from File). 2) Add credentials for Serper API (serper.dev, ~$0.20/1k results), Firecrawl API (firecrawl.dev, free tier available), and either Anthropic/OpenAI/Gemini for the AI node. 3) Create a Google Sheet with a 'Leads' tab containing the columns referenced in the Save to Google Sheets node. 4) Replace PLACEHOLDER_GOOGLE_SHEETS_ID with your actual Sheet ID (from the Sheets URL). 5) Customize the Set Search Parameters node defaults. 6) Click Execute Workflow. EXPECTED OUTPUT: 50 rows in your Google Sheet, each with business details and a personalized AI-generated cold email ready to send."
  }
}
