AutomationFlowsAI & RAG › Analyze UX Heuristics From UI Screenshots with Gpt-4 Vision and Google Sheets

Analyze UX Heuristics From UI Screenshots with Gpt-4 Vision and Google Sheets

ByOneclick AI Squad @oneclick-ai on n8n.io

This workflow evaluates website or app screenshots against established UX best practices (Nielsen's 10 Heuristics, WCAG accessibility guidelines, and mobile usability standards) and delivers prioritized, actionable improvement suggestions with severity ratings.

Webhook trigger★★★★☆ complexityAI-powered20 nodesHTTP RequestAgentOpenAI Chat
AI & RAG Trigger: Webhook Nodes: 20 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → 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": "ADYjux5aR6PlawVT",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI UX Heuristic Analyzer",
  "tags": [],
  "nodes": [
    {
      "id": "03001f57-8a04-40a2-b75e-a75d607e65ce",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1296,
        -320
      ],
      "parameters": {
        "width": 1020,
        "height": 980,
        "content": "## AI UX Heuristic Analyzer\n\nThis workflow evaluates website or app screenshots against established UX best practices (Nielsen's 10 Heuristics, WCAG accessibility guidelines, and mobile usability standards) and delivers prioritized, actionable improvement suggestions with severity ratings.\n\n### Who's it for\n\u2022 UX designers seeking automated design audits\n\u2022 Product managers reviewing UI quality pre-launch\n\u2022 Agencies conducting client UX audits at scale\n\u2022 Developers wanting accessibility compliance checks\n\u2022 Startups running lean without a dedicated UX team\n\n### How it works / What it does\n1. Accepts screenshot(s) via webhook upload crawl\n2. Classifies the UI type (web app, mobile, landing page, dashboard, e-commerce)\n3. Encodes image and sends to vision AI for heuristic analysis\n4. Evaluates against 10 Nielsen heuristics + WCAG + mobile standards\n5. Scores each heuristic category (0\u201310) and calculates overall UX score\n6. Generates prioritized, actionable improvement suggestions per finding\n7. Delivers a structured report via email and logs results to Google Sheets\n\n### How to set up\n1. Import this workflow\n2. Configure credentials (OpenAI with vision, SendGrid, Google Sheets)\n3. Set your brand/product context in the Set UX Config node\n4. Activate and submit screenshots via the webhook endpoint\n\n### Requirements\n\u2022 OpenAI API key with GPT-4 Vision access\n\u2022 Google Sheets (for audit log)\n\u2022 SendGrid or Gmail (for report delivery)\n\u2022 Screenshot files (PNG/JPG) or publicly accessible URLs\n\n### How to customize the workflow\n\u2022 Add or remove heuristic categories in the AI prompt node\n\u2022 Adjust severity thresholds in the Set UX Config node\n\u2022 Extend the Google Sheet columns to include client or project metadata\n\u2022 Chain multiple screenshots for full-flow UX audits"
      },
      "typeVersion": 1
    },
    {
      "id": "8f8a9ad3-dab2-4e66-81c7-f7c0a168e103",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        -64
      ],
      "parameters": {
        "color": 6,
        "width": 688,
        "height": 540,
        "content": "## 1. Trigger & Screenshot Intake"
      },
      "typeVersion": 1
    },
    {
      "id": "40777aad-8ddd-4068-a541-3ddfa06604d6",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        752,
        -96
      ],
      "parameters": {
        "color": 6,
        "width": 664,
        "height": 600,
        "content": "## 2. Image Preparation & UI Classification"
      },
      "typeVersion": 1
    },
    {
      "id": "48f3214b-2239-48f8-93a8-94e083133196",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1440,
        -64
      ],
      "parameters": {
        "color": 6,
        "width": 984,
        "height": 660,
        "content": "## 3. Heuristic Evaluation & Scoring"
      },
      "typeVersion": 1
    },
    {
      "id": "8c2ff94f-3265-4978-9e70-e4882ad561f5",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2480,
        -32
      ],
      "parameters": {
        "color": 6,
        "width": 1000,
        "height": 620,
        "content": "## 4. Report Generation & Delivery"
      },
      "typeVersion": 1
    },
    {
      "id": "53f7e199-0151-4e57-9a9b-26cbfb47e73b",
      "name": "Webhook - Screenshot Upload",
      "type": "n8n-nodes-base.webhook",
      "position": [
        144,
        224
      ],
      "parameters": {
        "path": "ux-heuristic-analyze",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1.1
    },
    {
      "id": "bc09d4be-4f8a-4432-aa90-1bd151e0859d",
      "name": "Prepare Audit Context",
      "type": "n8n-nodes-base.set",
      "position": [
        384,
        224
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "auditId",
              "type": "string",
              "value": "={{ 'UX-' + Date.now() }}"
            },
            {
              "name": "projectName",
              "type": "string",
              "value": "={{ $json.projectName || $json.body?.projectName || 'Untitled Project' }}"
            },
            {
              "name": "screenshotUrl",
              "type": "string",
              "value": "={{ $json.screenshotUrl || $json.body?.screenshotUrl || '' }}"
            },
            {
              "name": "screenshotBase64",
              "type": "string",
              "value": "={{ $json.screenshotBase64 || $json.body?.screenshotBase64 || '' }}"
            },
            {
              "name": "imageMediaType",
              "type": "string",
              "value": "={{ $json.mediaType || $json.body?.mediaType || 'image/png' }}"
            },
            {
              "name": "targetPlatform",
              "type": "string",
              "value": "={{ $json.platform || $json.body?.platform || 'web' }}"
            },
            {
              "name": "recipientEmail",
              "type": "string",
              "value": "={{ $json.email || $json.body?.email || 'ux-team@yourcompany.com' }}"
            },
            {
              "name": "submittedAt",
              "type": "string",
              "value": "={{ new Date().toISOString() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0b83ad7e-9df2-4c6d-a604-aa7239bfd433",
      "name": "Set UX Config",
      "type": "n8n-nodes-base.set",
      "position": [
        608,
        224
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "heuristicWeights",
              "type": "object",
              "value": "={{ { visibility_of_system_status: 0.10, match_real_world: 0.08, user_control: 0.10, consistency_standards: 0.10, error_prevention: 0.12, recognition_over_recall: 0.10, flexibility_efficiency: 0.08, aesthetic_minimalist: 0.10, error_recovery: 0.10, help_documentation: 0.07, accessibility_wcag: 0.05 } }}"
            },
            {
              "name": "severityLevels",
              "type": "object",
              "value": "={{ { critical: { min: 0, max: 3, label: 'CRITICAL', priority: 1 }, major: { min: 4, max: 6, label: 'MAJOR', priority: 2 }, minor: { min: 7, max: 8, label: 'MINOR', priority: 3 }, good: { min: 9, max: 10, label: 'GOOD', priority: 4 } } }}"
            },
            {
              "name": "scoreThresholds",
              "type": "object",
              "value": "={{ { excellent: 85, good: 70, needs_improvement: 50, poor: 0 } }}"
            },
            {
              "name": "platformProfiles",
              "type": "object",
              "value": "={{ { web: 'desktop browser web application', mobile: 'native mobile application (iOS/Android)', landing: 'marketing landing page', dashboard: 'data dashboard or analytics interface', ecommerce: 'e-commerce product or checkout flow' } }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "bfbe110a-3fff-44a5-8473-9b9f74bf25dc",
      "name": "Filter - Has Screenshot Input",
      "type": "n8n-nodes-base.filter",
      "position": [
        832,
        224
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "isNotEmpty"
              },
              "leftValue": "={{ $json.screenshotUrl || $json.screenshotBase64 }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "1dc20457-3c7d-44d4-80ab-875c8031c7d3",
      "name": "JS - Build Vision API Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        1056,
        224
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\n\n// Determine image source type and build vision API payload\nconst hasBase64 = !!(item.screenshotBase64 && item.screenshotBase64.length > 100);\nconst hasUrl = !!(item.screenshotUrl && item.screenshotUrl.startsWith('http'));\n\nlet imageSource = null;\nif (hasBase64) {\n  imageSource = {\n    type: 'base64',\n    mediaType: item.imageMediaType || 'image/png',\n    data: item.screenshotBase64\n  };\n} else if (hasUrl) {\n  imageSource = {\n    type: 'url',\n    url: item.screenshotUrl\n  };\n}\n\n// Map platform to descriptive profile\nconst platformProfiles = item.platformProfiles || {};\nconst platformDescription = platformProfiles[item.targetPlatform] || item.targetPlatform || 'web application';\n\n// Build the OpenAI vision message payload\nconst visionPayload = {\n  model: 'gpt-4o',\n  max_tokens: 4096,\n  messages: [\n    {\n      role: 'user',\n      content: [\n        {\n          type: 'text',\n          text: `You are an expert UX auditor. Analyze this screenshot of a ${platformDescription} and evaluate it against the 10 Nielsen Norman Heuristics, WCAG 2.1 accessibility guidelines, and platform-specific usability standards.\\n\\nReturn ONLY a valid JSON object in exactly this structure:\\n{\\n  \"uiClassification\": \"string (e.g. login screen, dashboard, product page, onboarding, settings)\",\\n  \"platformDetected\": \"string\",\\n  \"heuristicScores\": {\\n    \"visibility_of_system_status\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"match_real_world\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"user_control\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"consistency_standards\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"error_prevention\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"recognition_over_recall\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"flexibility_efficiency\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"aesthetic_minimalist\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"error_recovery\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"help_documentation\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" },\\n    \"accessibility_wcag\": { \"score\": 0, \"finding\": \"string\", \"suggestion\": \"string\" }\\n  },\\n  \"topIssues\": [\\n    { \"issue\": \"string\", \"severity\": \"CRITICAL|MAJOR|MINOR\", \"heuristic\": \"string\", \"actionablefix\": \"string\", \"estimatedEffort\": \"LOW|MEDIUM|HIGH\" }\\n  ],\\n  \"positivefindings\": [\"string\"],\\n  \"overallNotes\": \"string\"\\n}\\n\\nScore each heuristic 0\u201310 where 0 = severe violation, 10 = fully satisfies the heuristic. List at least 5 top issues. Be specific and reference visible UI elements.`\n        },\n        hasBase64\n          ? { type: 'image_url', image_url: { url: `data:${item.imageMediaType || 'image/png'};base64,${item.screenshotBase64}` } }\n          : { type: 'image_url', image_url: { url: item.screenshotUrl } }\n      ]\n    }\n  ]\n};\n\nreturn {\n  json: {\n    ...item,\n    imageSource,\n    platformDescription,\n    visionPayload\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "16b134b0-8e54-4963-832c-55a5684300e6",
      "name": "Wait 1 - Rate Limit Buffer",
      "type": "n8n-nodes-base.wait",
      "position": [
        1280,
        224
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "bf6b29a8-404d-4a7f-8efb-2d50da5773b7",
      "name": "OpenAI Vision - Heuristic Evaluation",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1504,
        160
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={{ JSON.stringify($json.visionPayload) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "f592742c-9a6c-484d-809c-50bbcb10ad87",
      "name": "JS - Parse Vision Response",
      "type": "n8n-nodes-base.code",
      "position": [
        1728,
        224
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\n\n// Extract raw AI response text\nconst rawContent = item.choices?.[0]?.message?.content || item.output || item.text || '{}';\nconst clean = rawContent.replace(/```json|```/g, '').trim();\n\nlet aiResult = {};\ntry {\n  aiResult = JSON.parse(clean);\n} catch(e) {\n  aiResult = {\n    uiClassification: 'unknown',\n    platformDetected: item.targetPlatform || 'web',\n    heuristicScores: {},\n    topIssues: [],\n    positiveFindings: [],\n    overallNotes: 'AI parse error: ' + e.message\n  };\n}\n\nreturn {\n  json: {\n    ...item,\n    aiResult\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "c309c553-9461-4415-9e0f-1d1f95c0ab34",
      "name": "JS - Calculate UX Score & Grade",
      "type": "n8n-nodes-base.code",
      "position": [
        1952,
        224
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\nconst ai = item.aiResult || {};\nconst weights = item.heuristicWeights || {};\nconst severityLevels = item.severityLevels || {};\nconst thresholds = item.scoreThresholds || { excellent: 85, good: 70, needs_improvement: 50, poor: 0 };\nconst scores = ai.heuristicScores || {};\n\n// Heuristic display labels\nconst heuristicLabels = {\n  visibility_of_system_status: 'Visibility of System Status',\n  match_real_world: 'Match Between System and Real World',\n  user_control: 'User Control & Freedom',\n  consistency_standards: 'Consistency & Standards',\n  error_prevention: 'Error Prevention',\n  recognition_over_recall: 'Recognition Over Recall',\n  flexibility_efficiency: 'Flexibility & Efficiency of Use',\n  aesthetic_minimalist: 'Aesthetic & Minimalist Design',\n  error_recovery: 'Help Users Recognize & Recover from Errors',\n  help_documentation: 'Help & Documentation',\n  accessibility_wcag: 'Accessibility (WCAG 2.1)'\n};\n\n// Build scored heuristics array\nconst scoredHeuristics = Object.entries(scores).map(([key, val]) => {\n  const score = typeof val === 'object' ? (val.score ?? 5) : (val ?? 5);\n  const finding = typeof val === 'object' ? val.finding : '';\n  const suggestion = typeof val === 'object' ? val.suggestion : '';\n  const weight = weights[key] || 0.09;\n\n  let severityLabel = 'GOOD';\n  for (const [level, cfg] of Object.entries(severityLevels)) {\n    if (score >= cfg.min && score <= cfg.max) { severityLabel = cfg.label; break; }\n  }\n\n  return {\n    key,\n    label: heuristicLabels[key] || key,\n    score,\n    weight,\n    weightedScore: +(score * weight).toFixed(4),\n    severity: severityLabel,\n    finding: finding || '',\n    suggestion: suggestion || ''\n  };\n});\n\n// Weighted overall UX score (scale 0\u2013100)\nconst totalWeight = scoredHeuristics.reduce((s, h) => s + h.weight, 0) || 1;\nconst rawWeighted = scoredHeuristics.reduce((s, h) => s + h.weightedScore, 0);\nconst overallUXScore = +((rawWeighted / totalWeight) * 10).toFixed(1);\n\n// Grade\nlet grade = 'F';\nif (overallUXScore >= thresholds.excellent) grade = 'A';\nelse if (overallUXScore >= thresholds.good) grade = 'B';\nelse if (overallUXScore >= thresholds.needs_improvement) grade = 'C';\nelse if (overallUXScore >= 30) grade = 'D';\n\n// Verdict label\nlet verdict = 'Poor UX \u2014 Immediate Redesign Required';\nif (grade === 'A') verdict = 'Excellent UX \u2014 Minor Polish Recommended';\nelse if (grade === 'B') verdict = 'Good UX \u2014 Targeted Improvements Needed';\nelse if (grade === 'C') verdict = 'Needs Improvement \u2014 Key Issues to Address';\nelse if (grade === 'D') verdict = 'Significant UX Problems \u2014 Prioritize Fixes';\n\n// Critical / major / minor counts from top issues\nconst issues = ai.topIssues || [];\nconst criticalCount = issues.filter(i => i.severity === 'CRITICAL').length;\nconst majorCount = issues.filter(i => i.severity === 'MAJOR').length;\nconst minorCount = issues.filter(i => i.severity === 'MINOR').length;\n\n// Sort issues by severity priority\nconst severityOrder = { CRITICAL: 1, MAJOR: 2, MINOR: 3 };\nconst sortedIssues = [...issues].sort((a, b) => (severityOrder[a.severity] || 9) - (severityOrder[b.severity] || 9));\n\n// Category breakdown (worst heuristics first)\nconst heuristicsSorted = [...scoredHeuristics].sort((a, b) => a.score - b.score);\n\nconst report = {\n  auditId: item.auditId,\n  projectName: item.projectName,\n  auditedAt: new Date().toISOString(),\n  submittedAt: item.submittedAt,\n  uiClassification: ai.uiClassification || 'Unknown',\n  platformDetected: ai.platformDetected || item.targetPlatform,\n  screenshotUrl: item.screenshotUrl || '(base64 upload)',\n\n  overallUXScore,\n  grade,\n  verdict,\n\n  issueSummary: { critical: criticalCount, major: majorCount, minor: minorCount, total: issues.length },\n  heuristicBreakdown: heuristicsSorted,\n  topIssues: sortedIssues,\n  positiveFindings: ai.positiveFindings || ai.positivefindings || [],\n  overallNotes: ai.overallNotes || '',\n\n  worstHeuristic: heuristicsSorted[0]?.label || 'N/A',\n  worstScore: heuristicsSorted[0]?.score ?? 'N/A',\n  bestHeuristic: heuristicsSorted[heuristicsSorted.length - 1]?.label || 'N/A',\n  bestScore: heuristicsSorted[heuristicsSorted.length - 1]?.score ?? 'N/A',\n\n  recipientEmail: item.recipientEmail,\n  status: 'Completed'\n};\n\nreturn { json: { ...item, report } };"
      },
      "typeVersion": 2
    },
    {
      "id": "ee199135-0a05-4973-a1f0-2e641b1a21d8",
      "name": "AI - Generate Improvement Report",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2176,
        224
      ],
      "parameters": {
        "text": "=You are a senior UX writer and consultant. Based on the structured heuristic audit data below, write a clear, professional, executive-ready UX improvement summary.\n\n## Audit Results\nProject: {{ $json.report.projectName }}\nUI Type: {{ $json.report.uiClassification }}\nPlatform: {{ $json.report.platformDetected }}\nOverall UX Score: {{ $json.report.overallUXScore }}/100 (Grade: {{ $json.report.grade }})\nVerdict: {{ $json.report.verdict }}\n\n## Issue Counts\nCritical: {{ $json.report.issueSummary.critical }}\nMajor: {{ $json.report.issueSummary.major }}\nMinor: {{ $json.report.issueSummary.minor }}\n\n## Top Issues\n{{ JSON.stringify($json.report.topIssues, null, 2) }}\n\n## Positive Findings\n{{ JSON.stringify($json.report.positiveFindings, null, 2) }}\n\n## Heuristic Notes\n{{ $json.report.overallNotes }}\n\nWrite a structured improvement report with these sections:\n1. **Executive Summary** (2\u20133 sentences)\n2. **Critical Issues to Fix First** (numbered list, each with what to fix and why it matters)\n3. **Quick Wins** (low-effort, high-impact fixes)\n4. **What's Working Well** (acknowledge strengths)\n5. **Recommended Next Steps** (prioritized 30/60/90 day roadmap)\n\nTone: Direct, constructive, actionable. Use plain language, not jargon.",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.6
    },
    {
      "id": "c44a3837-6337-4410-9faa-f637d41a6957",
      "name": "JS - Attach Narrative to Report",
      "type": "n8n-nodes-base.code",
      "position": [
        2528,
        224
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const item = $input.item.json;\n\nconst narrativeText = item.output || item.response || item.text || 'Report generation unavailable.';\n\nreturn {\n  json: {\n    ...item,\n    report: {\n      ...item.report,\n      narrativeReport: narrativeText\n    }\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e68abe9b-9117-4165-9ca6-4c039cf799ba",
      "name": "Update Google Sheet - Audit Log",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2976,
        32
      ],
      "parameters": {
        "url": "https://sheets.googleapis.com/v4/spreadsheets/YOUR_SHEET_ID/values/UX_Audits!A1:append?valueInputOption=USER_ENTERED",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"values\": [[\n    \"{{ $json.report.auditedAt }}\",\n    \"{{ $json.report.auditId }}\",\n    \"{{ $json.report.projectName }}\",\n    \"{{ $json.report.uiClassification }}\",\n    \"{{ $json.report.platformDetected }}\",\n    \"{{ $json.report.overallUXScore }}\",\n    \"{{ $json.report.grade }}\",\n    \"{{ $json.report.verdict }}\",\n    \"{{ $json.report.issueSummary.critical }}\",\n    \"{{ $json.report.issueSummary.major }}\",\n    \"{{ $json.report.issueSummary.minor }}\",\n    \"{{ $json.report.worstHeuristic }}\",\n    \"{{ $json.report.worstScore }}\",\n    \"{{ $json.report.bestHeuristic }}\",\n    \"{{ $json.report.bestScore }}\",\n    \"{{ $json.report.screenshotUrl }}\",\n    \"{{ $json.report.status }}\"\n  ]]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "cddbc69f-1687-41d5-ae6a-b1120dbe5815",
      "name": "Send UX Audit Report Email",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2976,
        224
      ],
      "parameters": {
        "url": "https://api.sendgrid.com/v3/mail/send",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"personalizations\": [{\"to\": [{\"email\": \"{{ $json.report.recipientEmail }}\"}]}],\n  \"from\": {\"email\": \"ux-auditor@yourcompany.com\", \"name\": \"UX Heuristic Analyzer\"},\n  \"subject\": \"UX Audit Report: {{ $json.report.projectName }} \u2014 Score {{ $json.report.overallUXScore }}/100 ({{ $json.report.grade }})\",\n  \"content\": [{\n    \"type\": \"text/plain\",\n    \"value\": \"UX HEURISTIC AUDIT REPORT\\n=========================\\nProject: {{ $json.report.projectName }}\\nAudit ID: {{ $json.report.auditId }}\\nDate: {{ $json.report.auditedAt }}\\nUI Type: {{ $json.report.uiClassification }}\\nPlatform: {{ $json.report.platformDetected }}\\n\\nOVERALL UX SCORE: {{ $json.report.overallUXScore }}/100\\nGRADE: {{ $json.report.grade }}\\nVERDICT: {{ $json.report.verdict }}\\n\\nISSUE SUMMARY\\n-------------\\nCritical: {{ $json.report.issueSummary.critical }}\\nMajor: {{ $json.report.issueSummary.major }}\\nMinor: {{ $json.report.issueSummary.minor }}\\n\\nWORST PERFORMING HEURISTIC\\n--------------------------\\n{{ $json.report.worstHeuristic }}: {{ $json.report.worstScore }}/10\\n\\nBEST PERFORMING HEURISTIC\\n-------------------------\\n{{ $json.report.bestHeuristic }}: {{ $json.report.bestScore }}/10\\n\\n{{ $json.report.narrativeReport }}\\n\\nFull breakdown logged to the UX Audit Google Sheet.\"\n  }]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "004f04e9-b86d-4c63-98ab-04c0d58ffade",
      "name": "Webhook Response - Return Audit Report",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2976,
        416
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json.report) }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "7e526af7-819c-41ea-a305-12c181ca8bf5",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2240,
        448
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "825bfa3a-3544-4655-91d8-9770b67cc650",
  "connections": {
    "Set UX Config": {
      "main": [
        [
          {
            "node": "Filter - Has Screenshot Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI - Generate Improvement Report",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Audit Context": {
      "main": [
        [
          {
            "node": "Set UX Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Parse Vision Response": {
      "main": [
        [
          {
            "node": "JS - Calculate UX Score & Grade",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 1 - Rate Limit Buffer": {
      "main": [
        [
          {
            "node": "OpenAI Vision - Heuristic Evaluation",
            "type": "main",
            "index": 0
          },
          {
            "node": "JS - Parse Vision Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Screenshot Upload": {
      "main": [
        [
          {
            "node": "Prepare Audit Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter - Has Screenshot Input": {
      "main": [
        [
          {
            "node": "JS - Build Vision API Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Build Vision API Payload": {
      "main": [
        [
          {
            "node": "Wait 1 - Rate Limit Buffer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Attach Narrative to Report": {
      "main": [
        [
          {
            "node": "Update Google Sheet - Audit Log",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send UX Audit Report Email",
            "type": "main",
            "index": 0
          },
          {
            "node": "Webhook Response - Return Audit Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JS - Calculate UX Score & Grade": {
      "main": [
        [
          {
            "node": "AI - Generate Improvement Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI - Generate Improvement Report": {
      "main": [
        [
          {
            "node": "JS - Attach Narrative to Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Vision - Heuristic Evaluation": {
      "main": [
        [
          {
            "node": "JS - Parse Vision Response",
            "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 evaluates website or app screenshots against established UX best practices (Nielsen's 10 Heuristics, WCAG accessibility guidelines, and mobile usability standards) and delivers prioritized, actionable improvement suggestions with severity ratings.

Source: https://n8n.io/workflows/15200/ — 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

⏺ 🚀 How it works

Agent, Anthropic Chat, Output Parser Structured +6
AI & RAG

L&D_AgentsAI_ATIVO. Uses httpRequest, agent, googleCalendarTool, toolSerpApi. Webhook trigger; 93 nodes.

HTTP Request, Agent, Google Calendar Tool +9
AI & RAG

CLINICAINTEGRAL_secretary. Uses postgres, mcpClientTool, googleDriveTool, toolWorkflow. Webhook trigger; 89 nodes.

Postgres, Mcp Client Tool, Google Drive Tool +14
AI & RAG

Remi 1.1. Uses lmChatOpenAi, memoryPostgresChat, openAi, postgres. Webhook trigger; 89 nodes.

OpenAI Chat, Memory Postgres Chat, OpenAI +7
AI & RAG

This n8n workflow orchestrates a powerful suite of AI Agents and automations to manage and optimize various aspects of an e-commerce operation, particularly for platforms like Shopify. It leverages La

Google Sheets, HTTP Request, Slack +10