Skip to content

In n8n, copy this file and paste it onto the canvas, or use Import from File.

{
  "name": "18 - Lead Enrichment and Scoring (starter)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "leads/enrich",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "m18-webhook",
      "name": "Enrich Lead",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        200
      ],
      "webhookId": "course-18-enrich"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "s1",
              "name": "offer",
              "value": "We build automation and AI follow-up systems (online booking, reminders, lead follow-up) for local service businesses: clinics, dentists, law firms, home services and real estate teams.",
              "type": "string"
            },
            {
              "id": "s2",
              "name": "icp",
              "type": "object",
              "value": "={{ {\n  industries: {\n    'healthcare-clinics': ['dental', 'dentist', 'clinic', 'medical', 'chiropract', 'physio'],\n    'home-services': ['plumb', 'roofing', 'hvac', 'electrician', 'landscap', 'cleaning service'],\n    'legal': ['law firm', 'attorney', 'lawyer', 'legal services'],\n    'real-estate': ['real estate', 'realtor', 'property management', 'homes for sale'],\n    'ecommerce': ['online store', 'shop now', 'free shipping', 'add to cart'],\n    'saas': ['software', 'saas', 'platform', 'api']\n  },\n  target_industries: ['healthcare-clinics', 'home-services', 'legal', 'real-estate'],\n  good_tech: ['wordpress', 'hubspot', 'google_analytics', 'wix', 'squarespace']\n} }}"
            },
            {
              "id": "s3",
              "name": "hot_threshold",
              "value": 70,
              "type": "number"
            },
            {
              "id": "s4",
              "name": "warm_threshold",
              "value": 40,
              "type": "number"
            },
            {
              "id": "s5",
              "name": "sheet_url",
              "value": "https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "id": "m18-settings",
      "name": "Settings (edit me)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        200
      ]
    },
    {
      "parameters": {
        "jsCode": "// Clean the incoming lead and work out which website to look at.\nconst FREE_MAIL = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com', 'icloud.com', 'aol.com', 'proton.me', 'protonmail.com', 'live.com', 'msn.com'];\nconst req = $('Enrich Lead').first().json;\nconst b = req.body ?? {};\n\nconst email = String(b.email ?? '').trim().toLowerCase();\nconst emailDomain = email.includes('@') ? email.split('@')[1] : '';\nconst freeEmail = FREE_MAIL.includes(emailDomain);\n\nlet website = String(b.website ?? '').trim();\nif (!website && emailDomain && !freeEmail) website = emailDomain; // business email → guess the site\nif (website && !/^https?:\\/\\//i.test(website)) website = 'https://' + website;\n\n// Hostname without www (a regex: the Code sandbox may not provide URL())\nconst domain = website ? website.replace(/^https?:\\/\\//i, '').split(/[/:?#]/)[0].replace(/^www\\./, '').toLowerCase() : '';\n\nreturn [{\n  json: {\n    email,\n    name: String(b.name ?? '').trim(),\n    company: String(b.company ?? '').trim(),\n    website,\n    domain: domain || (freeEmail ? '' : emailDomain),\n    free_email: freeEmail,\n    valid: Boolean(email || website),\n    dry_run: req.query?.dry_run === 'true',\n    use_ai: req.query?.dry_run !== 'true' && req.query?.ai !== 'false',\n  },\n}];\n"
      },
      "id": "m18-normalize",
      "name": "Normalize Lead",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        200
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "v1",
              "leftValue": "={{ $json.valid }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "m18-valid",
      "name": "Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        660,
        200
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { error: 'send at least an email or a website: {\"email\": \"...\", \"website\": \"...\", \"name\": \"...\", \"company\": \"...\"}' } }}",
        "options": {
          "responseCode": 400
        }
      },
      "id": "m18-400",
      "name": "Respond Invalid",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        880,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "w1",
              "leftValue": "={{ $json.website }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "m18-hasweb",
      "name": "Has Website?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        880,
        120
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.website }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (compatible; lead-enrichment/1.0)"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "text"
            }
          },
          "timeout": 10000,
          "redirect": {
            "redirect": {
              "maxRedirects": 5
            }
          }
        }
      },
      "id": "m18-fetch",
      "name": "Fetch Website",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1100,
        40
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "content": "## Your task\nLoad the demo websites too (`./scripts/load-workflow.sh modules/18-lead-enrichment`), then enrich with\n`{\"email\": \"dana@acmedental.example\", \"website\": \"http://localhost:5678/webhook/course-18-demo-sites/demo-sites/acme-dental\"}` and `?dry_run=true`.\n1. **Extract Signals** (Code): title, description, tech (WordPress, HubSpot…), booking links, hiring, emails, phones, socials, address.\n2. **Rule Score**: points vs the ICP in Settings, with a reason for each.\n3. Optional **AI Profile** (Information Extractor, *On Error: Continue*): summary, industry, size, fit score, pitch angle.\n4. **Combine** → tier. Unless dry run: **Google Sheets** append-or-update by email (*On Error: Continue*), alert on hot leads.\n\nTest: `./test/test.sh`",
        "height": 260,
        "width": 420
      },
      "id": "starter-task-note",
      "name": "Your Task",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -340
      ]
    }
  ],
  "connections": {
    "Enrich Lead": {
      "main": [
        [
          {
            "node": "Settings (edit me)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Settings (edit me)": {
      "main": [
        [
          {
            "node": "Normalize Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Lead": {
      "main": [
        [
          {
            "node": "Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Valid?": {
      "main": [
        [
          {
            "node": "Has Website?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond Invalid",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Website?": {
      "main": [
        [
          {
            "node": "Fetch Website",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Fetch Website": {
      "main": [
        []
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  }
}