AutomationFlowsAI & RAG › Generate a Legal Website Accessibility Statement with AI and Wave

Generate a Legal Website Accessibility Statement with AI and Wave

ByLukas Kunhardt @lukaskunhardt on n8n.io

This template is for any website owner, digital agency, or compliance officer operating within the European Union. It's designed for users who need to comply with the upcoming European Accessibility Act (EAA) but may not have deep technical or legal expertise.

Event trigger★★★★☆ complexityAI-powered13 nodesHTTP RequestOutput Parser StructuredAgentGoogle Gemini ChatMove Binary DataGmail
AI & RAG Trigger: Event Nodes: 13 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → Gmail 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
{
  "nodes": [
    {
      "id": "7d80a2b6-225b-41c3-9723-b1c533176dcd",
      "name": "Get Website HTML",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1680,
        180
      ],
      "parameters": {
        "url": "={{ $json['URL to analyze'] }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "598f30d8-ca38-469e-978b-c521b2163e6c",
      "name": "Get WAVE Report",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1420,
        180
      ],
      "parameters": {
        "url": "=https://wave.webaim.org/api/request?key={{ $('CHANGE THESE: dependencies').item.json.wave_api_key }}&reporttype=4&url={{ $('CHANGE THESE: dependencies').item.json['URL to analyze'] }}\n",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "0e6c3a12-ad82-4b84-85b2-b0d5a3ebe055",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -600,
        400
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"Accessibility Statement\": {\n\t\t\t\"type\": \"string\",\n            \"description\": \"output the HTML code for the accessibility statement here. The statement should use the specified language\"\n\t\t}\n\t}\n}"
      },
      "typeVersion": 1.2
    },
    {
      "id": "3ceda0aa-662d-49c8-a009-520889c54332",
      "name": "Accessibility Statement Generator",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -860,
        180
      ],
      "parameters": {
        "text": "=You are an expert legal-tech assistant. Your specialty is European digital accessibility law, specifically the **European Accessibility Act (EAA)** and the **Web Content Accessibility Guidelines (WCAG)**.\n\nYour task is to generate a formal **\"Accessibility Statement\"**.\n\nThe final statement must be written **in English** and formatted as a **clean, professional HTML document**. The content must be factually based on the provided data and compliant with the EAA's requirements.\n\n\nPlease generate a compliant Accessibility Statement by following these instructions precisely.\n\n**PART 1: INPUT DATA**\n\nYou will use the following information to construct the statement:\n\n* **Company Name:** `{{ $('CHANGE THESE: dependencies').item.json['Company Name'] }}`\n* **Company Country:** `{{ $('CHANGE THESE: dependencies').item.json['Country of the Company (used to apply local law)'] }}`\n* **Website URL:** `{{ $('CHANGE THESE: dependencies').item.json['URL to analyze'] }}`\n* **Accessibility Scan Results:** A JSON list of all accessibility problems detected on the website.\n    ```json\n    {{ $('Map WAVE Report Items to Website selectors.').item.json.analysisItems.toJsonString() }}\n    ```\n\n**PART 2: YOUR TASK & INSTRUCTIONS**\n\nCreate the HTML code for the \"Accessibility Statement\" by completing the following steps:\n\n**Step 1: Determine Compliance Status**\nAnalyze the `Accessibility Scan Results`. Since the data contains multiple items with an `issue_type` of \"error\", you **must** classify the website's status as **\"partially compliant\"**.\n\n**Step 2: Summarize Non-Accessible Content**\nFrom the `Accessibility Scan Results`, identify the main **categories** of problems. In the final statement, list 2-4 of these general categories. Do **not** list every single error. Good examples are:\n* \"Missing alternative text for images and graphics\"\n* \"Insufficient color contrasts between text and background\"\n* \"Form elements without associated labels\"\n* \"Menus or interactive elements not fully operable by keyboard\"\n\n**Step 3: Construct the Final HTML Document**\nGenerate a complete HTML document in English. The document **must** include the following sections, using the provided English titles:\n\n1.  **`Accessibility Statement`**: The main title of the page.\n2.  **`Commitment to Accessibility`**: A brief, professional introduction stating the company's commitment to digital accessibility in the name of `{{ $('CHANGE THESE: dependencies').item.json['Company Name'] }}`.\n3.  **`Conformance Status`**: State that the website is \"partially conformant with the Web Content Accessibility Guidelines (WCAG) 2.1 level AA\" due to the non-compliances listed below.\n4.  **`Non-Accessible Content`**: List the general problem categories you identified in Step 2.\n5.  **`Preparation of this Accessibility Statement`**: State that the assessment method was a `self-assessment` conducted with automated tools and that the statement was created on `{{ $now.toFormat('MMMM d, yyyy') }}`.\n6.  **`Feedback and Contact Information`**: Provide a section for users to report barriers. **Crucially, use a clear placeholder text** like `[Please insert your contact email address or a link to your contact form here]` so the end-user knows this part must be manually replaced.\n7.  **`Enforcement Procedure`**: This section is country-specific. **You must insert a clear placeholder** informing the user that they need to add the details of their country's official national enforcement body. Use placeholder text like: `[The website owner must insert the name, contact details, and web link for the official national enforcement/arbitration body for their country here.]`\n\n**Step 4: Final Output Requirement**\nYour final response must ONLY be the complete, raw HTML code for the statement in the required JSON format. The Accesibility Statement MUST be in {{ $('CHANGE THESE: dependencies').item.json['Desired Output Language'] }} language!!",
        "options": {},
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2
    },
    {
      "id": "70f20556-12de-4b43-8950-c3409be30e27",
      "name": "gemini 2.5 pro",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -920,
        400
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-2.5-pro-preview-06-05"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f16e7f9a-357f-4493-8b2f-677e5c3b3a97",
      "name": "Parse output as html",
      "type": "n8n-nodes-base.html",
      "position": [
        -480,
        180
      ],
      "parameters": {
        "html": "{{ $json.output['Accessibility Statement'] }}"
      },
      "typeVersion": 1.2
    },
    {
      "id": "c71c1911-4de6-42f9-a804-477237962e47",
      "name": "Create accesibility statement html file",
      "type": "n8n-nodes-base.moveBinaryData",
      "position": [
        -180,
        180
      ],
      "parameters": {
        "mode": "jsonToBinary",
        "options": {
          "fileName": "=accessibility_statement_{{ $('CHANGE THESE: dependencies').item.json['Company Name'].toSnakeCase() }}.html",
          "mimeType": "text/html",
          "useRawData": true
        },
        "sourceKey": "html",
        "convertAllData": false
      },
      "typeVersion": 1
    },
    {
      "id": "a7b3c3c8-f6b5-42ce-bf7e-b321cb68906c",
      "name": "Map WAVE Report Items to Website selectors.",
      "type": "n8n-nodes-base.code",
      "position": [
        -1200,
        180
      ],
      "parameters": {
        "jsCode": "// --- Corrected Script ---\n\nconsole.log(\"--- 1. SCRIPT INITIALIZING ---\");\nconst cheerio = require('cheerio');\nconsole.log(\"Cheerio library loaded.\");\n\nconst fullHtml = $('Get Website HTML').first().json.data;\nconst waveCategories = $('Get WAVE Report').first().json.categories;\nconsole.log(`Input data loaded. HTML Length: ${fullHtml.length}`);\n\n// --- 3. INITIALIZING CHEERIO ---\nconst $$ = cheerio.load(fullHtml);\nconsole.log(`Cheerio loaded HTML successfully. Document title: \"${$$('title').text()}\"`);\n\nconst analysisItems = [];\n\n// --- 4. PROCESSING LOOP ---\nif (waveCategories && typeof waveCategories === 'object') {\n  for (const categoryName in waveCategories) {\n    const category = waveCategories[categoryName];\n    if (category && category.items && typeof category.items === 'object') {\n      for (const itemId in category.items) {\n        const item = category.items[itemId];\n        if (item.selectors && Array.isArray(item.selectors)) {\n          \n          // Loop through the array of selector strings\n          for (const selectorString of item.selectors) {\n            \n            // --- THIS IS THE CORRECTED LINE ---\n            // The selector is the string itself from the array.\n            const selector = selectorString;\n\n            // Ensure the selector is a valid string before using it\n            if (typeof selector === 'string' && selector) {\n              console.log(`  -> Searching for selector: \"${selector.substring(0, 80)}...\"`);\n              const elementNode = $$(selector).first();\n              \n              if (elementNode.length > 0) {\n                console.log(`    -> SUCCESS: Found element!`);\n                let contextHtml = elementNode.parent().html();\n                \n                if (contextHtml && contextHtml.length > 2000) {\n                  contextHtml = $$.html(elementNode);\n                }\n\n                analysisItems.push({\n                  issue_type: categoryName,\n                  description: item.description,\n                  selector: selector,\n                  context_html: contextHtml ? contextHtml.trim() : \"\"\n                });\n              } else {\n                console.log(`    -> WARNING: Could not find element for selector.`);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// --- 5. FINAL OUTPUT ---\nconsole.log(`\\n--- PROCESSING COMPLETE ---`);\nconsole.log(`Total items collected for AI: ${analysisItems.length}`);\n\nreturn {\n  json: {\n    analysisItems\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e8750f9c-778f-41fd-aead-270d33e255e4",
      "name": "CHANGE THESE: dependencies",
      "type": "n8n-nodes-base.set",
      "position": [
        -1880,
        -100
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "678e5308-808a-46c5-89b4-c68b272e03c6",
              "name": "wave_api_key",
              "type": "string",
              "value": ""
            },
            {
              "id": "470042c1-8609-41ab-ab1e-bf817035d426",
              "name": "URL to analyze",
              "type": "string",
              "value": "="
            },
            {
              "id": "d3c198ec-2bba-45d1-8687-b918fc2f2499",
              "name": "Email for summary",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "f15f810e-ce67-463e-8b99-61de6f1bcb6c",
              "name": "Desired Output Language",
              "type": "string",
              "value": "english"
            },
            {
              "id": "219c0199-a819-4e45-bd9e-aa7197060da1",
              "name": "Company Name",
              "type": "string",
              "value": "Tiro"
            },
            {
              "id": "9423eed7-e169-4312-8293-149d305d156e",
              "name": "Country of the Company (used to apply local law)",
              "type": "string",
              "value": "Germany"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "510ed358-75a8-4842-a8aa-0b2fa95144b6",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -2080,
        180
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "1ebee155-5b9a-4da3-8e63-054bf14b945a",
      "name": "Send accessibility statement by email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        80,
        180
      ],
      "parameters": {
        "sendTo": "={{ $('CHANGE THESE: dependencies').item.json['Email for summary'] }}",
        "message": "=This Email contains your Accessibility Statement. Check the attached files.",
        "options": {
          "attachmentsUi": {
            "attachmentsBinary": [
              {}
            ]
          }
        },
        "subject": "=Accessibility Statement for {{ $('CHANGE THESE: dependencies').item.json['Company Name'] }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "cdda9066-42d8-4d9f-97a8-9a5184bd58e6",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3020,
        -280
      ],
      "parameters": {
        "color": 5,
        "width": 880,
        "height": 920,
        "content": "# \ud83d\ude80 Automatically Generate Your EU Accessibility Statement\n\n**What This Template Does:**\nThis workflow scans any website, analyzes its accessibility issues, and uses an AI agent to automatically generate a formal Accessibility Statement (Erkl\u00e4rung zur Barrierefreiheit). This document is a legal requirement for most EU businesses under the **European Accessibility Act (EAA)**, with a compliance deadline of **June 28, 2025**.\n\n**How It Works:**\nIt uses the **WAVE API** to find accessibility issues and **Google Gemini** to write the statement based on a specialized legal prompt, creating a ready-to-use `.html` file.\n\n---\n\n### **Setup in 3 Steps:**\n\n**1. Configure All Variables \u270f\ufe0f**\n* Click the **'CHANGE THESE: dependencies'** node. This is your central control panel.\n* Fill in all the values, including your WAVE API Key, the URL to analyze, company details, and desired output language.\n\n**2. Set Up Credentials \u2728**\n* You need to connect your Google accounts for this workflow to run.\n* **Gemini:** Click the **'gemini2.5 pro'** node and connect your Google Gemini (or Google AI Studio) API credentials. You can swap this out for any other language model as well.\n* **Gmail:** Click the **'Send report by email'** node and connect your Gmail account to allow sending the final report.\n\n**3. Run the Workflow \u25b6\ufe0f**\n* Click **'Execute workflow'** to run the analysis. The generated `.html` statement will be sent to the email address you specified."
      },
      "typeVersion": 1
    },
    {
      "id": "f3a66efe-8bce-4f08-871d-9a9a70f8ab9e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1980,
        -380
      ],
      "parameters": {
        "color": 4,
        "width": 320,
        "height": 460,
        "content": "# \u2699\ufe0f Step 1: Start Here!\n\nThis is the main configuration node for the workflow.\n\nClick on this node and fill in all the required fields on the right-hand panel before running the workflow."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "gemini 2.5 pro": {
      "ai_languageModel": [
        [
          {
            "node": "Accessibility Statement Generator",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get WAVE Report": {
      "main": [
        [
          {
            "node": "Map WAVE Report Items to Website selectors.",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Website HTML": {
      "main": [
        [
          {
            "node": "Get WAVE Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse output as html": {
      "main": [
        [
          {
            "node": "Create accesibility statement html file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Accessibility Statement Generator",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "CHANGE THESE: dependencies": {
      "main": [
        [
          {
            "node": "Get Website HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Accessibility Statement Generator": {
      "main": [
        [
          {
            "node": "Parse output as html",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "CHANGE THESE: dependencies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create accesibility statement html file": {
      "main": [
        [
          {
            "node": "Send accessibility statement by email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map WAVE Report Items to Website selectors.": {
      "main": [
        [
          {
            "node": "Accessibility Statement Generator",
            "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 template is for any website owner, digital agency, or compliance officer operating within the European Union. It's designed for users who need to comply with the upcoming European Accessibility Act (EAA) but may not have deep technical or legal expertise.

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

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

HTTP Request, Google Sheets, OpenRouter Chat +5
AI & RAG

This comprehensive n8n workflow automates the complete process of generating professional interior design moodboards from concept to client delivery. Users submit a design brief through a form, and th

Google Gemini Chat, Output Parser Structured, Tool Think +7
AI & RAG

This workflow automates the extraction and processing of invoice data from PDFs stored in a Google Drive folder. It leverages Google Drive, Google Sheets, and Gemini AI to streamline invoice managemen

Google Gemini Chat, Google Sheets, HTTP Request +5
AI & RAG

This n8n workflow converts a YouTube video into a polished, email-ready newsletter. It scrapes the transcript, extracts a thumbnail/logo and brand color theme, uses multiple AI agents to (1) clean & s

Output Parser Structured, Agent, Gmail +5
AI & RAG

This blueprint details a highly efficient, AI-powered workflow designed to automate customer reward fulfillment. Leveraging the accessible interface of Jotform, this system delivers superior reliabili

Jot Form Trigger, Google Gemini Chat, HTTP Request +4