{
  "updatedAt": "2026-02-11T07:52:30.443Z",
  "createdAt": "2026-01-20T02:37:46.215Z",
  "id": "uq4hnH0YHfhYOOzO",
  "name": "Lead Generating Web Scraper & CRM Automation",
  "description": null,
  "active": true,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 9
            }
          ]
        }
      },
      "id": "8b09efbe-4322-4e7f-8512-7805878088f9",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        200,
        300
      ],
      "typeVersion": 1.2
    },
    {
      "parameters": {},
      "id": "d35df8b8-f968-443c-8b89-39a59c0c5b6c",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        200,
        500
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "84cf11db-2680-4463-a454-c699becb72ee",
              "name": "searchQuery",
              "value": "dentists",
              "type": "string"
            },
            {
              "id": "f08cdf9d-8546-4dd3-b7eb-b94bbef7c532",
              "name": "location",
              "value": "Johannesburg",
              "type": "string"
            },
            {
              "id": "9e4af1d2-7149-45af-b052-9ab642a6754e",
              "name": "maxResults",
              "value": "50",
              "type": "number"
            },
            {
              "id": "115ebffa-b1b5-499a-a3a6-e37a19f90c2f",
              "name": "senderName",
              "value": "Ian Immelman",
              "type": "string"
            },
            {
              "id": "6a950459-74af-480d-a378-940e0b505ba7",
              "name": "senderCompany",
              "value": "AnyVision Media",
              "type": "string"
            },
            {
              "id": "8e8b75f3-f674-46fe-9fb4-6560bd139785",
              "name": "senderTitle",
              "value": "Director",
              "type": "string"
            },
            {
              "id": "7de74b4e-fbf6-434d-8340-552d9d6f468c",
              "name": "senderEmail",
              "value": "ian@anyvisionmedia.com",
              "type": "string"
            }
          ]
        }
      },
      "id": "d9c299bf-1039-4bb6-8c83-9025d9f67d29",
      "name": "Search Config",
      "type": "n8n-nodes-base.set",
      "position": [
        440,
        400
      ],
      "typeVersion": 3.4
    },
    {
      "parameters": {
        "jsCode": "const config = $input.first().json;\nconst query = encodeURIComponent(config.location + ' ' + config.searchQuery);\nconst url = 'https://www.google.com/maps/search/' + query;\nreturn {\n  json: {\n    ...config,\n    mapsUrl: url\n  }\n};"
      },
      "id": "de1745b8-9a52-4853-88e0-f120b7628682",
      "name": "Build Maps URL",
      "type": "n8n-nodes-base.code",
      "position": [
        660,
        400
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "url": "={{ $json.mapsUrl }}",
        "options": {
          "allowUnauthorizedCerts": true,
          "response": {
            "response": {
              "fullResponse": true
            }
          },
          "headers": {
            "parameters": [
              {
                "name": "User-Agent",
                "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
              },
              {
                "name": "Accept-Language",
                "value": "en-US,en;q=0.9"
              }
            ]
          }
        }
      },
      "id": "0833296b-0d6b-4ce3-acf5-5f1cf4a7606f",
      "name": "Scrape Google Maps",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        400
      ],
      "typeVersion": 4.2,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const html = $input.first().json.data || '';\nconst config = $('Search Config').first().json;\n\n// Extract website URLs from Google Maps HTML\nconst urlRegex = /https?:\\/\\/[^\\/\\s\"'>]+/g;\nconst allUrls = html.match(urlRegex) || [];\n\n// Filter out Google/system domains\nconst blockedDomains = ['google', 'gstatic', 'googleapis', 'schema', 'ggpht', 'youtube', 'goo.gl'];\nconst validUrls = [...new Set(allUrls)].filter(url => {\n  const lower = url.toLowerCase();\n  return !blockedDomains.some(d => lower.includes(d));\n});\n\n// Try to extract business names from Maps HTML\n// Google Maps embeds business data in various patterns\nconst nameRegex = /\\[\"([^\"]{3,60})\"(?:,null){0,3},\"https?:\\/\\//g;\nconst names = [];\nlet nameMatch;\nwhile ((nameMatch = nameRegex.exec(html)) !== null) {\n  names.push(nameMatch[1]);\n}\n\n// Try to extract addresses\nconst addressRegex = /\\[\"(\\d+[^\"]{5,80}(?:St|Street|Ave|Avenue|Rd|Road|Dr|Drive|Blvd|Way|Lane|Ln|Ct|Pl)[^\"]{0,40})\"/gi;\nconst addresses = [];\nlet addrMatch;\nwhile ((addrMatch = addressRegex.exec(html)) !== null) {\n  addresses.push(addrMatch[1]);\n}\n\n// Try to extract phone numbers from Maps data\nconst phoneRegex = /(?:\\+\\d{1,3}[\\s.-]?)?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}/g;\nconst phones = [...new Set((html.match(phoneRegex) || []))];\n\n// Try to extract ratings\nconst ratingRegex = /\\[(\\d\\.\\d),\"\\d+ review/g;\nconst ratings = [];\nlet rateMatch;\nwhile ((rateMatch = ratingRegex.exec(html)) !== null) {\n  ratings.push(parseFloat(rateMatch[1]));\n}\n\n// Build business objects - match URLs with any extracted metadata\nconst maxResults = parseInt(config.maxResults) || 50;\nconst businesses = validUrls.slice(0, maxResults).map((url, i) => ({\n  businessName: names[i] || '',\n  website: url.split('/').slice(0, 3).join('/'),\n  address: addresses[i] || '',\n  phone: phones[i] || '',\n  rating: ratings[i] || 0,\n  industry: config.searchQuery,\n  location: config.location\n}));\n\nif (businesses.length === 0) {\n  return [{ json: { error: 'No businesses found', urlCount: allUrls.length } }];\n}\n\nreturn businesses.map(b => ({ json: b }));"
      },
      "id": "0de6edb7-d8c9-4d8d-a520-792acfe09704",
      "name": "Extract Business Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1100,
        400
      ],
      "typeVersion": 2,
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "78e62e38-359d-41c5-812d-08a65777e4ed",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": "google"
            },
            {
              "id": "0f8aff4a-667a-408d-8020-e9de3e58e925",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": "gstatic"
            },
            {
              "id": "bee50ccd-bedb-4eb5-a7f9-7edf95b5ff40",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": ""
            }
          ]
        },
        "options": {}
      },
      "id": "f05bb8c6-dd38-40b7-9ae1-441dd1956979",
      "name": "Filter Valid URLs",
      "type": "n8n-nodes-base.filter",
      "position": [
        1320,
        400
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "compareValue": "={{ $json.website }}",
        "options": {}
      },
      "id": "cba71d67-6d61-4bab-aab2-cef1454acb40",
      "name": "Remove Duplicate URLs",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        1520,
        400
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "46e3ad26-9572-438a-93ce-7cde4fd68622",
      "name": "Loop Over Businesses",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1720,
        400
      ],
      "typeVersion": 3
    },
    {
      "parameters": {
        "url": "={{ $json.website }}",
        "options": {
          "allowUnauthorizedCerts": true,
          "timeout": 10000,
          "response": {
            "response": {
              "fullResponse": true
            }
          },
          "headers": {
            "parameters": [
              {
                "name": "User-Agent",
                "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
              }
            ]
          }
        }
      },
      "id": "c1ee4f05-acfc-47ca-ae40-9695f7c2402a",
      "name": "Scrape Website",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1920,
        600
      ],
      "typeVersion": 4.2,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "amount": 2
      },
      "id": "c8518cba-78ff-4450-b4bd-5f92ef46d4ad",
      "name": "Rate Limit Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        2140,
        600
      ],
      "typeVersion": 1.1
    },
    {
      "parameters": {
        "jsCode": "const html = $input.first().json.data || $input.first().json.body || '';\nconst original = $('Loop Over Businesses').item.json;\n\n// Email extraction (exclude image extensions)\nconst emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.(?!jpeg|jpg|png|gif|webp|svg|css|js)[a-zA-Z]{2,}/g;\nconst rawEmails = html.match(emailRegex) || [];\nconst emails = [...new Set(rawEmails.map(e => e.toLowerCase()))];\n\n// Phone extraction\nconst phoneRegex = /(?:\\+\\d{1,3}[\\s.-]?)?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}/g;\nconst rawPhones = html.match(phoneRegex) || [];\nconst phones = [...new Set(rawPhones)].filter(p => p.replace(/\\D/g, '').length >= 7);\n\n// Social media extraction\nconst linkedinRegex = /https?:\\/\\/(?:www\\.)?linkedin\\.com\\/(?:company|in)\\/[^\\s\"'<>)]+/gi;\nconst facebookRegex = /https?:\\/\\/(?:www\\.)?facebook\\.com\\/[^\\s\"'<>)]+/gi;\nconst instagramRegex = /https?:\\/\\/(?:www\\.)?instagram\\.com\\/[^\\s\"'<>)]+/gi;\n\nconst linkedin = (html.match(linkedinRegex) || [])[0] || '';\nconst facebook = (html.match(facebookRegex) || [])[0] || '';\nconst instagram = (html.match(instagramRegex) || [])[0] || '';\n\n// Merge with original business data\nconst result = {\n  businessName: original.businessName || '',\n  website: original.website || '',\n  address: original.address || '',\n  phone: phones[0] || original.phone || '',\n  rating: original.rating || 0,\n  industry: original.industry || '',\n  location: original.location || '',\n  emails: emails,\n  linkedin: linkedin,\n  facebook: facebook,\n  instagram: instagram\n};\n\nreturn { json: result };"
      },
      "id": "32891ac9-3b4f-4267-a6c6-2ec5bd19b018",
      "name": "Extract Contact Info",
      "type": "n8n-nodes-base.code",
      "position": [
        2360,
        600
      ],
      "typeVersion": 2,
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "070f22a6-f970-4efa-928e-3365f444cb1e",
              "operator": {
                "type": "array",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.emails }}",
              "rightValue": ""
            }
          ]
        },
        "options": {}
      },
      "id": "d63f5e84-70e9-429a-b5ea-dd81b9dd24a4",
      "name": "Filter Has Email",
      "type": "n8n-nodes-base.filter",
      "position": [
        1920,
        260
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "fieldToSplitOut": "emails",
        "options": {}
      },
      "id": "94b26752-5433-41a7-90c6-90f04b6973ba",
      "name": "Split Emails",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        2140,
        260
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst seen = new Set();\nconst results = [];\n\nfor (const item of items) {\n  const email = (item.json.emails || '').toString().toLowerCase().trim();\n  if (!email || seen.has(email)) continue;\n  seen.add(email);\n\n  // Calculate lead score (0-100)\n  let score = 0;\n  if (email) score += 20;\n  if (item.json.phone) score += 15;\n  if (item.json.businessName) score += 15;\n  if (item.json.address) score += 10;\n  if (item.json.rating > 0) score += 10;\n  if (item.json.linkedin || item.json.facebook || item.json.instagram) score += 10;\n  if (item.json.website) score += 10;\n  if (item.json.phone && email) score += 10; // bonus for multiple contact methods\n\n  results.push({\n    json: {\n      ...item.json,\n      email: email,\n      leadScore: score,\n      status: 'New',\n      source: 'Google Maps Scraper',\n      datescraped: new Date().toISOString().split('T')[0]\n    }\n  });\n}\n\nreturn results.length > 0 ? results : [{ json: { _empty: true } }];"
      },
      "id": "8245be47-2574-4582-b81d-aae0890e2173",
      "name": "Score Leads",
      "type": "n8n-nodes-base.code",
      "position": [
        2360,
        260
      ],
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "operation": "search",
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appzcZpiIZ6QPtJXT",
          "cachedResultName": "n8n Workflows"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tbludJQgwxtvcyo2Q",
          "cachedResultName": "Leads"
        },
        "filterByFormula": "=({Email} = \"{{ $json.email }}\")"
      },
      "id": "006a844f-efec-4965-bd74-ac1c75f68b51",
      "name": "Check Airtable Exists",
      "type": "n8n-nodes-base.airtable",
      "position": [
        2580,
        260
      ],
      "typeVersion": 2.1,
      "alwaysOutputData": true,
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b49bdf15-d3aa-4079-9324-3bef95556834",
              "operator": {
                "type": "object",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json }}",
              "rightValue": ""
            }
          ]
        },
        "options": {}
      },
      "id": "92a5aad5-3acd-473a-bf09-35e4271ea76e",
      "name": "Is New Lead?",
      "type": "n8n-nodes-base.if",
      "position": [
        2800,
        260
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "operation": "create",
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appzcZpiIZ6QPtJXT",
          "cachedResultName": "n8n Workflows"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tbludJQgwxtvcyo2Q",
          "cachedResultName": "Leads"
        },
        "columns": {
          "value": {
            "Business Name": "={{ $('Score Leads').item.json.businessName }}",
            "Email": "={{ $('Score Leads').item.json.email }}",
            "Phone": "={{ $('Score Leads').item.json.phone }}",
            "Website": "={{ $('Score Leads').item.json.website }}",
            "Address": "={{ $('Score Leads').item.json.address }}",
            "Industry": "={{ $('Score Leads').item.json.industry }}",
            "Location": "={{ $('Score Leads').item.json.location }}",
            "Rating": "={{ $('Score Leads').item.json.rating }}",
            "Social - LinkedIn": "={{ $('Score Leads').item.json.linkedin }}",
            "Social - Facebook": "={{ $('Score Leads').item.json.facebook }}",
            "Social - Instagram": "={{ $('Score Leads').item.json.instagram }}",
            "Lead Score": "={{ $('Score Leads').item.json.leadScore }}",
            "Status": "New",
            "Source": "Google Maps Scraper",
            "Date Scraped": "={{ $('Score Leads').item.json.datescraped }}"
          },
          "schema": [
            {
              "id": "Business Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Business Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Website",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Address",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Industry",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Industry",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Location",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Location",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rating",
              "type": "number",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Rating",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - LinkedIn",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - LinkedIn",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - Facebook",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - Facebook",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - Instagram",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - Instagram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Lead Score",
              "type": "number",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Lead Score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Source",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date Scraped",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Date Scraped",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Email"
          ]
        },
        "options": {}
      },
      "id": "83c36de1-20d9-4e4a-80bc-611c55076342",
      "name": "Create in Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        3040,
        160
      ],
      "typeVersion": 2.1,
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appzcZpiIZ6QPtJXT",
          "cachedResultName": "n8n Workflows"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tbludJQgwxtvcyo2Q",
          "cachedResultName": "Leads"
        },
        "columns": {
          "value": {
            "Phone": "={{ $('Score Leads').item.json.phone }}",
            "Social - LinkedIn": "={{ $('Score Leads').item.json.linkedin }}",
            "Social - Facebook": "={{ $('Score Leads').item.json.facebook }}",
            "Social - Instagram": "={{ $('Score Leads').item.json.instagram }}",
            "Lead Score": "={{ $('Score Leads').item.json.leadScore }}"
          },
          "schema": [
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - LinkedIn",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - LinkedIn",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - Facebook",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - Facebook",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - Instagram",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - Instagram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Lead Score",
              "type": "number",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Lead Score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Email"
          ]
        },
        "options": {}
      },
      "id": "dbe06f00-f18e-4785-8d4a-9e76bbede172",
      "name": "Update in Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        3040,
        380
      ],
      "typeVersion": 2.1,
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1E9_OSvO6F37iG9wh_gaetPT3IzuwdeSNbomIPXzKu94",
          "mode": "list",
          "cachedResultName": "LEAD GEN EMAILSCRAPER",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1E9_OSvO6F37iG9wh_gaetPT3IzuwdeSNbomIPXzKu94/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1E9_OSvO6F37iG9wh_gaetPT3IzuwdeSNbomIPXzKu94/edit#gid=0"
        },
        "columns": {
          "value": {
            "Business Name": "={{ $('Score Leads').item.json.businessName }}",
            "Email": "={{ $('Score Leads').item.json.email }}",
            "Phone": "={{ $('Score Leads').item.json.phone }}",
            "Website": "={{ $('Score Leads').item.json.website }}",
            "Address": "={{ $('Score Leads').item.json.address }}",
            "Industry": "={{ $('Score Leads').item.json.industry }}",
            "Location": "={{ $('Score Leads').item.json.location }}",
            "Rating": "={{ $('Score Leads').item.json.rating }}",
            "LinkedIn": "={{ $('Score Leads').item.json.linkedin }}",
            "Facebook": "={{ $('Score Leads').item.json.facebook }}",
            "Instagram": "={{ $('Score Leads').item.json.instagram }}",
            "Lead Score": "={{ $('Score Leads').item.json.leadScore }}",
            "Status": "={{ $('Score Leads').item.json.status }}",
            "Date Scraped": "={{ $('Score Leads').item.json.datescraped }}"
          },
          "schema": [
            {
              "id": "Business Name",
              "type": "string",
              "display": true,
              "displayName": "Business Name"
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "displayName": "Email"
            },
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "displayName": "Phone"
            },
            {
              "id": "Website",
              "type": "string",
              "display": true,
              "displayName": "Website"
            },
            {
              "id": "Address",
              "type": "string",
              "display": true,
              "displayName": "Address"
            },
            {
              "id": "Industry",
              "type": "string",
              "display": true,
              "displayName": "Industry"
            },
            {
              "id": "Location",
              "type": "string",
              "display": true,
              "displayName": "Location"
            },
            {
              "id": "Rating",
              "type": "string",
              "display": true,
              "displayName": "Rating"
            },
            {
              "id": "LinkedIn",
              "type": "string",
              "display": true,
              "displayName": "LinkedIn"
            },
            {
              "id": "Facebook",
              "type": "string",
              "display": true,
              "displayName": "Facebook"
            },
            {
              "id": "Instagram",
              "type": "string",
              "display": true,
              "displayName": "Instagram"
            },
            {
              "id": "Lead Score",
              "type": "string",
              "display": true,
              "displayName": "Lead Score"
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "displayName": "Status"
            },
            {
              "id": "Date Scraped",
              "type": "string",
              "display": true,
              "displayName": "Date Scraped"
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Email"
          ]
        },
        "options": {
          "useAppend": true
        }
      },
      "id": "8ef8bdff-836f-4128-9e18-b345431d3dde",
      "name": "Append to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3280,
        260
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c2682c88-5122-4418-8da2-97fe49badfca",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            }
          ]
        },
        "options": {}
      },
      "id": "dc5f251d-e3c0-4e4a-8fc6-7e402e48bc1c",
      "name": "Filter New Leads",
      "type": "n8n-nodes-base.filter",
      "position": [
        3500,
        160
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "amount": 30
      },
      "id": "9886589d-0ff5-4076-b2ee-249a20d56186",
      "name": "Rate Limit Emails",
      "type": "n8n-nodes-base.wait",
      "position": [
        3700,
        160
      ],
      "typeVersion": 1.1
    },
    {
      "parameters": {
        "jsCode": "const input = $input.first().json;\nconst leadData = $('Score Leads').item.json;\nconst config = $('Search Config').first().json;\n\n// Parse AI response - chainLlm returns text in 'text' field\nlet emailContent;\ntry {\n  const rawText = input.text || input.response || JSON.stringify(input);\n  const jsonMatch = rawText.match(/\\{[\\s\\S]*\\}/);\n  emailContent = JSON.parse(jsonMatch[0]);\n} catch (e) {\n  emailContent = {\n    subject: 'Partnership opportunity for ' + (leadData.businessName || 'your business'),\n    body: 'I noticed ' + (leadData.businessName || 'your business') + ' in ' + leadData.location + ' and wanted to reach out about a potential collaboration that could benefit your ' + leadData.industry + ' practice.',\n    cta_text: 'Would a brief 10-minute call this week work to explore this?'\n  };\n}\n\nconst htmlBody = '<div style=\"font-family:Segoe UI,Arial,sans-serif;max-width:600px;margin:0 auto;background:#fff;\">' +\n  '<div style=\"padding:30px 40px 20px;border-bottom:3px solid #FF6D5A;\">' +\n  '<h1 style=\"margin:0;font-size:22px;color:#1A1A2E;\">' + config.senderCompany + '</h1></div>' +\n  '<div style=\"padding:30px 40px;\">' +\n  '<p style=\"font-size:15px;line-height:1.6;color:#333;\">' + emailContent.body + '</p>' +\n  '<p style=\"font-size:15px;line-height:1.6;color:#333;\">' + emailContent.cta_text + '</p>' +\n  '<p style=\"font-size:15px;line-height:1.6;color:#333;margin-top:24px;\">Best regards,<br>' +\n  '<strong>' + config.senderName + '</strong><br>' +\n  '<span style=\"color:#666;\">' + config.senderTitle + '</span><br>' +\n  '<span style=\"color:#666;\">' + config.senderCompany + '</span></p></div>' +\n  '<div style=\"padding:20px 40px;background:#f8f8f8;border-top:1px solid #eee;\">' +\n  '<p style=\"font-size:11px;color:#999;\">You received this because your business was listed publicly on Google Maps. Reply &quot;unsubscribe&quot; to be removed.</p></div></div>';\n\nreturn {\n  json: {\n    to: leadData.email,\n    subject: emailContent.subject,\n    htmlBody: htmlBody,\n    leadEmail: leadData.email,\n    businessName: leadData.businessName\n  }\n};"
      },
      "id": "a9e237e5-cf9a-46d0-94af-f4717e8de8f1",
      "name": "Format Email",
      "type": "n8n-nodes-base.code",
      "position": [
        4140,
        160
      ],
      "typeVersion": 2,
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "emailType": "html",
        "message": "={{ $json.htmlBody }}",
        "options": {}
      },
      "id": "ce1c44f4-fbbd-4937-b420-ac4c6971be48",
      "name": "Send Outreach Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        4360,
        160
      ],
      "typeVersion": 2.1,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "update",
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appzcZpiIZ6QPtJXT",
          "cachedResultName": "n8n Workflows"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tbludJQgwxtvcyo2Q",
          "cachedResultName": "Leads"
        },
        "columns": {
          "value": {
            "Status": "Email Sent",
            "Email Sent Date": "={{ new Date().toISOString().split('T')[0] }}",
            "Notes": "={{ $('Format Email').item.json.subject }}",
            "Email": "={{ .item.json.leadEmail }}"
          },
          "schema": [
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "displayName": "Email"
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "displayName": "Status"
            },
            {
              "id": "Email Sent Date",
              "type": "string",
              "display": true,
              "displayName": "Email Sent Date"
            },
            {
              "id": "Notes",
              "type": "string",
              "display": true,
              "displayName": "Notes"
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Email"
          ]
        },
        "options": {}
      },
      "id": "c670553e-beea-4a4e-a2c3-8b479ff572e8",
      "name": "Update Lead Status",
      "type": "n8n-nodes-base.airtable",
      "position": [
        4580,
        160
      ],
      "typeVersion": 2.1,
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const items = $('Score Leads').all();\nconst config = $('Search Config').first().json;\n\nconst totalLeads = items.filter(i => !i.json._empty).length;\nconst avgScore = totalLeads > 0\n  ? Math.round(items.reduce((sum, i) => sum + (i.json.leadScore || 0), 0) / totalLeads)\n  : 0;\n\nconst now = new Date().toLocaleString('en-ZA', { timeZone: 'Africa/Johannesburg' });\n\nreturn {\n  json: {\n    subject: `Lead Scraper Complete: ${totalLeads} leads found`,\n    body: [\n      `<h2>Lead Scraper Run Complete</h2>`,\n      `<p><strong>Date:</strong> ${now}</p>`,\n      `<p><strong>Search:</strong> ${config.searchQuery} in ${config.location}</p>`,\n      `<hr>`,\n      `<p><strong>Total Leads Found:</strong> ${totalLeads}</p>`,\n      `<p><strong>Average Lead Score:</strong> ${avgScore}/100</p>`,\n      `<hr>`,\n      `<p>Check your <a href=\"https://airtable.com/appzcZpiIZ6QPtJXT\">Airtable CRM</a> for details.</p>`\n    ].join('\\n')\n  }\n};"
      },
      "id": "1f7002bf-1734-4722-abe2-5562e549dbcb",
      "name": "Aggregate Results",
      "type": "n8n-nodes-base.code",
      "position": [
        3500,
        460
      ],
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "sendTo": "ian@anyvisionmedia.com",
        "subject": "={{ $json.subject }}",
        "emailType": "html",
        "message": "={{ $json.body }}",
        "options": {}
      },
      "id": "88766fe8-6c3c-48ec-aa0b-215ae375d327",
      "name": "Send Summary",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3720,
        460
      ],
      "typeVersion": 2.1,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {},
      "id": "96b86d80-eaf5-492b-b698-82ffc461c3e7",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        200,
        740
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "sendTo": "ian@anyvisionmedia.com",
        "subject": "Lead Scraper ERROR - {{ $json.workflow.name }}",
        "emailType": "html",
        "message": "=<h2>Workflow Error Alert</h2>\n<p><strong>Workflow:</strong> {{ $json.workflow.name }}</p>\n<p><strong>Error:</strong> {{ $json.execution.error.message }}</p>\n<p><strong>Node:</strong> {{ $json.execution.lastNodeExecuted }}</p>\n<p><a href=\"{{ $json.execution.url }}\">View Execution</a></p>",
        "options": {}
      },
      "id": "2e26c609-1352-4d70-ab2c-e3ed8c930a2c",
      "name": "Error Notification",
      "type": "n8n-nodes-base.gmail",
      "position": [
        440,
        740
      ],
      "typeVersion": 2.1,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## STAGE 1: Triggers & Configuration\n\n**Schedule:** Runs weekly on Monday 9AM\n**Manual:** Click 'Test workflow' for ad-hoc runs\n**Config:** Set your search query, location, and sender details",
        "height": 140,
        "width": 340
      },
      "id": "sticky-1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        140
      ],
      "typeVersion": 1,
      "name": "Note 1"
    },
    {
      "parameters": {
        "content": "## STAGE 2: Google Maps Scraping\n\nScrapes Google Maps HTML for business listings.\nExtracts names, websites, addresses, phones, ratings.",
        "height": 120,
        "width": 340
      },
      "id": "sticky-2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        620,
        240
      ],
      "typeVersion": 1,
      "name": "Note 2"
    },
    {
      "parameters": {
        "content": "## STAGE 3: Website Scraping & Enrichment\n\nVisits each business website with rate limiting.\nExtracts emails, phone numbers, and social media links.",
        "height": 120,
        "width": 360
      },
      "id": "sticky-3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1280,
        520
      ],
      "typeVersion": 1,
      "name": "Note 3"
    },
    {
      "parameters": {
        "content": "## STAGE 4: Scoring & Dedup\n\nScores leads 0-100 based on data completeness.\nDeduplicates by email address.",
        "height": 100,
        "width": 340
      },
      "id": "sticky-4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1880,
        120
      ],
      "typeVersion": 1,
      "name": "Note 4"
    },
    {
      "parameters": {
        "content": "## STAGE 5: CRM Storage\n\n**Airtable** (primary): Creates new or updates existing records\n**Google Sheets** (mirror): Appends all leads for easy sharing",
        "height": 100,
        "width": 380
      },
      "id": "sticky-5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2540,
        80
      ],
      "typeVersion": 1,
      "name": "Note 5"
    },
    {
      "parameters": {
        "content": "## STAGE 6: AI Email Outreach\n\nAI generates personalized cold email per lead.\nSent via Gmail with branded HTML template.\nAirtable status updated to 'Email Sent'.",
        "height": 100,
        "width": 380
      },
      "id": "sticky-6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3460,
        40
      ],
      "typeVersion": 1,
      "name": "Note 6"
    },
    {
      "parameters": {
        "content": "## STAGE 7: Notifications\n\nSummary email after each run.\nError alerts on failures.",
        "height": 80,
        "width": 340
      },
      "id": "sticky-7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3460,
        380
      ],
      "typeVersion": 1,
      "name": "Note 7"
    },
    {
      "parameters": {
        "prompt": {
          "value": "={{ \"You are a professional business development writer. Write a short, personalized cold outreach email.\\n\\nINSTRUCTIONS:\\n1. Subject line (max 50 chars) specific to their business\\n2. Personalized opening referencing their industry\\n3. 2-3 sentences with clear value proposition\\n4. Low-commitment CTA\\n5. Under 150 words\\n6. Professional, friendly - NOT salesy\\n\\nOUTPUT FORMAT (JSON only, no markdown):\\n{\\\"subject\\\": \\\"...\\\", \\\"body\\\": \\\"...\\\", \\\"cta_text\\\": \\\"...\\\"}\\n\\nBusiness: {businessName}\\nIndustry: {industry}\\nLocation: {location}\\nWebsite: {website}\".replace('{businessName}', $json.businessName || ['Business Name'] || '').replace('{industry}', $json.industry || ['Industry'] || '').replace('{location}', $json.location || ['Location'] || '').replace('{website}', $json.website || ['Website'] || '') }}"
        },
        "hasOutputParser": false,
        "options": {}
      },
      "id": "190fe33f-b95d-49be-a55b-fbe14eb69f16",
      "name": "AI Generate Email",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        3920,
        160
      ],
      "typeVersion": 1.4
    },
    {
      "parameters": {
        "model": "anthropic/claude-sonnet-4-20250514",
        "options": {
          "maxTokens": 500,
          "temperature": 0.7
        }
      },
      "id": "e7d1cd87-6dc4-4c46-b171-4dc1f8bfdea1",
      "name": "OpenRouter Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        3940,
        380
      ],
      "typeVersion": 1,
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Search Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Search Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Config": {
      "main": [
        [
          {
            "node": "Build Maps URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Maps URL": {
      "main": [
        [
          {
            "node": "Scrape Google Maps",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Google Maps": {
      "main": [
        [
          {
            "node": "Extract Business Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Business Data": {
      "main": [
        [
          {
            "node": "Filter Valid URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Valid URLs": {
      "main": [
        [
          {
            "node": "Remove Duplicate URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicate URLs": {
      "main": [
        [
          {
            "node": "Loop Over Businesses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Businesses": {
      "main": [
        [
          {
            "node": "Filter Has Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Scrape Website",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Website": {
      "main": [
        [
          {
            "node": "Rate Limit Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Wait": {
      "main": [
        [
          {
            "node": "Extract Contact Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Contact Info": {
      "main": [
        [
          {
            "node": "Loop Over Businesses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Has Email": {
      "main": [
        [
          {
            "node": "Split Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Emails": {
      "main": [
        [
          {
            "node": "Score Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Score Leads": {
      "main": [
        [
          {
            "node": "Check Airtable Exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Airtable Exists": {
      "main": [
        [
          {
            "node": "Is New Lead?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is New Lead?": {
      "main": [
        [
          {
            "node": "Create in Airtable",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update in Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create in Airtable": {
      "main": [
        [
          {
            "node": "Append to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update in Airtable": {
      "main": [
        [
          {
            "node": "Append to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append to Sheets": {
      "main": [
        [
          {
            "node": "Filter New Leads",
            "type": "main",
            "index": 0
          },
          {
            "node": "Aggregate Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter New Leads": {
      "main": [
        [
          {
            "node": "Rate Limit Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Emails": {
      "main": [
        [
          {
            "node": "AI Generate Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Email": {
      "main": [
        [
          {
            "node": "Send Outreach Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Outreach Email": {
      "main": [
        [
          {
            "node": "Update Lead Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Results": {
      "main": [
        [
          {
            "node": "Send Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Error Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Generate Email": {
      "main": [
        [
          {
            "node": "Format Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Generate Email",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false,
    "callerPolicy": "workflowsFromSameOwner"
  },
  "staticData": {
    "node:Schedule Trigger": {
      "recurrenceRules": []
    }
  },
  "meta": {
    "templateId": "5385",
    "templateCredsSetupCompleted": true
  },
  "versionId": "d62b4ce2-21b0-4bb9-a9c9-b0ee5051a348",
  "activeVersionId": "d62b4ce2-21b0-4bb9-a9c9-b0ee5051a348",
  "versionCounter": 9,
  "triggerCount": 1,
  "shared": [
    {
      "updatedAt": "2026-01-20T02:37:46.218Z",
      "createdAt": "2026-01-20T02:37:46.218Z",
      "role": "workflow:owner",
      "workflowId": "uq4hnH0YHfhYOOzO",
      "projectId": "pcmgdUiECq7jnQCv",
      "project": {
        "updatedAt": "2025-11-18T07:15:09.271Z",
        "createdAt": "2025-04-12T06:49:26.305Z",
        "id": "pcmgdUiECq7jnQCv",
        "name": "Ian Immelman <ian@anyvisionmedia.com>",
        "type": "personal",
        "icon": null,
        "description": null,
        "creatorId": "74ac6501-35e4-401e-af85-5fe3fc463160",
        "projectRelations": [
          {
            "updatedAt": "2025-04-12T06:49:26.305Z",
            "createdAt": "2025-04-12T06:49:26.305Z",
            "userId": "74ac6501-35e4-401e-af85-5fe3fc463160",
            "projectId": "pcmgdUiECq7jnQCv",
            "user": {
              "updatedAt": "2026-02-11T02:31:29.000Z",
              "createdAt": "2025-04-12T06:49:24.842Z",
              "id": "74ac6501-35e4-401e-af85-5fe3fc463160",
              "email": "ian@anyvisionmedia.com",
              "firstName": "Ian",
              "lastName": "Immelman",
              "personalizationAnswers": null,
              "settings": {
                "userActivated": true,
                "easyAIWorkflowOnboarded": true,
                "userClaimedAiCredits": true,
                "firstSuccessfulWorkflowId": "u0bLAklw7ax799uj",
                "userActivatedAt": 1762780853271,
                "npsSurvey": {
                  "responded": true,
                  "lastShownAt": 1763368708549
                }
              },
              "disabled": false,
              "mfaEnabled": false,
              "lastActiveAt": "2026-02-10",
              "isPending": false
            }
          }
        ]
      }
    }
  ],
  "tags": [],
  "activeVersion": {
    "updatedAt": "2026-02-11T07:52:30.444Z",
    "createdAt": "2026-02-11T07:52:30.444Z",
    "versionId": "d62b4ce2-21b0-4bb9-a9c9-b0ee5051a348",
    "workflowId": "uq4hnH0YHfhYOOzO",
    "nodes": [
      {
        "parameters": {
          "rule": {
            "interval": [
              {
                "field": "weeks",
                "triggerAtDay": [
                  1
                ],
                "triggerAtHour": 9
              }
            ]
          }
        },
        "id": "8b09efbe-4322-4e7f-8512-7805878088f9",
        "name": "Schedule Trigger",
        "type": "n8n-nodes-base.scheduleTrigger",
        "position": [
          200,
          300
        ],
        "typeVersion": 1.2
      },
      {
        "parameters": {},
        "id": "d35df8b8-f968-443c-8b89-39a59c0c5b6c",
        "name": "Manual Trigger",
        "type": "n8n-nodes-base.manualTrigger",
        "position": [
          200,
          500
        ],
        "typeVersion": 1
      },
      {
        "parameters": {
          "mode": "manual",
          "duplicateItem": false,
          "assignments": {
            "assignments": [
              {
                "id": "84cf11db-2680-4463-a454-c699becb72ee",
                "name": "searchQuery",
                "value": "dentists",
                "type": "string"
              },
              {
                "id": "f08cdf9d-8546-4dd3-b7eb-b94bbef7c532",
                "name": "location",
                "value": "Johannesburg",
                "type": "string"
              },
              {
                "id": "9e4af1d2-7149-45af-b052-9ab642a6754e",
                "name": "maxResults",
                "value": "50",
                "type": "number"
              },
              {
                "id": "115ebffa-b1b5-499a-a3a6-e37a19f90c2f",
                "name": "senderName",
                "value": "Ian Immelman",
                "type": "string"
              },
              {
                "id": "6a950459-74af-480d-a378-940e0b505ba7",
                "name": "senderCompany",
                "value": "AnyVision Media",
                "type": "string"
              },
              {
                "id": "8e8b75f3-f674-46fe-9fb4-6560bd139785",
                "name": "senderTitle",
                "value": "Director",
                "type": "string"
              },
              {
                "id": "7de74b4e-fbf6-434d-8340-552d9d6f468c",
                "name": "senderEmail",
                "value": "ian@anyvisionmedia.com",
                "type": "string"
              }
            ]
          }
        },
        "id": "d9c299bf-1039-4bb6-8c83-9025d9f67d29",
        "name": "Search Config",
        "type": "n8n-nodes-base.set",
        "position": [
          440,
          400
        ],
        "typeVersion": 3.4
      },
      {
        "parameters": {
          "jsCode": "const config = $input.first().json;\nconst query = encodeURIComponent(config.location + ' ' + config.searchQuery);\nconst url = 'https://www.google.com/maps/search/' + query;\nreturn {\n  json: {\n    ...config,\n    mapsUrl: url\n  }\n};"
        },
        "id": "de1745b8-9a52-4853-88e0-f120b7628682",
        "name": "Build Maps URL",
        "type": "n8n-nodes-base.code",
        "position": [
          660,
          400
        ],
        "typeVersion": 2
      },
      {
        "parameters": {
          "url": "={{ $json.mapsUrl }}",
          "options": {
            "allowUnauthorizedCerts": true,
            "response": {
              "response": {
                "fullResponse": true
              }
            }
          }
        },
        "id": "0833296b-0d6b-4ce3-acf5-5f1cf4a7606f",
        "name": "Scrape Google Maps",
        "type": "n8n-nodes-base.httpRequest",
        "position": [
          880,
          400
        ],
        "typeVersion": 4.2,
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {
          "jsCode": "const html = $input.first().json.data || '';\nconst config = $('Search Config').first().json;\n\n// Extract website URLs from Google Maps HTML\nconst urlRegex = /https?:\\/\\/[^\\/\\s\"'>]+/g;\nconst allUrls = html.match(urlRegex) || [];\n\n// Filter out Google/system domains\nconst blockedDomains = ['google', 'gstatic', 'googleapis', 'schema', 'ggpht', 'youtube', 'goo.gl'];\nconst validUrls = [...new Set(allUrls)].filter(url => {\n  const lower = url.toLowerCase();\n  return !blockedDomains.some(d => lower.includes(d));\n});\n\n// Try to extract business names from Maps HTML\n// Google Maps embeds business data in various patterns\nconst nameRegex = /\\[\"([^\"]{3,60})\"(?:,null){0,3},\"https?:\\/\\//g;\nconst names = [];\nlet nameMatch;\nwhile ((nameMatch = nameRegex.exec(html)) !== null) {\n  names.push(nameMatch[1]);\n}\n\n// Try to extract addresses\nconst addressRegex = /\\[\"(\\d+[^\"]{5,80}(?:St|Street|Ave|Avenue|Rd|Road|Dr|Drive|Blvd|Way|Lane|Ln|Ct|Pl)[^\"]{0,40})\"/gi;\nconst addresses = [];\nlet addrMatch;\nwhile ((addrMatch = addressRegex.exec(html)) !== null) {\n  addresses.push(addrMatch[1]);\n}\n\n// Try to extract phone numbers from Maps data\nconst phoneRegex = /(?:\\+\\d{1,3}[\\s.-]?)?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}/g;\nconst phones = [...new Set((html.match(phoneRegex) || []))];\n\n// Try to extract ratings\nconst ratingRegex = /\\[(\\d\\.\\d),\"\\d+ review/g;\nconst ratings = [];\nlet rateMatch;\nwhile ((rateMatch = ratingRegex.exec(html)) !== null) {\n  ratings.push(parseFloat(rateMatch[1]));\n}\n\n// Build business objects - match URLs with any extracted metadata\nconst maxResults = parseInt(config.maxResults) || 50;\nconst businesses = validUrls.slice(0, maxResults).map((url, i) => ({\n  businessName: names[i] || '',\n  website: url.split('/').slice(0, 3).join('/'),\n  address: addresses[i] || '',\n  phone: phones[i] || '',\n  rating: ratings[i] || 0,\n  industry: config.searchQuery,\n  location: config.location\n}));\n\nif (businesses.length === 0) {\n  return [{ json: { error: 'No businesses found', urlCount: allUrls.length } }];\n}\n\nreturn businesses.map(b => ({ json: b }));"
        },
        "id": "0de6edb7-d8c9-4d8d-a520-792acfe09704",
        "name": "Extract Business Data",
        "type": "n8n-nodes-base.code",
        "position": [
          1100,
          400
        ],
        "typeVersion": 2,
        "alwaysOutputData": true,
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "version": 2,
              "leftValue": "",
              "caseSensitive": true,
              "typeValidation": "strict"
            },
            "combinator": "and",
            "conditions": [
              {
                "id": "78e62e38-359d-41c5-812d-08a65777e4ed",
                "operator": {
                  "type": "string",
                  "operation": "notContains"
                },
                "leftValue": "={{ $json.website }}",
                "rightValue": "google"
              },
              {
                "id": "0f8aff4a-667a-408d-8020-e9de3e58e925",
                "operator": {
                  "type": "string",
                  "operation": "notContains"
                },
                "leftValue": "={{ $json.website }}",
                "rightValue": "gstatic"
              },
              {
                "id": "bee50ccd-bedb-4eb5-a7f9-7edf95b5ff40",
                "operator": {
                  "type": "string",
                  "operation": "exists",
                  "singleValue": true
                },
                "leftValue": "={{ $json.website }}",
                "rightValue": ""
              }
            ]
          },
          "options": {}
        },
        "id": "f05bb8c6-dd38-40b7-9ae1-441dd1956979",
        "name": "Filter Valid URLs",
        "type": "n8n-nodes-base.filter",
        "position": [
          1320,
          400
        ],
        "typeVersion": 2.2
      },
      {
        "parameters": {
          "compareValue": "={{ $json.website }}",
          "options": {}
        },
        "id": "cba71d67-6d61-4bab-aab2-cef1454acb40",
        "name": "Remove Duplicate URLs",
        "type": "n8n-nodes-base.removeDuplicates",
        "position": [
          1520,
          400
        ],
        "typeVersion": 2
      },
      {
        "parameters": {
          "options": {}
        },
        "id": "46e3ad26-9572-438a-93ce-7cde4fd68622",
        "name": "Loop Over Businesses",
        "type": "n8n-nodes-base.splitInBatches",
        "position": [
          1720,
          400
        ],
        "typeVersion": 3
      },
      {
        "parameters": {
          "url": "={{ $json.website }}",
          "options": {
            "allowUnauthorizedCerts": true,
            "timeout": 10000,
            "response": {
              "response": {
                "fullResponse": true
              }
            }
          }
        },
        "id": "c1ee4f05-acfc-47ca-ae40-9695f7c2402a",
        "name": "Scrape Website",
        "type": "n8n-nodes-base.httpRequest",
        "position": [
          1920,
          600
        ],
        "typeVersion": 4.2,
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {
          "amount": 2
        },
        "id": "c8518cba-78ff-4450-b4bd-5f92ef46d4ad",
        "name": "Rate Limit Wait",
        "type": "n8n-nodes-base.wait",
        "position": [
          2140,
          600
        ],
        "webhookId": "2e42a648-14ef-46f2-8186-966019c04687",
        "typeVersion": 1.1
      },
      {
        "parameters": {
          "jsCode": "const html = $input.first().json.data || $input.first().json.body || '';\nconst original = $('Loop Over Businesses').item.json;\n\n// Email extraction (exclude image extensions)\nconst emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.(?!jpeg|jpg|png|gif|webp|svg|css|js)[a-zA-Z]{2,}/g;\nconst rawEmails = html.match(emailRegex) || [];\nconst emails = [...new Set(rawEmails.map(e => e.toLowerCase()))];\n\n// Phone extraction\nconst phoneRegex = /(?:\\+\\d{1,3}[\\s.-]?)?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}/g;\nconst rawPhones = html.match(phoneRegex) || [];\nconst phones = [...new Set(rawPhones)].filter(p => p.replace(/\\D/g, '').length >= 7);\n\n// Social media extraction\nconst linkedinRegex = /https?:\\/\\/(?:www\\.)?linkedin\\.com\\/(?:company|in)\\/[^\\s\"'<>)]+/gi;\nconst facebookRegex = /https?:\\/\\/(?:www\\.)?facebook\\.com\\/[^\\s\"'<>)]+/gi;\nconst instagramRegex = /https?:\\/\\/(?:www\\.)?instagram\\.com\\/[^\\s\"'<>)]+/gi;\n\nconst linkedin = (html.match(linkedinRegex) || [])[0] || '';\nconst facebook = (html.match(facebookRegex) || [])[0] || '';\nconst instagram = (html.match(instagramRegex) || [])[0] || '';\n\n// Merge with original business data\nconst result = {\n  businessName: original.businessName || '',\n  website: original.website || '',\n  address: original.address || '',\n  phone: phones[0] || original.phone || '',\n  rating: original.rating || 0,\n  industry: original.industry || '',\n  location: original.location || '',\n  emails: emails,\n  linkedin: linkedin,\n  facebook: facebook,\n  instagram: instagram\n};\n\nreturn { json: result };"
        },
        "id": "32891ac9-3b4f-4267-a6c6-2ec5bd19b018",
        "name": "Extract Contact Info",
        "type": "n8n-nodes-base.code",
        "position": [
          2360,
          600
        ],
        "typeVersion": 2,
        "alwaysOutputData": true,
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "version": 2,
              "leftValue": "",
              "caseSensitive": true,
              "typeValidation": "strict"
            },
            "combinator": "and",
            "conditions": [
              {
                "id": "070f22a6-f970-4efa-928e-3365f444cb1e",
                "operator": {
                  "type": "array",
                  "operation": "notEmpty",
                  "singleValue": true
                },
                "leftValue": "={{ $json.emails }}",
                "rightValue": ""
              }
            ]
          },
          "options": {}
        },
        "id": "d63f5e84-70e9-429a-b5ea-dd81b9dd24a4",
        "name": "Filter Has Email",
        "type": "n8n-nodes-base.filter",
        "position": [
          1920,
          260
        ],
        "typeVersion": 2.2
      },
      {
        "parameters": {
          "fieldToSplitOut": "emails",
          "options": {}
        },
        "id": "94b26752-5433-41a7-90c6-90f04b6973ba",
        "name": "Split Emails",
        "type": "n8n-nodes-base.splitOut",
        "position": [
          2140,
          260
        ],
        "typeVersion": 1
      },
      {
        "parameters": {
          "jsCode": "const items = $input.all();\nconst seen = new Set();\nconst results = [];\n\nfor (const item of items) {\n  const email = (item.json.emails || '').toString().toLowerCase().trim();\n  if (!email || seen.has(email)) continue;\n  seen.add(email);\n\n  // Calculate lead score (0-100)\n  let score = 0;\n  if (email) score += 20;\n  if (item.json.phone) score += 15;\n  if (item.json.businessName) score += 15;\n  if (item.json.address) score += 10;\n  if (item.json.rating > 0) score += 10;\n  if (item.json.linkedin || item.json.facebook || item.json.instagram) score += 10;\n  if (item.json.website) score += 10;\n  if (item.json.phone && email) score += 10; // bonus for multiple contact methods\n\n  results.push({\n    json: {\n      ...item.json,\n      email: email,\n      leadScore: score,\n      status: 'New',\n      source: 'Google Maps Scraper',\n      datescraped: new Date().toISOString().split('T')[0]\n    }\n  });\n}\n\nreturn results.length > 0 ? results : [{ json: { _empty: true } }];"
        },
        "id": "8245be47-2574-4582-b81d-aae0890e2173",
        "name": "Score Leads",
        "type": "n8n-nodes-base.code",
        "position": [
          2360,
          260
        ],
        "typeVersion": 2,
        "alwaysOutputData": true
      },
      {
        "parameters": {
          "operation": "search",
          "base": {
            "__rl": true,
            "mode": "list",
            "value": "appzcZpiIZ6QPtJXT",
            "cachedResultName": "n8n Workflows"
          },
          "table": {
            "__rl": true,
            "mode": "list",
            "value": "tbludJQgwxtvcyo2Q",
            "cachedResultName": "Leads"
          },
          "filterByFormula": "=({Email} = \"{{ $json.email }}\")"
        },
        "id": "006a844f-efec-4965-bd74-ac1c75f68b51",
        "name": "Check Airtable Exists",
        "type": "n8n-nodes-base.airtable",
        "position": [
          2580,
          260
        ],
        "typeVersion": 2.1,
        "alwaysOutputData": true,
        "credentials": {
          "airtableTokenApi": {
            "id": "ZyBrcAO6fps7YB3u",
            "name": "Whatsapp Multi Agent"
          }
        }
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "version": 2,
              "leftValue": "",
              "caseSensitive": true,
              "typeValidation": "strict"
            },
            "combinator": "and",
            "conditions": [
              {
                "id": "b49bdf15-d3aa-4079-9324-3bef95556834",
                "operator": {
                  "type": "object",
                  "operation": "empty",
                  "singleValue": true
                },
                "leftValue": "={{ $json }}",
                "rightValue": ""
              }
            ]
          },
          "options": {}
        },
        "id": "92a5aad5-3acd-473a-bf09-35e4271ea76e",
        "name": "Is New Lead?",
        "type": "n8n-nodes-base.if",
        "position": [
          2800,
          260
        ],
        "typeVersion": 2.2
      },
      {
        "parameters": {
          "operation": "create",
          "base": {
            "__rl": true,
            "mode": "list",
            "value": "appzcZpiIZ6QPtJXT",
            "cachedResultName": "n8n Workflows"
          },
          "table": {
            "__rl": true,
            "mode": "list",
            "value": "tbludJQgwxtvcyo2Q",
            "cachedResultName": "Leads"
          },
          "columns": {
            "value": {
              "Business Name": "={{ $('Score Leads').item.json.businessName }}",
              "Email": "={{ $('Score Leads').item.json.email }}",
              "Phone": "={{ $('Score Leads').item.json.phone }}",
              "Website": "={{ $('Score Leads').item.json.website }}",
              "Address": "={{ $('Score Leads').item.json.address }}",
              "Industry": "={{ $('Score Leads').item.json.industry }}",
              "Location": "={{ $('Score Leads').item.json.location }}",
              "Rating": "={{ $('Score Leads').item.json.rating }}",
              "Social - LinkedIn": "={{ $('Score Leads').item.json.linkedin }}",
              "Social - Facebook": "={{ $('Score Leads').item.json.facebook }}",
              "Social - Instagram": "={{ $('Score Leads').item.json.instagram }}",
              "Lead Score": "={{ $('Score Leads').item.json.leadScore }}",
              "Status": "New",
              "Source": "Google Maps Scraper",
              "Date Scraped": "={{ $('Score Leads').item.json.datescraped }}"
            },
            "schema": [
              {
                "id": "Business Name",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Business Name",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Email",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Email",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Phone",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Phone",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Website",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Website",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Address",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Address",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Industry",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Industry",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Location",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Location",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Rating",
                "type": "number",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Rating",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Social - LinkedIn",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Social - LinkedIn",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Social - Facebook",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Social - Facebook",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Social - Instagram",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Social - Instagram",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Lead Score",
                "type": "number",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Lead Score",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Status",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Status",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Source",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Source",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Date Scraped",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Date Scraped",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              }
            ],
            "mappingMode": "defineBelow",
            "matchingColumns": [
              "Email"
            ]
          },
          "options": {}
        },
        "id": "83c36de1-20d9-4e4a-80bc-611c55076342",
        "name": "Create in Airtable",
        "type": "n8n-nodes-base.airtable",
        "position": [
          3040,
          160
        ],
        "typeVersion": 2.1,
        "credentials": {
          "airtableTokenApi": {
            "id": "ZyBrcAO6fps7YB3u",
            "name": "Whatsapp Multi Agent"
          }
        }
      },
      {
        "parameters": {
          "operation": "update",
          "base": {
            "__rl": true,
            "mode": "list",
            "value": "appzcZpiIZ6QPtJXT",
            "cachedResultName": "n8n Workflows"
          },
          "table": {
            "__rl": true,
            "mode": "list",
            "value": "tbludJQgwxtvcyo2Q",
            "cachedResultName": "Leads"
          },
          "columns": {
            "value": {
              "Phone": "={{ $('Score Leads').item.json.phone }}",
              "Social - LinkedIn": "={{ $('Score Leads').item.json.linkedin }}",
              "Social - Facebook": "={{ $('Score Leads').item.json.facebook }}",
              "Social - Instagram": "={{ $('Score Leads').item.json.instagram }}",
              "Lead Score": "={{ $('Score Leads').item.json.leadScore }}"
            },
            "schema": [
              {
                "id": "Phone",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Phone",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Social - LinkedIn",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Social - LinkedIn",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Social - Facebook",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Social - Facebook",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Social - Instagram",
                "type": "string",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Social - Instagram",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              },
              {
                "id": "Lead Score",
                "type": "number",
                "display": true,
                "removed": false,
                "required": false,
                "displayName": "Lead Score",
                "defaultMatch": false,
                "canBeUsedToMatch": true
              }
            ],
            "mappingMode": "defineBelow",
            "matchingColumns": [
              "Email"
            ]
          },
          "options": {}
        },
        "id": "dbe06f00-f18e-4785-8d4a-9e76bbede172",
        "name": "Update in Airtable",
        "type": "n8n-nodes-base.airtable",
        "position": [
          3040,
          380
        ],
        "typeVersion": 2.1,
        "credentials": {
          "airtableTokenApi": {
            "id": "ZyBrcAO6fps7YB3u",
            "name": "Whatsapp Multi Agent"
          }
        }
      },
      {
        "parameters": {
          "operation": "append",
          "documentId": {
            "__rl": true,
            "value": "1E9_OSvO6F37iG9wh_gaetPT3IzuwdeSNbomIPXzKu94",
            "mode": "list",
            "cachedResultName": "LEAD GEN EMAILSCRAPER",
            "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1E9_OSvO6F37iG9wh_gaetPT3IzuwdeSNbomIPXzKu94/edit?usp=drivesdk"
          },
          "sheetName": {
            "__rl": true,
            "value": "gid=0",
            "mode": "list",
            "cachedResultName": "Sheet1",
            "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1E9_OSvO6F37iG9wh_gaetPT3IzuwdeSNbomIPXzKu94/edit#gid=0"
          },
          "columns": {
            "value": {
              "Business Name": "={{ $('Score Leads').item.json.businessName }}",
              "Email": "={{ $('Score Leads').item.json.email }}",
              "Phone": "={{ $('Score Leads').item.json.phone }}",
              "Website": "={{ $('Score Leads').item.json.website }}",
              "Address": "={{ $('Score Leads').item.json.address }}",
              "Industry": "={{ $('Score Leads').item.json.industry }}",
              "Location": "={{ $('Score Leads').item.json.location }}",
              "Rating": "={{ $('Score Leads').item.json.rating }}",
              "LinkedIn": "={{ $('Score Leads').item.json.linkedin }}",
              "Facebook": "={{ $('Score Leads').item.json.facebook }}",
              "Instagram": "={{ $('Score Leads').item.json.instagram }}",
              "Lead Score": "={{ $('Score Leads').item.json.leadScore }}",
              "Status": "={{ $('Score Leads').item.json.status }}",
              "Date Scraped": "={{ $('Score Leads').item.json.datescraped }}"
            },
            "schema": [
              {
                "id": "Business Name",
                "type": "string",
                "display": true,
                "displayName": "Business Name"
              },
              {
                "id": "Email",
                "type": "string",
                "display": true,
                "displayName": "Email"
              },
              {
                "id": "Phone",
                "type": "string",
                "display": true,
                "displayName": "Phone"
              },
              {
                "id": "Website",
                "type": "string",
                "display": true,
                "displayName": "Website"
              },
              {
                "id": "Address",
                "type": "string",
                "display": true,
                "displayName": "Address"
              },
              {
                "id": "Industry",
                "type": "string",
                "display": true,
                "displayName": "Industry"
              },
              {
                "id": "Location",
                "type": "string",
                "display": true,
                "displayName": "Location"
              },
              {
                "id": "Rating",
                "type": "string",
                "display": true,
                "displayName": "Rating"
              },
              {
                "id": "LinkedIn",
                "type": "string",
                "display": true,
                "displayName": "LinkedIn"
              },
              {
                "id": "Facebook",
                "type": "string",
                "display": true,
                "displayName": "Facebook"
              },
              {
                "id": "Instagram",
                "type": "string",
                "display": true,
                "displayName": "Instagram"
              },
              {
                "id": "Lead Score",
                "type": "string",
                "display": true,
                "displayName": "Lead Score"
              },
              {
                "id": "Status",
                "type": "string",
                "display": true,
                "displayName": "Status"
              },
              {
                "id": "Date Scraped",
                "type": "string",
                "display": true,
                "displayName": "Date Scraped"
              }
            ],
            "mappingMode": "defineBelow",
            "matchingColumns": [
              "Email"
            ]
          },
          "options": {
            "useAppend": true
          }
        },
        "id": "8ef8bdff-836f-4128-9e18-b345431d3dde",
        "name": "Append to Sheets",
        "type": "n8n-nodes-base.googleSheets",
        "position": [
          3280,
          260
        ],
        "typeVersion": 4.5,
        "credentials": {
          "googleSheetsOAuth2Api": {
            "id": "OkpDXxwI8WcUJp4P",
            "name": "Google Sheets AVM Tutorial"
          }
        }
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "version": 2,
              "leftValue": "",
              "caseSensitive": true,
              "typeValidation": "strict"
            },
            "combinator": "and",
            "conditions": [
              {
                "id": "c2682c88-5122-4418-8da2-97fe49badfca",
                "operator": {
                  "type": "string",
                  "operation": "exists",
                  "singleValue": true
                },
                "leftValue": "={{ $json.id }}",
                "rightValue": ""
              }
            ]
          },
          "options": {}
        },
        "id": "dc5f251d-e3c0-4e4a-8fc6-7e402e48bc1c",
        "name": "Filter New Leads",
        "type": "n8n-nodes-base.filter",
        "position": [
          3500,
          160
        ],
        "typeVersion": 2.2
      },
      {
        "parameters": {
          "amount": 30
        },
        "id": "9886589d-0ff5-4076-b2ee-249a20d56186",
        "name": "Rate Limit Emails",
        "type": "n8n-nodes-base.wait",
        "position": [
          3700,
          160
        ],
        "webhookId": "aa030ef6-ae29-4f65-9c14-aac7aefa8f14",
        "typeVersion": 1.1
      },
      {
        "parameters": {
          "jsCode": "const input = $input.first().json;\nconst leadData = $('Score Leads').item.json;\nconst config = $('Search Config').first().json;\n\n// Parse AI response - chainLlm returns text in 'text' field\nlet emailContent;\ntry {\n  const rawText = input.text || input.response || JSON.stringify(input);\n  const jsonMatch = rawText.match(/\\{[\\s\\S]*\\}/);\n  emailContent = JSON.parse(jsonMatch[0]);\n} catch (e) {\n  emailContent = {\n    subject: 'Partnership opportunity for ' + (leadData.businessName || 'your business'),\n    body: 'I noticed ' + (leadData.businessName || 'your business') + ' in ' + leadData.location + ' and wanted to reach out about a potential collaboration that could benefit your ' + leadData.industry + ' practice.',\n    cta_text: 'Would a brief 10-minute call this week work to explore this?'\n  };\n}\n\nconst htmlBody = '<div style=\"font-family:Segoe UI,Arial,sans-serif;max-width:600px;margin:0 auto;background:#fff;\">' +\n  '<div style=\"padding:30px 40px 20px;border-bottom:3px solid #FF6D5A;\">' +\n  '<h1 style=\"margin:0;font-size:22px;color:#1A1A2E;\">' + config.senderCompany + '</h1></div>' +\n  '<div style=\"padding:30px 40px;\">' +\n  '<p style=\"font-size:15px;line-height:1.6;color:#333;\">' + emailContent.body + '</p>' +\n  '<p style=\"font-size:15px;line-height:1.6;color:#333;\">' + emailContent.cta_text + '</p>' +\n  '<p style=\"font-size:15px;line-height:1.6;color:#333;margin-top:24px;\">Best regards,<br>' +\n  '<strong>' + config.senderName + '</strong><br>' +\n  '<span style=\"color:#666;\">' + config.senderTitle + '</span><br>' +\n  '<span style=\"color:#666;\">' + config.senderCompany + '</span></p></div>' +\n  '<div style=\"padding:20px 40px;background:#f8f8f8;border-top:1px solid #eee;\">' +\n  '<p style=\"font-size:11px;color:#999;\">You received this because your business was listed publicly on Google Maps. Reply &quot;unsubscribe&quot; to be removed.</p></div></div>';\n\nreturn {\n  json: {\n    to: leadData.email,\n    subject: emailContent.subject,\n    htmlBody: htmlBody,\n    leadEmail: leadData.email,\n    businessName: leadData.businessName\n  }\n};"
        },
        "id": "a9e237e5-cf9a-46d0-94af-f4717e8de8f1",
        "name": "Format Email",
        "type": "n8n-nodes-base.code",
        "position": [
          4140,
          160
        ],
        "typeVersion": 2,
        "alwaysOutputData": true,
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {
          "sendTo": "={{ $json.to }}",
          "subject": "={{ $json.subject }}",
          "emailType": "html",
          "message": "={{ $json.htmlBody }}",
          "options": {}
        },
        "id": "ce1c44f4-fbbd-4937-b420-ac4c6971be48",
        "name": "Send Outreach Email",
        "type": "n8n-nodes-base.gmail",
        "position": [
          4360,
          160
        ],
        "typeVersion": 2.1,
        "credentials": {
          "gmailOAuth2": {
            "id": "2IuycrTIgWJZEjBE",
            "name": "Gmail account AVM Tutorial"
          }
        },
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {
          "operation": "update",
          "base": {
            "__rl": true,
            "mode": "list",
            "value": "appzcZpiIZ6QPtJXT",
            "cachedResultName": "n8n Workflows"
          },
          "table": {
            "__rl": true,
            "mode": "list",
            "value": "tbludJQgwxtvcyo2Q",
            "cachedResultName": "Leads"
          },
          "columns": {
            "value": {
              "Status": "Email Sent",
              "Email Sent Date": "={{ new Date().toISOString().split('T')[0] }}",
              "Notes": "={{ $('Format Email').item.json.subject }}"
            },
            "schema": [
              {
                "id": "Status",
                "type": "string",
                "display": true,
                "displayName": "Status"
              },
              {
                "id": "Email Sent Date",
                "type": "string",
                "display": true,
                "displayName": "Email Sent Date"
              },
              {
                "id": "Notes",
                "type": "string",
                "display": true,
                "displayName": "Notes"
              }
            ],
            "mappingMode": "defineBelow",
            "matchingColumns": [
              "Email"
            ]
          },
          "options": {}
        },
        "id": "c670553e-beea-4a4e-a2c3-8b479ff572e8",
        "name": "Update Lead Status",
        "type": "n8n-nodes-base.airtable",
        "position": [
          4580,
          160
        ],
        "typeVersion": 2.1,
        "credentials": {
          "airtableTokenApi": {
            "id": "ZyBrcAO6fps7YB3u",
            "name": "Whatsapp Multi Agent"
          }
        },
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {
          "jsCode": "const items = $('Score Leads').all();\nconst config = $('Search Config').first().json;\n\nconst totalLeads = items.filter(i => !i.json._empty).length;\nconst avgScore = totalLeads > 0\n  ? Math.round(items.reduce((sum, i) => sum + (i.json.leadScore || 0), 0) / totalLeads)\n  : 0;\n\nconst now = new Date().toLocaleString('en-ZA', { timeZone: 'Africa/Johannesburg' });\n\nreturn {\n  json: {\n    subject: `Lead Scraper Complete: ${totalLeads} leads found`,\n    body: [\n      `<h2>Lead Scraper Run Complete</h2>`,\n      `<p><strong>Date:</strong> ${now}</p>`,\n      `<p><strong>Search:</strong> ${config.searchQuery} in ${config.location}</p>`,\n      `<hr>`,\n      `<p><strong>Total Leads Found:</strong> ${totalLeads}</p>`,\n      `<p><strong>Average Lead Score:</strong> ${avgScore}/100</p>`,\n      `<hr>`,\n      `<p>Check your <a href=\"https://airtable.com/${'appzcZpiIZ6QPtJXT'\">Airtable CRM</a> for details.</p>`\n    ].join('\\n')\n  }\n};"
        },
        "id": "1f7002bf-1734-4722-abe2-5562e549dbcb",
        "name": "Aggregate Results",
        "type": "n8n-nodes-base.code",
        "position": [
          3500,
          460
        ],
        "typeVersion": 2,
        "alwaysOutputData": true
      },
      {
        "parameters": {
          "sendTo": "ian@anyvisionmedia.com",
          "subject": "={{ $json.subject }}",
          "emailType": "html",
          "message": "={{ $json.body }}",
          "options": {}
        },
        "id": "88766fe8-6c3c-48ec-aa0b-215ae375d327",
        "name": "Send Summary",
        "type": "n8n-nodes-base.gmail",
        "position": [
          3720,
          460
        ],
        "typeVersion": 2.1,
        "credentials": {
          "gmailOAuth2": {
            "id": "2IuycrTIgWJZEjBE",
            "name": "Gmail account AVM Tutorial"
          }
        },
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {},
        "id": "96b86d80-eaf5-492b-b698-82ffc461c3e7",
        "name": "Error Trigger",
        "type": "n8n-nodes-base.errorTrigger",
        "position": [
          200,
          740
        ],
        "typeVersion": 1
      },
      {
        "parameters": {
          "sendTo": "ian@anyvisionmedia.com",
          "subject": "Lead Scraper ERROR - {{ $json.workflow.name }}",
          "emailType": "html",
          "message": "=<h2>Workflow Error Alert</h2>\n<p><strong>Workflow:</strong> {{ $json.workflow.name }}</p>\n<p><strong>Error:</strong> {{ $json.execution.error.message }}</p>\n<p><strong>Node:</strong> {{ $json.execution.lastNodeExecuted }}</p>\n<p><a href=\"{{ $json.execution.url }}\">View Execution</a></p>",
          "options": {}
        },
        "id": "2e26c609-1352-4d70-ab2c-e3ed8c930a2c",
        "name": "Error Notification",
        "type": "n8n-nodes-base.gmail",
        "position": [
          440,
          740
        ],
        "typeVersion": 2.1,
        "credentials": {
          "gmailOAuth2": {
            "id": "2IuycrTIgWJZEjBE",
            "name": "Gmail account AVM Tutorial"
          }
        }
      },
      {
        "parameters": {
          "content": "## STAGE 1: Triggers & Configuration\n\n**Schedule:** Runs weekly on Monday 9AM\n**Manual:** Click 'Test workflow' for ad-hoc runs\n**Config:** Set your search query, location, and sender details",
          "height": 140,
          "width": 340
        },
        "id": "sticky-1",
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          160,
          140
        ],
        "typeVersion": 1,
        "name": "Note 1"
      },
      {
        "parameters": {
          "content": "## STAGE 2: Google Maps Scraping\n\nScrapes Google Maps HTML for business listings.\nExtracts names, websites, addresses, phones, ratings.",
          "height": 120,
          "width": 340
        },
        "id": "sticky-2",
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          620,
          240
        ],
        "typeVersion": 1,
        "name": "Note 2"
      },
      {
        "parameters": {
          "content": "## STAGE 3: Website Scraping & Enrichment\n\nVisits each business website with rate limiting.\nExtracts emails, phone numbers, and social media links.",
          "height": 120,
          "width": 360
        },
        "id": "sticky-3",
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          1280,
          520
        ],
        "typeVersion": 1,
        "name": "Note 3"
      },
      {
        "parameters": {
          "content": "## STAGE 4: Scoring & Dedup\n\nScores leads 0-100 based on data completeness.\nDeduplicates by email address.",
          "height": 100,
          "width": 340
        },
        "id": "sticky-4",
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          1880,
          120
        ],
        "typeVersion": 1,
        "name": "Note 4"
      },
      {
        "parameters": {
          "content": "## STAGE 5: CRM Storage\n\n**Airtable** (primary): Creates new or updates existing records\n**Google Sheets** (mirror): Appends all leads for easy sharing",
          "height": 100,
          "width": 380
        },
        "id": "sticky-5",
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          2540,
          80
        ],
        "typeVersion": 1,
        "name": "Note 5"
      },
      {
        "parameters": {
          "content": "## STAGE 6: AI Email Outreach\n\nAI generates personalized cold email per lead.\nSent via Gmail with branded HTML template.\nAirtable status updated to 'Email Sent'.",
          "height": 100,
          "width": 380
        },
        "id": "sticky-6",
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          3460,
          40
        ],
        "typeVersion": 1,
        "name": "Note 6"
      },
      {
        "parameters": {
          "content": "## STAGE 7: Notifications\n\nSummary email after each run.\nError alerts on failures.",
          "height": 80,
          "width": 340
        },
        "id": "sticky-7",
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          3460,
          380
        ],
        "typeVersion": 1,
        "name": "Note 7"
      },
      {
        "parameters": {
          "prompt": {
            "value": "={{ \"You are a professional business development writer. Write a short, personalized cold outreach email.\\n\\nINSTRUCTIONS:\\n1. Subject line (max 50 chars) specific to their business\\n2. Personalized opening referencing their industry\\n3. 2-3 sentences with clear value proposition\\n4. Low-commitment CTA\\n5. Under 150 words\\n6. Professional, friendly - NOT salesy\\n\\nOUTPUT FORMAT (JSON only, no markdown):\\n{\\\"subject\\\": \\\"...\\\", \\\"body\\\": \\\"...\\\", \\\"cta_text\\\": \\\"...\\\"}\\n\\nBusiness: {businessName}\\nIndustry: {industry}\\nLocation: {location}\\nWebsite: {website}\".replace('{businessName}', $json.businessName || '').replace('{industry}', $json.industry || '').replace('{location}', $json.location || '').replace('{website}', $json.website || '') }}"
          },
          "hasOutputParser": false,
          "options": {}
        },
        "id": "190fe33f-b95d-49be-a55b-fbe14eb69f16",
        "name": "AI Generate Email",
        "type": "@n8n/n8n-nodes-langchain.chainLlm",
        "position": [
          3920,
          160
        ],
        "typeVersion": 1.4
      },
      {
        "parameters": {
          "model": "anthropic/claude-sonnet-4-20250514",
          "options": {
            "maxTokens": 500,
            "temperature": 0.7
          }
        },
        "id": "e7d1cd87-6dc4-4c46-b171-4dc1f8bfdea1",
        "name": "OpenRouter Model",
        "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
        "position": [
          3940,
          380
        ],
        "typeVersion": 1,
        "credentials": {
          "openRouterApi": {
            "id": "9ZgHenDBrFuyboov",
            "name": "OpenRouter 2WC"
          }
        }
      }
    ],
    "connections": {
      "Schedule Trigger": {
        "main": [
          [
            {
              "node": "Search Config",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Manual Trigger": {
        "main": [
          [
            {
              "node": "Search Config",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Search Config": {
        "main": [
          [
            {
              "node": "Build Maps URL",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Build Maps URL": {
        "main": [
          [
            {
              "node": "Scrape Google Maps",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Scrape Google Maps": {
        "main": [
          [
            {
              "node": "Extract Business Data",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Extract Business Data": {
        "main": [
          [
            {
              "node": "Filter Valid URLs",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Filter Valid URLs": {
        "main": [
          [
            {
              "node": "Remove Duplicate URLs",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Remove Duplicate URLs": {
        "main": [
          [
            {
              "node": "Loop Over Businesses",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Loop Over Businesses": {
        "main": [
          [
            {
              "node": "Filter Has Email",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Scrape Website",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Scrape Website": {
        "main": [
          [
            {
              "node": "Rate Limit Wait",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Rate Limit Wait": {
        "main": [
          [
            {
              "node": "Extract Contact Info",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Extract Contact Info": {
        "main": [
          [
            {
              "node": "Loop Over Businesses",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Filter Has Email": {
        "main": [
          [
            {
              "node": "Split Emails",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Split Emails": {
        "main": [
          [
            {
              "node": "Score Leads",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Score Leads": {
        "main": [
          [
            {
              "node": "Check Airtable Exists",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Check Airtable Exists": {
        "main": [
          [
            {
              "node": "Is New Lead?",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Is New Lead?": {
        "main": [
          [
            {
              "node": "Create in Airtable",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Update in Airtable",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Create in Airtable": {
        "main": [
          [
            {
              "node": "Append to Sheets",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Update in Airtable": {
        "main": [
          [
            {
              "node": "Append to Sheets",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Append to Sheets": {
        "main": [
          [
            {
              "node": "Filter New Leads",
              "type": "main",
              "index": 0
            },
            {
              "node": "Aggregate Results",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Filter New Leads": {
        "main": [
          [
            {
              "node": "Rate Limit Emails",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Rate Limit Emails": {
        "main": [
          [
            {
              "node": "AI Generate Email",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Format Email": {
        "main": [
          [
            {
              "node": "Send Outreach Email",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Send Outreach Email": {
        "main": [
          [
            {
              "node": "Update Lead Status",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Aggregate Results": {
        "main": [
          [
            {
              "node": "Send Summary",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Error Trigger": {
        "main": [
          [
            {
              "node": "Error Notification",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "AI Generate Email": {
        "main": [
          [
            {
              "node": "Format Email",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "OpenRouter Model": {
        "ai_languageModel": [
          [
            {
              "node": "AI Generate Email",
              "type": "ai_languageModel",
              "index": 0
            }
          ]
        ]
      }
    },
    "authors": "Ian Immelman",
    "name": null,
    "description": null,
    "autosaved": false,
    "workflowPublishHistory": [
      {
        "createdAt": "2026-02-11T07:52:30.876Z",
        "id": 10,
        "workflowId": "uq4hnH0YHfhYOOzO",
        "versionId": "d62b4ce2-21b0-4bb9-a9c9-b0ee5051a348",
        "event": "activated",
        "userId": "74ac6501-35e4-401e-af85-5fe3fc463160"
      }
    ]
  }
}