AutomationFlowsAI & RAG › Automated Website Audit & Personalized Outreach with Lighthouse and Gpt-4

Automated Website Audit & Personalized Outreach with Lighthouse and Gpt-4

ByShahrukh @shahrukhma on n8n.io

This workflow is perfect for marketing agencies, SEO consultants, and growth specialists who need to scale personalized outreach without spending hours on manual research.

Webhook trigger★★★★★ complexityAI-powered43 nodesGoogle SheetsHTTP RequestOpenAI
AI & RAG Trigger: Webhook Nodes: 43 Complexity: ★★★★★ AI nodes: yes Added:

This workflow corresponds to n8n.io template #5940 — we link there as the canonical source.

This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "G44pYgYD1odi0RqW",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "My workflow 2",
  "tags": [],
  "nodes": [
    {
      "id": "3d4e3710-7b7c-434a-8fa5-d29f4dfaa165",
      "name": "Wait1",
      "type": "n8n-nodes-base.wait",
      "position": [
        1640,
        340
      ],
      "parameters": {
        "amount": 25
      },
      "typeVersion": 1.1
    },
    {
      "id": "8236459e-12c0-480a-89de-8cce992d5f09",
      "name": "Get New Rows",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueErrorOutput",
      "position": [
        140,
        620
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "ff17bbac-59e9-4c67-a3c5-4f71fb1ba004",
      "name": "FirstRow",
      "type": "n8n-nodes-base.code",
      "position": [
        140,
        200
      ],
      "parameters": {
        "jsCode": "return [items[0]];"
      },
      "typeVersion": 2
    },
    {
      "id": "002ae828-5db7-4e57-b879-62840449580d",
      "name": "If Email is not Empty",
      "type": "n8n-nodes-base.if",
      "position": [
        140,
        480
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "88f0524b-2dd2-4a50-8776-9d305498776e",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.Email }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ca905c6b-dc60-4876-9349-b190051b5cb2",
      "name": "Update Sheet (email doesn't exist)",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueErrorOutput",
      "position": [
        140,
        760
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "f6f76a64-5f7d-4c6e-8220-f61eef86a8cd",
      "name": "If Email is not BokaDirect's support email",
      "type": "n8n-nodes-base.if",
      "position": [
        360,
        620
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "463f92c5-e786-4bdc-bc8c-21781e683325",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.Email }}",
              "rightValue": "user@example.com"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "02fa89b6-a094-489b-b78e-c6d435074b94",
      "name": "BokaDirect Profile URL Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        580,
        200
      ],
      "parameters": {
        "url": "={{ $json['BokaDirect Profile'] }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "f68ba01c-11fe-443a-92ab-a0ad3cf60195",
      "name": "ProfilePage or Redirected",
      "type": "n8n-nodes-base.code",
      "position": [
        580,
        480
      ],
      "parameters": {
        "jsCode": "const html = $('BokaDirect Profile URL Request').first().json.data; // Replace with your actual input\n\n// Regex to check for <div class=\"w-full\"> with any content inside, non-greedy\nconst regex = /<div\\s+class=[\"']w-full[\"'][^>]*>[\\s\\S]*?<\\/div>/i;\n\nconst found = regex.test(html);\n\nreturn {\n  hasWFullDiv: found\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "5a717b6c-5b7e-4149-a3c6-cb7867168824",
      "name": "Get Email",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        200
      ],
      "parameters": {
        "jsCode": "const html = $('BokaDirect Profile URL Request').first().json.data; // Replace with actual HTML input\n\n// Regex to find an email address (basic pattern)\nconst emailRegex = /[\\w.+-]+@[\\w-]+\\.[\\w.-]+/g;\n\nconst match = html.match(emailRegex);\n\nreturn {\n  emailFound: !!match,\n  email: match ? match[0] : null\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "09611789-b242-4050-8a43-af96798d9305",
      "name": "Update Sheet URL redirected",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueErrorOutput",
      "position": [
        580,
        1040
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "8d620fb9-7481-49fd-905b-d0d97a086054",
      "name": "DecisionMaker Name",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        580,
        360
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "assistant",
              "content": "You're a helpful, intelligent web scraping assistant."
            },
            {
              "content": "Tell me about DecisionMakerName using the StaffList and email scrape below. Use the following JSON format.\n\n{\"DecisionMakerName\":\"\"}\n\nNOTE: Donot add variable name as json for given output. Only give output in curly brackets {given format}. For DecisionMakerName, check the name and role in given data below in this format: name1, role1; name2, role2; ....\nIf 1 or more than 1 staff members, check who's the decision maker from their roles and return their name only. \n\nAlway prioritize this:\nCheck if given email has name from the given staff list, then consider that staff a decision maker."
            },
            {
              "content": "=StaffList:  {{ $json.staff_list }}\nEmail: {{ $('FirstRow').item.json.Email }}"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "3dc05efa-b498-415a-9ad2-aa0c85c60000",
      "name": "Business Details",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        480
      ],
      "parameters": {
        "jsCode": "// Get HTML content from previous node\nconst htmlContent = $('BokaDirect Profile URL Request').first().json.data;\n\n// Basic HTML entity decoder\nfunction decodeHtmlEntities(str) {\n  const entities = {\n    '&amp;': '&',\n    '&lt;': '<',\n    '&gt;': '>',\n    '&quot;': '\"',\n    '&#39;': \"'\",\n    '&apos;': \"'\",\n    '&nbsp;': ' ',\n  };\n  return str.replace(/&[a-zA-Z0-9#]+;/g, (entity) => entities[entity] || entity);\n}\n\n// Base64 decoding function\nfunction decodeBase64(str) {\n  return decodeURIComponent(\n    Array.prototype.map.call(atob(str), c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')\n  );\n}\n\n// Regex patterns\nconst nameRegex = /<h1.*?>(.*?)<\\/h1>/i;\nconst descriptionRegex = /<div id=\"welcomeText\".*?>(.*?)<\\/div>/is;\nconst staffNameRegex = /<span class=\" block font-semibold\">\\s*(?:<div[^>]*>.*?<\\/div>\\s*)*([^<]+)\\s*<\\/span>/g;\nconst staffRoleRegex = /<span class=\"text-black-600 text-sm\">([^<]+)<\\/span>/g;\nconst urlRegex = /<li[^>]*class=\"[^\"]*border-black-100[^\"]*\"[^>]*>[\\s\\S]*?<a[^>]*href=\"([^\"]+)\"[^>]*>/i;\nconst phoneRegex = /<meta data-react-helmet=\"true\" name=\"business:contact_data:phone_number\" content=\"(.*?)\"\\s*\\/>/i;\nconst emailImageRegex = /<img[^>]+src=\"data:image\\/png;base64,([^\"]+)\"[^>]*>/i;\n\n// Extract business name\nconst nameMatch = htmlContent.match(nameRegex);\nconst businessName = nameMatch ? decodeHtmlEntities(nameMatch[1].trim()) : null;\n\n// Extract business description\nconst descriptionMatch = htmlContent.match(descriptionRegex);\nconst businessDescription = descriptionMatch ? decodeHtmlEntities(descriptionMatch[1].trim()) : null;\n\n// Extract staff names\nconst names = [];\nlet nameMatchLoop;\nwhile ((nameMatchLoop = staffNameRegex.exec(htmlContent)) !== null) {\n  names.push(decodeHtmlEntities(nameMatchLoop[1].trim()));\n}\n\n// Extract staff roles\nconst roles = [];\nlet roleMatchLoop;\nwhile ((roleMatchLoop = staffRoleRegex.exec(htmlContent)) !== null) {\n  roles.push(decodeHtmlEntities(roleMatchLoop[1].trim()));\n}\n\n// Combine staff names and roles\nconst staffList = names.map((name, i) => {\n  const role = roles[i] || 'Unknown';\n  return `${name}, ${role}`;\n}).join('; ');\n\n// Extract URL\nconst urlMatch = htmlContent.match(urlRegex);\nconst url = urlMatch ? decodeHtmlEntities(urlMatch[1].trim()) : null;\n\n// Extract phone number\nconst phoneMatch = htmlContent.match(phoneRegex);\nconst phone = phoneMatch ? phoneMatch[1].trim() : null;\n\n// Extract email image (Base64)\nconst emailImageMatch = htmlContent.match(emailImageRegex);\nlet email = null;\n\nif (emailImageMatch) {\n  // Decode Base64 string and extract the email content\n  const base64String = emailImageMatch[1].trim();\n  const decodedEmailContent = decodeBase64(base64String);\n\n  // Assuming decoded content might contain email-like text (this can be further refined as needed)\n  // You can try a simple regex for extracting an email\n  const emailRegex = /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})/i;\n  const emailMatch = decodedEmailContent.match(emailRegex);\n\n  if (emailMatch) {\n    email = emailMatch[1];\n  } else {\n    email = \"Decoded content doesn't contain a valid email\";\n  }\n} else {\n  email = \"No Base64 image email found\";\n}\n\nconst pBlockRegex = /<p class=\"cursor-pointer\">([\\s\\S]*?)<\\/p>/;\nconst spanRegex = /<span>(.*?)<\\/span>/g;\n\nconst pMatch = htmlContent.match(pBlockRegex);\nlet address = '';\n\nif (pMatch) {\n  const spanContent = [...pMatch[1].matchAll(spanRegex)].map(m => m[1]);\n  address = spanContent.join(', ');\n}\n\n// Decode HTML entities (like &amp; to &)\nconst txt = decodeHtmlEntities(address)\n\n// Return structured result\nreturn [\n  {\n    json: {\n      business_name: businessName,\n      business_description: businessDescription,\n      staff_list: staffList,\n      url: url,\n      phone: phone,\n      email: email,\n      location: txt\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5e7bf73d-25c9-44ba-bf4c-9efedf3188e6",
      "name": "If ProfilePage or not",
      "type": "n8n-nodes-base.if",
      "position": [
        580,
        620
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "991cacdd-b64e-4e72-8d36-70d1aeee738b",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('ProfilePage or Redirected').item.json.hasWFullDiv }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8fe37ef9-8050-44b3-ac66-d9ec473aa532",
      "name": "If website exist",
      "type": "n8n-nodes-base.if",
      "position": [
        580,
        760
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "22599d7c-3e5d-4e6b-980e-5c81edd3edec",
              "operator": {
                "type": "string",
                "operation": "notStartsWith"
              },
              "leftValue": "={{ $('Business Details').item.json.url }}",
              "rightValue": "/"
            },
            {
              "id": "0f8bc517-b7a4-41f6-9f81-46c99d327f16",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $('Business Details').item.json.url }}",
              "rightValue": "none"
            },
            {
              "id": "eaef4953-4c8b-4402-92c1-a7d07ae508c1",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $('Business Details').item.json.url }}",
              "rightValue": "instagram"
            },
            {
              "id": "8f2fbedf-23fb-4638-8e99-f3432fe2fe54",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $('Business Details').item.json.url }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "281d4589-99d0-4155-9ef7-29e4558fd230",
      "name": "Professional Email or Personal",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        620
      ],
      "parameters": {
        "jsCode": "// List of keywords that indicate personal/free email providers\nconst freeEmailKeywords = [\n  \"gmail\", \"yahoo\", \"hotmail\", \"outlook\", \"aol\",\n  \"icloud\", \"live\", \"protonmail\", \"msn\", \"mail\", \n  \"zoho\", \"yandex\", \"gmx\"\n];\n\nreturn items.map(item => {\n  const email = $('FirstRow').first().json.Email || \"\";\n  const domain = email.split(\"@\")[1]?.toLowerCase() || \"\";\n\n  const isPersonal = freeEmailKeywords.some(keyword => domain.includes(keyword));\n  const isProfessional = !isPersonal;\n\n  // Extract domain only if it's a professional email\n  const result = {\n    ...item.json,\n    isProfessional,\n    emailType: isProfessional ? \"professional\" : \"personal\"\n  };\n\n  if (isProfessional) {\n    result.domain = domain;\n  }\n\n  return { json: result };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "1862d6bc-74cd-4db7-a229-67b039f5ca24",
      "name": "Professional or Not",
      "type": "n8n-nodes-base.if",
      "position": [
        720,
        760
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "96bf5921-7ef0-41a8-ac42-37b50fbb1e57",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.emailType }}",
              "rightValue": "professional"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "964b2306-5b0d-4126-9dc9-43f5b6f78216",
      "name": "Update Website doesn't exist",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueErrorOutput",
      "position": [
        580,
        1200
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "f9c7aaa4-7af1-4e3c-86cd-22ca4dbebb32",
      "name": "Set URL from Professional Email",
      "type": "n8n-nodes-base.set",
      "position": [
        720,
        900
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "65d5a8d6-e8d8-4500-abff-bd5517f728ae",
              "name": "URL",
              "type": "string",
              "value": "=https://www.{{ $json.domain }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "62fb45a6-4198-40a9-b69b-f98350414199",
      "name": "Set URL",
      "type": "n8n-nodes-base.set",
      "position": [
        580,
        900
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e0dd80bd-1a37-4c11-b808-139b529eca41",
              "name": "URL",
              "type": "string",
              "value": "={{ $('Business Details').item.json.url }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "d5b88f94-b13a-474e-8e21-ba470d54cdcb",
      "name": "Update Scraped Business Details",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueErrorOutput",
      "position": [
        720,
        1040
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "2e9901f8-233f-4954-a1d1-51aa9bd9a942",
      "name": "Business Website Request",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        960,
        620
      ],
      "parameters": {
        "url": "={{ $json.URL }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "fc02ee1d-3613-421b-874b-d48ba90dbea0",
      "name": "Write message for error in Website",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1220,
        1000
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "The error type (e.g., 404 Not Found, 500 Server Error, SSL Error, timeout)\n\n\nBasic context (business name, location, what they do)\n\n\nYour task:\n Generate a personalized icebreaker for a cold outreach email, pitching web repair, redesign, and performance/SEO improvements. Make it feel researched and human-written. Always output as a JSON below:\n{\n  \"content\": \"Hey [Team companyShort or FirstName], I tried visiting your site but it looks like it's returning a [error_type] error right now. That kind of issue can quietly block new leads, affect your SEO, or just make the business feel offline to potential customers.\\n\\nFigured you\u2019d want to know, since it\u2019s likely costing traffic and trust. I\u2019ve worked on 500+ web projects over the last 8 years\u2014fixing things like this fast, and often turning them into a chance to improve performance, ranking, and UX.\\n\\nAlong with development, I also bring SEO and AI expertise to help businesses strengthen their online presence and drive more qualified traffic. If you're open to it, happy to jump on a quick call and walk through what I\u2019d recommend for getting your site up and performing at its best.\\n\\nNo pressure\u2014just reaching out since I already ran the check. Let me know if you want to see some sites I\u2019ve helped turn around.\"\n}\n\nRules Recap (for this error-based version):\n-Clearly mention the error type (e.g., 404, timeout, SSL error)\n-Highlight the cost of downtime: missed leads, SEO loss, trust issues\n-Keep tone helpful, not critical\n-Include your track record (1000+ web projects, 8 years)\n-Mention your SEO and AI expertise\n-Offer web repair + performance/redesign improvements\n-Include a soft, benefit-driven CTA (e.g., \u201chappy to jump on a quick call\u2026\u201d)\n\n\nBasic Business Details: \nName: {{ $('Business Details').item.json.business_name }}\nLocation: {{ $('Business Details').item.json.location }}\nBusiness Description: {{ $('Business Details').item.json.business_description }}\nDecision Maker Name: {{ $('DecisionMaker Name').item.json.message.content.DecisionMakerName }}\n\nError Details: \nError Name: {{ $('Business Website Request').item.json.error.name }}\nError Code: {{ $('Business Website Request').item.json.error.code}}\nError Status: {{ $('Business Website Request').item.json.error.status }}"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "df154851-f651-4a8b-ad3e-13076f777f01",
      "name": "Lighthouse Stats Request",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        1180,
        240
      ],
      "parameters": {
        "url": "=https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url={{ $('Update Scraped Business Details').item.json.URL }}&strategy=mobile&category=performance&category=accessibility&category=seo&category=best-practices&key=**YourPassKey**",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "a724316b-4643-4071-a5bb-f7c676c6ffb0",
      "name": "Translate into Swedish",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1520,
        600
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "assistant",
              "content": "You are a native Swedish copywriter. Your job is to translate English into fluent Swedish."
            },
            {
              "content": "=Here's text to translate:\n{{ $json.message.content.content }}"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "4591596e-4646-458e-b1b8-2651a2251dc0",
      "name": "Update Error in Stats",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueErrorOutput",
      "position": [
        1180,
        540
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "1a6e8cf0-d939-4f46-8070-1bdb03957439",
      "name": "Filter Stats",
      "type": "n8n-nodes-base.code",
      "position": [
        1180,
        400
      ],
      "parameters": {
        "jsCode": "const categories = $input.first().json.lighthouseResult?.categories || {};\n\nfunction extractScore(cat) {\n  return categories[cat]?.score !== undefined\n    ? Math.round(categories[cat].score * 100)\n    : \"none\";\n}\n\nreturn [{\n  performance: extractScore(\"performance\"),\n  accessibility: extractScore(\"accessibility\"),\n  bestPractices: extractScore(\"best-practices\"),\n  seo: extractScore(\"seo\"),\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "569192d1-825e-4532-9a3d-bd5a9585cf43",
      "name": "Screenshot of Website Request",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        1380,
        240
      ],
      "parameters": {
        "url": "=https://image.thum.io/get/fullpage/{{ $('Update Scraped Business Details').item.json.URL }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "d7561abd-1681-424f-a728-6402cb6dfaf8",
      "name": "Analyse UI/UX design, Lighthouse stats & Business details",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1520,
        220
      ],
      "parameters": {
        "text": "=We scraped a business website and collected the following data:\n- A screenshot of the homepage  \n- A Lighthouse audit with scores for Performance (e.g. 34), Accessibility (e.g. 55), SEO (e.g. 61), and Best Practices  \n- Basic context (business name, location, what they do)  \n\nYour task:  \nGenerate a personalized email copy for a cold outreach email, pitching web redesign, performance optimization, and AI features depending on what you found. Always output the icebreaker value only from the following JSON. \n\n{\n \"icebreaker\": \"Hey [FirstName/companyShort], I came across [companyShort] and thought the way you***********Write your prompt****************************************************",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "resource": "image",
        "inputType": "base64",
        "operation": "analyze"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "b3d56936-7c43-41f1-8b0a-7098dd0a4593",
      "name": "Update Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueErrorOutput",
      "position": [
        1560,
        980
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "31bfb667-c41f-4a62-80d6-6bd33f58bf55",
      "name": "Update Sheet with messages and Status",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueErrorOutput",
      "position": [
        1520,
        460
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "b2d12902-a0dc-4459-97a4-1cc61e6e83a2",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -80,
        620
      ],
      "parameters": {
        "path": "0f267a89-60f6-46c5-a97e-39c4e1452579",
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "8455a92d-5052-4bc1-8161-4698b357afbc",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        140,
        340
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "981579ab-37d4-483d-932e-42700c91f616",
      "name": "Wait2",
      "type": "n8n-nodes-base.wait",
      "position": [
        140,
        920
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "f20403fe-f238-4243-8ccd-87f582384cd4",
      "name": "Wait3",
      "type": "n8n-nodes-base.wait",
      "position": [
        720,
        1200
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "0c8ed81c-3ca9-4201-989a-016adba159b9",
      "name": "Wait4",
      "type": "n8n-nodes-base.wait",
      "position": [
        580,
        1340
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "b11ea917-3cd2-4acb-8f44-be75efbcd15f",
      "name": "Wait5",
      "type": "n8n-nodes-base.wait",
      "position": [
        720,
        1340
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "87d6352f-4eb0-4e87-b34a-b24e2317fe09",
      "name": "Wait6",
      "type": "n8n-nodes-base.wait",
      "position": [
        1560,
        1140
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "5685ace3-0176-4fcf-8e97-c1bb70aff7cd",
      "name": "Wait7",
      "type": "n8n-nodes-base.wait",
      "position": [
        1320,
        540
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "520c0c73-0589-49f5-9c8c-25dae8ac7d52",
      "name": "Wait8",
      "type": "n8n-nodes-base.wait",
      "position": [
        1360,
        400
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "d171e318-d5a7-480b-9cb2-4b95e9e08985",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        60,
        60
      ],
      "parameters": {
        "color": 2,
        "width": 260,
        "height": 1100,
        "content": "# Step 1.\n## Trigger & CRM Input"
      },
      "typeVersion": 1
    },
    {
      "id": "214dfeb1-daaa-4ae1-b85a-3cedc0d6ca22",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        500,
        60
      ],
      "parameters": {
        "color": 6,
        "width": 420,
        "height": 1520,
        "content": "# Step 2.\n## Scraping Business Data"
      },
      "typeVersion": 1
    },
    {
      "id": "eb1be7fe-147d-44d6-913b-7508e8d8ab8b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1100,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 720,
        "height": 680,
        "content": "# Step 3.\n## Analyzing Lighthouse Stats + Website UI/UX Design & Generating Hyper-Personalised Outreach Email"
      },
      "typeVersion": 1
    },
    {
      "id": "fb473b10-10b7-4d37-8d8f-41b250d7b46e",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1160,
        800
      ],
      "parameters": {
        "color": 3,
        "width": 660,
        "height": 540,
        "content": "# Step 4.\n## Analyzing Website Error & Generating Hyper-Personalised Outreach Email"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "1d38610c-a6da-410e-acf9-fd3d0712094b",
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Get New Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait1": {
      "main": [
        [
          {
            "node": "Screenshot of Website Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait2": {
      "main": [
        [
          {
            "node": "Update Sheet (email doesn't exist)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait3": {
      "main": [
        [
          {
            "node": "Update Sheet URL redirected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait4": {
      "main": [
        [
          {
            "node": "Update Website doesn't exist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait5": {
      "main": [
        [
          {
            "node": "Update Scraped Business Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait6": {
      "main": [
        [
          {
            "node": "Update Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait7": {
      "main": [
        [
          {
            "node": "Update Error in Stats",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait8": {
      "main": [
        [
          {
            "node": "Update Sheet with messages and Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set URL": {
      "main": [
        [
          {
            "node": "Update Scraped Business Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Get New Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FirstRow": {
      "main": [
        [
          {
            "node": "If Email is not Empty",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Email": {
      "main": [
        [
          {
            "node": "If ProfilePage or not",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Stats": {
      "main": [
        [
          {
            "node": "Screenshot of Website Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get New Rows": {
      "main": [
        [
          {
            "node": "FirstRow",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Sheet": {
      "main": [
        [],
        [
          {
            "node": "Wait6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Business Details": {
      "main": [
        [
          {
            "node": "DecisionMaker Name",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If website exist": {
      "main": [
        [
          {
            "node": "Set URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Professional Email or Personal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DecisionMaker Name": {
      "main": [
        [
          {
            "node": "If website exist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Professional or Not": {
      "main": [
        [
          {
            "node": "Set URL from Professional Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Website doesn't exist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Email is not Empty": {
      "main": [
        [
          {
            "node": "If Email is not BokaDirect's support email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Sheet (email doesn't exist)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If ProfilePage or not": {
      "main": [
        [
          {
            "node": "Business Details",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Sheet URL redirected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Error in Stats": {
      "main": [
        [],
        [
          {
            "node": "Wait7",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Translate into Swedish": {
      "main": [
        []
      ]
    },
    "Business Website Request": {
      "main": [
        [
          {
            "node": "Lighthouse Stats Request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Write message for error in Website",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lighthouse Stats Request": {
      "main": [
        [
          {
            "node": "Filter Stats",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Error in Stats",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ProfilePage or Redirected": {
      "main": [
        [
          {
            "node": "Get Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Sheet URL redirected": {
      "main": [
        [],
        [
          {
            "node": "Wait3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Website doesn't exist": {
      "main": [
        [],
        [
          {
            "node": "Wait4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Screenshot of Website Request": {
      "main": [
        [
          {
            "node": "Analyse UI/UX design, Lighthouse stats & Business details",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BokaDirect Profile URL Request": {
      "main": [
        [
          {
            "node": "ProfilePage or Redirected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Professional Email or Personal": {
      "main": [
        [
          {
            "node": "Professional or Not",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set URL from Professional Email": {
      "main": [
        [
          {
            "node": "Update Scraped Business Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Scraped Business Details": {
      "main": [
        [
          {
            "node": "Business Website Request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Sheet (email doesn't exist)": {
      "main": [
        [
          {
            "node": "Get New Rows",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write message for error in Website": {
      "main": [
        [
          {
            "node": "Update Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Sheet with messages and Status": {
      "main": [
        [],
        [
          {
            "node": "Wait8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Email is not BokaDirect's support email": {
      "main": [
        [
          {
            "node": "BokaDirect Profile URL Request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Sheet URL redirected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyse UI/UX design, Lighthouse stats & Business details": {
      "main": [
        [
          {
            "node": "Update Sheet with messages and Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow is perfect for marketing agencies, SEO consultants, and growth specialists who need to scale personalized outreach without spending hours on manual research.

Source: https://n8n.io/workflows/5940/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Instantly map all internal URLs, perform AI-powered (ChatGPT) analysis, and deliver results in HTML via webhook, Google Sheets, or email. All from your own n8n instance!

OpenAI, HTTP Request, XML +3
AI & RAG

Watch on Youtube▶️

HTTP Request, Email Send, Google Sheets +3
AI & RAG

This workflow automates the creation of Journal Entries in SAP Business One (SAP B1). Depending on the source of the input data, it dynamically transforms and sends accounting records in the appropria

HTTP Request, Google Sheets, OpenAI
AI & RAG

How it works: Send notes from Obsidian via Webhook to start the audio conversion OpenAI converts your text to natural-sounding audio and generates episode descriptions Audio files are stored in Cloudi

OpenAI, HTTP Request, Google Sheets
AI & RAG

This workflow automates the initial screening process for new job applications, freeing up your recruitment team to focus on qualified candidates. It receives applications from a webhook, uses OpenAI

HTTP Request, OpenAI, Google Sheets +2