{
  "id": "rhJSbsxsjPsr4VJ6",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "n8n Workflow Auditor & Notion Doc Generator",
  "tags": [],
  "nodes": [
    {
      "id": "ddbc08fd-cfd0-43b7-8714-dac5a2c87bbe",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        23184,
        -2096
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## n8n Workflow Auditor & Notion Doc Generator\n\n### How it works\n\nThis workflow accepts an n8n workflow submission through a form, validates and audits the workflow JSON, then builds a prompt for an AI documentation generator. Claude Sonnet generates structured documentation, which is parsed into Notion-compatible blocks and published to a newly created Notion page. The workflow appends content in batches with a wait step to respect Notion API limits, and a separate error path captures failed executions.\n\n### Setup steps\n\n- Configure the form trigger fields so users can submit the workflow JSON and any required metadata.\n- Add Anthropic credentials for the Claude Sonnet chat model used by the LLM chain.\n- Connect Notion credentials and configure the target Notion database or parent page for newly generated documentation pages.\n- Review the custom code nodes for expected input field names, validation rules, prompt format, and Notion block output structure.\n- Set an appropriate batch size and wait duration for the Notion append loop to match Notion API rate limits.\n\n### Customization\n\nYou can customize the audit criteria, documentation prompt, Claude model settings, Notion page properties, block formatting, batch size, and rate-limit delay to match your documentation standards."
      },
      "typeVersion": 1
    },
    {
      "id": "2185c9d5-6797-445e-8877-d8dac7f7014e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        23712,
        -1952
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 368,
        "content": "## Capture workflow input\n\nStarts the main path with a submitted workflow and performs initial parsing and validation before deeper analysis."
      },
      "typeVersion": 1
    },
    {
      "id": "c92841e7-3c5e-4c4d-9641-92af934ea350",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        24160,
        -1952
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 368,
        "content": "## Audit and prompt\n\nAnalyzes the validated workflow and converts the audit results into a prompt for AI-generated documentation."
      },
      "typeVersion": 1
    },
    {
      "id": "8ca22035-82e1-4511-9572-ef0b7e19c389",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        24608,
        -1952
      ],
      "parameters": {
        "color": 7,
        "width": 544,
        "height": 576,
        "content": "## Generate AI documentation\n\nRuns the LLM documentation step with Claude Sonnet and parses the model output into a structured response for downstream formatting."
      },
      "typeVersion": 1
    },
    {
      "id": "d6977413-49fb-4860-b2bc-d17291d3d9bd",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        25184,
        -1952
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 384,
        "content": "## Prepare Notion page\n\nTransforms parsed documentation into Notion block payloads and creates the destination Notion page that will receive the content."
      },
      "typeVersion": 1
    },
    {
      "id": "a055357f-f68a-4865-a729-f7680034ef94",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        25632,
        -2096
      ],
      "parameters": {
        "color": 7,
        "width": 1088,
        "height": 528,
        "content": "## Append blocks in batches\n\nLoops through generated Notion blocks, appends them to the page, waits between batches for rate limiting, and exits through the completion node when all batches are processed."
      },
      "typeVersion": 1
    },
    {
      "id": "d31fe588-f631-4d3e-9408-576edb772146",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        23712,
        -1536
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 336,
        "content": "## Handle execution errors\n\nA separate lower canvas cluster catches failed workflow executions and stops with a logged error for troubleshooting."
      },
      "typeVersion": 1
    },
    {
      "id": "692c5ff9-1303-4415-bbdd-907ab77c44a9",
      "name": "When Form Submitted",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        23760,
        -1760
      ],
      "parameters": {
        "options": {},
        "formTitle": "Workflow Documentation Generator",
        "formFields": {
          "values": [
            {
              "fieldType": "file",
              "fieldLabel": "Workflow JSON",
              "multipleFiles": false,
              "requiredField": true,
              "acceptFileTypes": ".json"
            },
            {
              "fieldLabel": "Client Name",
              "placeholder": "e.g. Acme Corp",
              "requiredField": true
            },
            {
              "fieldLabel": "Notion Parent Page ID",
              "placeholder": "32-character page ID (no dashes)",
              "requiredField": true
            }
          ]
        },
        "formDescription": "Upload your exported n8n workflow JSON. A Notion page with a business summary and technical audit will be created automatically."
      },
      "typeVersion": 2.2
    },
    {
      "id": "9008dfbd-52d2-46ab-bd11-7f24b17d5044",
      "name": "Validate Form Data",
      "type": "n8n-nodes-base.code",
      "position": [
        23984,
        -1760
      ],
      "parameters": {
        "jsCode": "const item = $input.first();\nconst form = item.json;\n\n// Read uploaded JSON file\nlet raw = '';\nif (item.binary?.['Workflow JSON']) {\n  const buf = await this.helpers.getBinaryDataBuffer(0, 'Workflow JSON');\n  raw = buf.toString('utf-8');\n} else {\n  throw new Error('No workflow JSON file uploaded.');\n}\n\nconst wf = JSON.parse(raw);\nif (!Array.isArray(wf.nodes))           throw new Error('Invalid workflow: nodes must be an array.');\nif (typeof wf.connections !== 'object') throw new Error('Invalid workflow: connections must be an object.');\n\nconst notionParentId = (form['Notion Parent Page ID'] || '').trim();\nif (!notionParentId) throw new Error('Notion Parent Page ID is required.');\n\nconst clientName   = (form['Client Name'] || 'Client').trim();\nconst workflowName = (wf.name || 'Unnamed Workflow')\n  .replace(/[\\x00-\\x1F\\x7F]/g, '').replace(/[`*_#\\[\\]<>|]/g, ' ').trim()\n  || 'Unnamed Workflow';\n\nreturn [{ json: { wf, clientName, notionParentId, workflowName } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "3a14f08c-abd9-4ef8-99ed-9b34a5fa5e0d",
      "name": "Review Workflow Content",
      "type": "n8n-nodes-base.code",
      "position": [
        24208,
        -1760
      ],
      "parameters": {
        "jsCode": "const { wf, clientName, notionParentId, workflowName } = $input.first().json;\nconst { nodes, connections } = wf;\n\n// Build node maps\nconst byId   = Object.fromEntries(nodes.map(n => [n.id, n]));\nconst nameId = Object.fromEntries(nodes.map(n => [n.name, n.id]));\nconst adj    = Object.fromEntries(nodes.map(n => [n.id, []]));\nconst indeg  = Object.fromEntries(nodes.map(n => [n.id, 0]));\n\nfor (const [src, outs] of Object.entries(connections)) {\n  const sid = nameId[src]; if (!sid) continue;\n  for (const branches of Object.values(outs)) {\n    if (!Array.isArray(branches)) continue;\n    for (const branch of branches) {\n      if (!Array.isArray(branch)) continue;\n      for (const { node } of branch) {\n        const tid = nameId[node]; if (!tid) continue;\n        adj[sid].push(tid);\n        indeg[tid]++;\n      }\n    }\n  }\n}\n\n// Kahn's topological sort\nconst queue   = nodes.filter(n => indeg[n.id] === 0).map(n => n.id);\nconst sorted  = [];\nconst seen    = new Set();\nwhile (queue.length) {\n  const id = queue.shift();\n  if (seen.has(id)) continue;\n  seen.add(id); sorted.push(id);\n  for (const nid of adj[id]) { if (--indeg[nid] === 0) queue.push(nid); }\n}\n\nconst TRIGGERS = new Set(['n8n-nodes-base.webhook','n8n-nodes-base.scheduleTrigger',\n  'n8n-nodes-base.manualTrigger','n8n-nodes-base.formTrigger',\n  'n8n-nodes-base.emailReadImap','@n8n/n8n-nodes-langchain.chatTrigger']);\n\nconst cycleNodes = nodes.filter(n => !seen.has(n.id)).map(n => n.name);\nconst deadNodes  = nodes.filter(n => {\n  return !connections[n.name] && !nodes.some(o => adj[o.id]?.includes(n.id))\n         && !TRIGGERS.has(n.type);\n}).map(n => n.name);\n\nconst ordered = [...sorted, ...cycleNodes.map(nm => nameId[nm]).filter(Boolean)]\n  .map(id => byId[id]).filter(Boolean);\n\n// Static audit\nconst findings = [];\nconst SECRETS = [\n  [/sk-[a-zA-Z0-9]{20,}/,              'OpenAI API key'],\n  [/xoxb-[a-zA-Z0-9-]+/,               'Slack bot token'],\n  [/ghp_[a-zA-Z0-9]{36}/,              'GitHub PAT'],\n  [/Bearer\\s+[a-zA-Z0-9\\-_.]{20,}/,    'hardcoded Bearer YOUR_TOKEN_HERE'],\n];\n\nfor (const n of nodes) {\n  const p = JSON.stringify(n.parameters || {});\n  if (n.type === 'n8n-nodes-base.httpRequest' && !n.parameters?.options?.timeout)\n    findings.push({ sev:'HIGH', node:n.name, issue:'HTTP Request has no timeout set.', fix:'Set Options \u203a Timeout to 15000\u201330000 ms.' });\n  for (const [re, label] of SECRETS)\n    if (re.test(p)) findings.push({ sev:'CRITICAL', node:n.name, issue:`Possible hardcoded ${label}.`, fix:'Move to n8n Credentials.' });\n  if (n.type === 'n8n-nodes-base.webhook' && (!n.parameters?.authentication || n.parameters.authentication === 'none'))\n    findings.push({ sev:'HIGH', node:n.name, issue:'Webhook has no authentication.', fix:'Enable Header Auth or Basic Auth.' });\n  if (n.type === '@n8n/n8n-nodes-langchain.agent' && !nodes.some(o => /approval|confirm|human/i.test(o.name)))\n    findings.push({ sev:'MEDIUM', node:n.name, issue:'AI Agent has no human-in-the-loop step.', fix:'Add a Wait or approval node before destructive actions.' });\n  if (['n8n-nodes-base.httpRequest','n8n-nodes-base.executeWorkflow'].includes(n.type)) {\n    const hasErrNode = adj[n.id]?.some(id => byId[id]?.name?.toLowerCase().includes('error'));\n    if (!hasErrNode) findings.push({ sev:'MEDIUM', node:n.name, issue:'No explicit error-handling node downstream.', fix:'Add an error branch or configure \"On Error: Continue\" with conditional logic.' });\n  }\n}\nfor (const nm of deadNodes)  findings.push({ sev:'LOW',    node:nm, issue:'Node is unreachable.', fix:'Remove or reconnect it.' });\nfor (const nm of cycleNodes) findings.push({ sev:'MEDIUM', node:nm, issue:'Node is in a cycle \u2014 sort order unresolved.', fix:'Ensure loops have an exit condition.' });\n\nconst score = Math.max(0, 100\n  - findings.filter(f=>f.sev==='CRITICAL').length * 25\n  - findings.filter(f=>f.sev==='HIGH').length     * 10\n  - findings.filter(f=>f.sev==='MEDIUM').length   *  5\n  - findings.filter(f=>f.sev==='LOW').length      *  2);\n\nconst nodeList = ordered.map((n,i) => {\n  const ds = (adj[n.id]||[]).map(id=>byId[id]?.name).filter(Boolean);\n  return `${i+1}. [${n.type}] \"${n.name}\"${ds.length ? ' \u2192 '+ds.join(', ') : ' (terminal)'}${cycleNodes.includes(n.name)?' \u26a0 CYCLE':''}`;\n}).join('\\n');\n\nconst findingsSummary = findings.length\n  ? findings.map(f=>`[${f.sev}] ${f.node}: ${f.issue} Fix: ${f.fix}`).join('\\n')\n  : 'No issues found.';\n\nreturn [{ json: {\n  workflowName, clientName, notionParentId,\n  nodeCount: nodes.length, score,\n  criticalCount: findings.filter(f=>f.sev==='CRITICAL').length,\n  highCount:     findings.filter(f=>f.sev==='HIGH').length,\n  mediumCount:   findings.filter(f=>f.sev==='MEDIUM').length,\n  lowCount:      findings.filter(f=>f.sev==='LOW').length,\n  findings, cycleNodes, deadNodes, hasCycles: cycleNodes.length > 0,\n  nodeList, findingsSummary\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "a3934f6e-7dd1-4b75-a0f5-bd3c2ae0dccc",
      "name": "Construct AI Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        24432,
        -1760
      ],
      "parameters": {
        "jsCode": "const d = $input.first().json;\n\nconst system = `You are an automation consultant writing client documentation.\n\nReturn ONLY a raw JSON object with two keys \u2014 no markdown fences, no preamble:\n{\n  \"businessLogic\": \"<150-300 word plain-English summary of what this workflow does, when it runs, and what outcomes it produces. No technical node names.>\",\n  \"auditReport\":   \"<Developer-facing audit findings in Markdown. Bold each severity label. End with an overall assessment paragraph. If no findings, say so.>\"\n}`;\n\nconst user =\n`Workflow: ${d.workflowName}\nClient: ${d.clientName}\nNodes: ${d.nodeCount} | Audit Score: ${d.score}/100\n${d.hasCycles ? 'WARNING: cycles detected in ' + d.cycleNodes.join(', ') : ''}\n\nEXECUTION ORDER:\n${d.nodeList}\n\nAUDIT FINDINGS:\n${d.findingsSummary}`;\n\nreturn [{ json: { ...d, system, user } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "c34d4be6-afa4-450a-9ae3-a37acee29ace",
      "name": "Generate Documentation",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        24656,
        -1760
      ],
      "parameters": {
        "messages": {
          "messageValues": [
            {
              "message": "={{ $json.system }}"
            }
          ]
        }
      },
      "typeVersion": 1.4
    },
    {
      "id": "7e96ba02-6f50-419e-9da7-3c31fd1c5641",
      "name": "Claude Model Interaction",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        24736,
        -1536
      ],
      "parameters": {
        "model": "claude-sonnet-4-6",
        "options": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "f693461e-63c9-406c-bb8b-133af29fbbcd",
      "name": "Interpret AI Response",
      "type": "n8n-nodes-base.code",
      "position": [
        25008,
        -1760
      ],
      "parameters": {
        "jsCode": "const text = ($input.first().json.text || '').trim()\n  .replace(/^```(?:json)?\\n?/, '').replace(/\\n?```$/, '');\n\nif (!text) throw new Error('Model returned empty output.');\n\nlet parsed;\ntry   { parsed = JSON.parse(text); }\ncatch { throw new Error('Model did not return valid JSON: ' + text.substring(0, 200)); }\n\nif (!parsed.businessLogic) throw new Error('Missing businessLogic in model response.');\nif (!parsed.auditReport)   throw new Error('Missing auditReport in model response.');\n\nconst ctx = $('Construct AI Prompt').first().json;\n\nreturn [{ json: {\n  ...ctx,\n  businessLogicMd: parsed.businessLogic,\n  auditReportMd:   parsed.auditReport\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "1c79db3d-ec34-47e7-8f9d-22be290ff83e",
      "name": "Formulate Notion Blocks",
      "type": "n8n-nodes-base.code",
      "position": [
        25232,
        -1760
      ],
      "parameters": {
        "jsCode": "const { workflowName, clientName, notionParentId, score,\n        criticalCount, highCount, mediumCount, lowCount,\n        nodeCount, businessLogicMd, auditReportMd,\n        findings, hasCycles, cycleNodes, deadNodes } = $input.first().json;\n\nconst MAX = 1990;\n\nfunction spans(text, ann={}) {\n  const chunks = [];\n  let s = String(text||'');\n  while (s.length > MAX) {\n    let i = s.lastIndexOf(' ', MAX); if (i < MAX*0.5) i = MAX;\n    chunks.push(s.slice(0,i)); s = s.slice(i).trimStart();\n  }\n  if (s) chunks.push(s);\n  return chunks.map(c => ({ type:'text', text:{content:c},\n    annotations:{bold:false,italic:false,strikethrough:false,\n                 underline:false,code:false,color:'default',...ann} }));\n}\n\nfunction mdToBlocks(md) {\n  const out = []; let inCode=false, codeLines=[], lang='';\n  for (const line of (md||'').split('\\n')) {\n    if (/^```/.test(line.trim())) {\n      if (!inCode) { inCode=true; lang=line.trim().slice(3)||'plain text'; codeLines=[]; }\n      else {\n        inCode=false;\n        const content=codeLines.join('\\n');\n        for(let i=0;i<content.length;i+=MAX)\n          out.push({object:'block',type:'code',code:{language:lang,rich_text:[{type:'text',text:{content:content.slice(i,i+MAX)}}]}});\n        codeLines=[]; lang='';\n      }\n      continue;\n    }\n    if (inCode) { codeLines.push(line); continue; }\n    const t = line.trimEnd();\n    if (!t) continue;\n    if (/^### /.test(t)) { out.push({object:'block',type:'heading_3',heading_3:{rich_text:spans(t.slice(4))}}); continue; }\n    if (/^## /.test(t))  { out.push({object:'block',type:'heading_2',heading_2:{rich_text:spans(t.slice(3))}}); continue; }\n    if (/^# /.test(t))   { out.push({object:'block',type:'heading_1',heading_1:{rich_text:spans(t.slice(2))}}); continue; }\n    if (/^[-*] /.test(t)){ out.push({object:'block',type:'bulleted_list_item',bulleted_list_item:{rich_text:spans(t.slice(2))}}); continue; }\n    if (/^\\d+\\. /.test(t)){ out.push({object:'block',type:'numbered_list_item',numbered_list_item:{rich_text:spans(t.replace(/^\\d+\\. /,''))}}); continue; }\n    if (/^---+$/.test(t)) { out.push({object:'block',type:'divider',divider:{}}); continue; }\n    // paragraph with inline bold\n    const parts=[]; const re=/\\*\\*(.+?)\\*\\*/g; let idx=0,m;\n    while((m=re.exec(t))!==null){if(m.index>idx)parts.push(...spans(t.slice(idx,m.index)));parts.push(...spans(m[1],{bold:true}));idx=m.index+m[0].length;}\n    if(idx<t.length)parts.push(...spans(t.slice(idx)));\n    out.push({object:'block',type:'paragraph',paragraph:{rich_text:(parts.length?parts:spans(t)).slice(0,100)}});\n  }\n  return out;\n}\n\nconst emoji  = score>=80?'\u2705':score>=50?'\u26a0\ufe0f':'\ud83d\udd34';\nconst color  = score>=80?'green_background':score>=50?'yellow_background':'red_background';\nconst today  = new Date().toISOString().slice(0,10);\n\nconst blocks = [\n  { object:'block',type:'callout',callout:{\n    rich_text: spans(`${emoji} Audit Score: ${score}/100  \u00b7  Critical: ${criticalCount}  High: ${highCount}  Medium: ${mediumCount}  Low: ${lowCount}  \u00b7  ${clientName}  \u00b7  ${today}`),\n    color, icon:{type:'emoji',emoji}\n  }},\n  {object:'block',type:'divider',divider:{}},\n  {object:'block',type:'heading_2',heading_2:{rich_text:spans('Business Logic Summary')}},\n  ...mdToBlocks(businessLogicMd),\n  {object:'block',type:'divider',divider:{}},\n  {object:'block',type:'heading_2',heading_2:{rich_text:spans('Technical Audit Report')}},\n  ...mdToBlocks(auditReportMd),\n];\n\nif (hasCycles || deadNodes?.length) {\n  blocks.push({object:'block',type:'divider',divider:{}});\n  blocks.push({object:'block',type:'heading_2',heading_2:{rich_text:spans('Graph Warnings')}});\n  if (hasCycles)       blocks.push({object:'block',type:'callout',callout:{rich_text:spans('Cycles: '+cycleNodes.join(', ')),color:'orange_background',icon:{type:'emoji',emoji:'\u26a0\ufe0f'}}});\n  if (deadNodes?.length) blocks.push({object:'block',type:'callout',callout:{rich_text:spans('Unreachable: '+deadNodes.join(', ')),color:'yellow_background',icon:{type:'emoji',emoji:'\ud83d\udd38'}}});\n}\n\nblocks.push({object:'block',type:'divider',divider:{}});\nblocks.push({object:'block',type:'paragraph',paragraph:{rich_text:[\n  ...spans('Generated by '), ...spans('AutomiQ',{bold:true}), ...spans(' \u00b7 automiq.fi \u00b7 '+today)\n]}});\n\nconst batches=[];\nfor(let i=0;i<blocks.length;i+=99) batches.push(blocks.slice(i,i+99));\n\nreturn batches.map((b,i)=>({json:{notionParentId,workflowName,blocks:b,batchIndex:i,totalBatches:batches.length}}));"
      },
      "typeVersion": 2
    },
    {
      "id": "9c5f6bfa-966f-451f-9e0b-8bc13bdc5637",
      "name": "Create Notion Document",
      "type": "n8n-nodes-base.notion",
      "position": [
        25456,
        -1760
      ],
      "parameters": {
        "title": "={{ $('Construct AI Prompt').first().json.workflowName }} \u2014 Docs",
        "pageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Construct AI Prompt').first().json.notionParentId }}"
        },
        "blockUi": {
          "blockValues": []
        },
        "options": {}
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "39f915b0-904a-480c-98c7-31b6e5fe762b",
      "name": "Process Batch of 10",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        25936,
        -1760
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "ebf15d41-ad3c-4c1c-8cde-7c1bd1d13e9a",
      "name": "Append to Notion Document",
      "type": "n8n-nodes-base.notion",
      "position": [
        26160,
        -1952
      ],
      "parameters": {
        "blockId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Create Notion Document').first().json.id }}"
        },
        "blockUi": {
          "blockValues": [
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {},
            {}
          ]
        },
        "resource": "block"
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "b35f8579-8350-4942-8a39-135d7ac15b7d",
      "name": "Completion Marker",
      "type": "n8n-nodes-base.noOp",
      "position": [
        26160,
        -1760
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "e5092f35-a8d5-428c-84bb-213dffd3b115",
      "name": "Wait 350 Milliseconds",
      "type": "n8n-nodes-base.wait",
      "position": [
        26384,
        -1776
      ],
      "parameters": {
        "amount": 35
      },
      "typeVersion": 1.1
    },
    {
      "id": "4c1783c2-888f-4434-bb30-18eb30e987d6",
      "name": "On Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        23760,
        -1360
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "ec4d4b78-2e05-4eb2-aa47-e75b00b2ef4e",
      "name": "Record Error Details",
      "type": "n8n-nodes-base.stopAndError",
      "position": [
        23984,
        -1360
      ],
      "parameters": {
        "errorMessage": "={{ 'Workflow documentation failed\\n\\nNode: ' + $json.execution.lastNodeExecuted + '\\nError: ' + $json.error.message }}"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "c096ff74-2d7f-40c9-b1e5-8e59afbb0c81",
  "nodeGroups": [],
  "connections": {
    "On Error Trigger": {
      "main": [
        [
          {
            "node": "Record Error Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Form Data": {
      "main": [
        [
          {
            "node": "Review Workflow Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Construct AI Prompt": {
      "main": [
        [
          {
            "node": "Generate Documentation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Batch of 10": {
      "main": [
        [
          {
            "node": "Append to Notion Document",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Completion Marker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Form Submitted": {
      "main": [
        [
          {
            "node": "Validate Form Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Interpret AI Response": {
      "main": [
        [
          {
            "node": "Formulate Notion Blocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 350 Milliseconds": {
      "main": [
        [
          {
            "node": "Process Batch of 10",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Notion Document": {
      "main": [
        [
          {
            "node": "Process Batch of 10",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Documentation": {
      "main": [
        [
          {
            "node": "Interpret AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Formulate Notion Blocks": {
      "main": [
        [
          {
            "node": "Create Notion Document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Review Workflow Content": {
      "main": [
        [
          {
            "node": "Construct AI Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Model Interaction": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Documentation",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Append to Notion Document": {
      "main": [
        [
          {
            "node": "Wait 350 Milliseconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}