{"workflow":{"id":14093,"name":"Generate C API HTML docs from Google Drive headers with GPT-4o and Gmail","views":27,"recentViews":1,"totalViews":27,"createdAt":"2026-03-16T17:55:53.272Z","description":"\n## 🚀 Overview\n\nThis n8n workflow automatically generates **professional API documentation from C header (`.h`) files** using AI.\n\nIt scans a Google Drive folder for header files, extracts the source code, sends it to **GPT-4o** for structured analysis, and generates a **beautiful HTML documentation page**. The final documentation is uploaded back to Google Drive and a completion email is sent.\n\nThis workflow is ideal for **embedded systems teams, firmware engineers, and SDK developers** who want an automated documentation pipeline.\n\n---\n\n# ✨ Key Features\n\n⚡ Fully automated documentation generation  \n📁 Reads `.h` files directly from **Google Drive**  \n🤖 Uses **AI to analyze C APIs and extract documentation**  \n📑 Generates **clean HTML documentation**  \n📊 Documents functions, types, enums, and constants  \n🔁 Processes files **one-by-one for reliability**  \n☁️ Saves generated documentation back to **Google Drive**  \n📧 Sends a **completion email notification**\n\n---\n\n# 🧠 What the AI Extracts\n\nThe workflow automatically identifies and documents:\n\n- 📘 **Overview** of the header file\n- 🔧 **Functions**\n  - Signatures\n  - Parameters\n  - Return values\n  - Usage examples\n- 🧩 **Enumerations**\n- 🧱 **Data Types & Structures**\n- 🔢 **Constants / Macros**\n- 📝 **Developer Notes**\n\n---\n\n# 🖥 Generated Documentation\n\nThe output is a **clean developer-friendly HTML documentation page** including:\n\n- 🧭 Sidebar navigation\n- 📌 Function cards\n- 📊 Parameter tables\n- 💻 Code examples\n- 🎨 Professional developer layout\n\nPerfect for:\n\n- Developer portals\n- SDK documentation\n- Internal engineering documentation\n- Embedded system libraries\n\n---\n\n# ⚙️ Workflow Architecture\n\n| Step | Node | Purpose |\n|-----|-----|--------|\n| 1 | ▶️ Manual Trigger | Starts the workflow |\n| 2 | 📂 Get all files | Reads files from Google Drive |\n| 3 | 🔎 Filter `.h` files | Keeps only header files |\n| 4 | 🔁 Split in Batches | Processes files sequentially |\n| 5 | ⬇️ Download file | Downloads the header file |\n| 6 | 📖 Extract text | Extracts code content |\n| 7 | 🤖 AI Extraction | AI extracts API structure |\n| 8 | 🧹 Parse JSON | Cleans AI output |\n| 9 | 🎨 Generate HTML | Builds documentation page |\n|10 | ☁️ Upload to Drive | Saves documentation |\n|11 | 📧 Email notification | Sends completion email |\n\n---\n\n# 🔧 Requirements\n\nTo run this workflow you need:\n\n🔹 **Google Drive OAuth2 credentials**  \n🔹 **OpenAI API credentials**  \n🔹 **Gmail credentials**\n\n---\n\n# 🛠 Setup Guide\n\n## 1️⃣ Configure Google Drive\n\nCreate two folders.\n\n**Source folder**\n\n**Output folder**\n\nUpdate the folder IDs in the nodes:\n\n- `Get all files from folder`\n- `Save documentation to Google Drive`\n\n---\n\n## 2️⃣ Configure OpenAI\n\nAdd an **OpenAI credential** in n8n.\n\nModel used:\n\nThe model analyzes C header files and returns structured API documentation.\n\n---\n\n## 3️⃣ Configure Gmail\n\nAdd a **Gmail OAuth credential**.\n\nUpdate the recipient address inside:\n\n---\n\n## ▶️ Run the Workflow\n\nClick **Execute Workflow**.\n\nThe workflow will:\n\n1️⃣ Scan the Google Drive folder  \n2️⃣ Process each `.h` file  \n3️⃣ Generate HTML documentation  \n4️⃣ Upload documentation to Drive  \n5️⃣ Send a completion email  \n\n---\n\n# 🖼 Documentation Preview\n\n![API Documentation Example](https://raw.githubusercontent.com/tejasv694/n8n-template-images/main/image.png)\n\n---\n\n# 💡 Use Cases\n\n🔧 Embedded firmware documentation  \n📦 SDK documentation generation  \n🧑‍💻 Developer portal automation  \n📚 C library documentation  \n⚙️ Continuous documentation pipelines  \n\n---\n\n# 🔮 Future Improvements\n\nThis workflow can be extended with several enhancements:\n\n### 📄 PDF Documentation Export\nAdd a step to convert the generated HTML documentation into **PDF files** using tools such as:\n\n- Puppeteer\n- HTML-to-PDF services\n- n8n community PDF nodes\n\nThis allows teams to distribute documentation as **downloadable reports**.\n\n---\n\n### 🔐 Local AI for Security (Ollama / Open-Source Models)\n\nInstead of using the OpenAI node, the workflow can be modified to run **fully locally** using AI models such as:\n\n- **Ollama**\n- **Open-source LLMs (Llama, Mistral, CodeLlama)**\n\nThese models can run **on your own server**, which provides:\n\n🔒 Better **data privacy**  \n🏢 **No external API calls**  \n⚡ Faster responses on local infrastructure  \n🛡 Increased **security for proprietary source code**\n\nThis can be implemented in n8n using:\n\n- **HTTP Request node → Ollama API**\n- Local AI inference servers\n- Private LLM deployments\n\n---\n\n### 📚 Multi-Language Documentation\n\nThe workflow could also support additional languages such as:\n.c\n.cpp\n.hpp\n.rs\n.go\n","workflow":{"meta":{},"name":"API Documentation — Generator","tags":[],"nodes":[{"id":"8655873a-3376-45e4-8311-68a646570e47","name":"Extract from File2","type":"n8n-nodes-base.extractFromFile","position":[-208,544],"parameters":{"options":{},"operation":"text"},"typeVersion":1.1},{"id":"a0edb4ab-f3e6-4ff0-be75-a7ef3d6bfb39","name":"When clicking ‘Execute workflow’","type":"n8n-nodes-base.manualTrigger","position":[-1520,448],"parameters":{},"typeVersion":1},{"id":"75a5f62e-1479-4845-84ce-283291ad65a5","name":"Sticky Note — Step 1","type":"n8n-nodes-base.stickyNote","position":[-1520,256],"parameters":{"color":4,"width":560,"height":344,"content":"### 📁 Step 1 — Load Files\nTrigger manually, then fetch all files from the configured Google Drive folder and filter down to `.h` files only.\n\n⚙️ **To configure:** Open **Get all files from folder** and update the `queryString` with your own Google Drive folder ID."},"typeVersion":1},{"id":"046dba14-4d05-491a-a311-2446963de7dd","name":"Sticky Note — Step 8","type":"n8n-nodes-base.stickyNote","position":[-848,256],"parameters":{"width":252,"height":424,"content":"### 🔁 Step 2 — Loop\nProcess one `.h` file at a time using **Split in Batches**.\n\nThe loop returns here after each file is saved to Drive, until all files are processed. When done, it branches to the email notification."},"typeVersion":1},{"id":"77d98362-d5c4-4be3-967d-da5fc59d573b","name":"Sticky Note — Step 9","type":"n8n-nodes-base.stickyNote","position":[-464,416],"parameters":{"width":372,"height":280,"content":"### 📥 Step 3 — Download & Read\nDownloads the binary file from Google Drive and extracts its raw text content, ready to be sent to GPT-4o."},"typeVersion":1},{"id":"6493ac89-3f40-45eb-822f-988d82f4a570","name":"Sticky Note — Step 10","type":"n8n-nodes-base.stickyNote","position":[0,256],"parameters":{"color":2,"width":248,"height":392,"content":"### 🤖 Step 4 — AI Extraction\nSends the raw `.h` file content to **GPT-4o** with a structured prompt.\n\nGPT returns a strict JSON object with:\n`overview` · `functions` · `enumerators` · `data_types` · `constants` · `notes`\n\n⚠️ Requires an **OpenAI credential** with GPT-4o access."},"typeVersion":1},{"id":"11cab0db-26db-4a64-ae73-c7601eefdf75","name":"Sticky Note — Step 11","type":"n8n-nodes-base.stickyNote","position":[288,320],"parameters":{"width":224,"height":360,"content":"### 🔧 Step 5 — Parse Response\nStrips any markdown fences from the GPT response and parses it as JSON.\n\n💡 If parsing fails, the raw GPT text is placed in `overview` so you can debug what GPT returned."},"typeVersion":1},{"id":"f248ab44-1130-49c1-90f2-ec65567cc480","name":"Sticky Note — Step 12","type":"n8n-nodes-base.stickyNote","position":[560,224],"parameters":{"color":5,"width":244,"height":472,"content":"### 🎨 Step 6 — Generate HTML\nBuilds a fully styled HTML documentation file with:\n- Sidebar navigation\n- Hero header with metadata\n- Function cards with signature, params table & examples\n- Data types, enums, constants sections\n\nOutputs as a **binary `.html` file** ready for Google Drive upload."},"typeVersion":1},{"id":"ebb3f0fc-5d41-4022-a8d2-58b7b951c215","name":"Sticky Note — Step 13","type":"n8n-nodes-base.stickyNote","position":[848,224],"parameters":{"color":4,"width":252,"height":472,"content":"### 💾 Step 7 — Save to Drive\nUploads the generated `DOC_filename.html` file to the configured output folder in Google Drive.\n\n⚙️ **To configure:** Update the `folderId` in this node with your output folder ID."},"typeVersion":1},{"id":"0410927e-234a-46f5-aca6-a9a60eeb56d0","name":"Sticky Note — Email1","type":"n8n-nodes-base.stickyNote","position":[-480,-32],"parameters":{"color":2,"width":440,"height":340,"content":"### ✉️ Completion Email\nOnce all files are processed, the **Split in Batches** node exits the loop and triggers this branch.\n\nBuilds a summary email and sends it via **Gmail** to notify that all docs have been generated.\n\n⚙️ Update the recipient email in **Send completion email**."},"typeVersion":1},{"id":"240eb434-776f-4195-a456-15a83b8f5d5a","name":"Sticky Note — Overview1","type":"n8n-nodes-base.stickyNote","position":[-1968,80],"parameters":{"color":5,"width":396,"height":678,"content":"# 📄 API Documentation Generator\n\nAutomatically generates **beautiful HTML documentation** from C header (`.h`) files stored in Google Drive.\n\n**How it works:**\n1. Reads all files from a specified Google Drive folder\n2. Filters to `.h` header files only\n3. Processes each file one at a time through GPT-4o\n4. GPT extracts functions, types, enums, constants & notes as structured JSON\n5. Generates a professional HTML doc and saves it back to Google Drive\n6. Sends a completion email when all files are processed\n\n**Requirements:**\n- Google Drive OAuth2 credentials\n- OpenAI API credentials\n- Gmail credentials\n- Update the Google Drive folder ID in the **Get all files** node"},"typeVersion":1},{"id":"f63de4ea-6d88-4f10-a195-feb2f7b5f3ea","name":"Get all files from folder","type":"n8n-nodes-base.googleDrive","position":[-1328,448],"parameters":{"filter":{},"options":{},"resource":"fileFolder","returnAll":true,"queryString":"'YOUR_FOLDER_ID' in parents and trashed=false","searchMethod":"query"},"typeVersion":3},{"id":"14c89229-0249-4e47-a46c-1480e961b59c","name":"Filter .h files only","type":"n8n-nodes-base.code","position":[-1104,448],"parameters":{"jsCode":"const items = $input.all();\nconst results = [];\nfor (const item of items) {\n  const name = (item.json.name || '').toLowerCase();\n  if (name.endsWith('.h')) {\n    results.push({ json: { id: item.json.id, fileName: item.json.name } });\n  }\n}\nif (results.length === 0) throw new Error('No .h header files found.');\nreturn results;"},"typeVersion":2},{"id":"b2362399-2709-42d6-a294-5d834562a4b7","name":"Process one file at a time","type":"n8n-nodes-base.splitInBatches","position":[-768,528],"parameters":{"options":{}},"executeOnce":false,"typeVersion":3},{"id":"e74de2b2-e2bf-4e9f-b191-e703a914cb15","name":"Download file","type":"n8n-nodes-base.googleDrive","position":[-432,544],"parameters":{"fileId":{"__rl":true,"mode":"","value":"={{ $json.id }}"},"options":{},"operation":"download"},"typeVersion":3},{"id":"477c9a71-e6b8-4125-8dca-9987320fda1a","name":"Build email body","type":"n8n-nodes-base.code","position":[-400,160],"parameters":{"jsCode":"const items = $input.all();\nfor (const item of items) {\n  const fileName = item.json.pdfName || item.json.name || 'DOC.pdf';\n  item.json.subject = 'API Documentation PDF Ready — XYZ Technologies';\n  item.json.message = `Hello,\n\nYour C header file API documentation PDF has been generated by XYZ Technologies.\n\nFile: ${fileName}\nUploaded to: Output folder in Google Drive\nGenerated: ${new Date().toUTCString()}\n\nThe PDF includes:\n  - Overview\n  - Functions (signatures, parameters, return values, examples)\n  - Enumerators\n  - Data Types & Structures\n  - Constants\n  - Notes\n\nBest regards,\nXYZ Technologies Automation`;\n}\nreturn items;"},"typeVersion":2},{"id":"e350754f-4db4-407b-a6d0-ce199df685bd","name":"Send completion email","type":"n8n-nodes-base.gmail","position":[-192,160],"parameters":{"sendTo":"your@email.com","message":"={{ $json.message }}","options":{},"subject":"={{ $json.subject }}"},"typeVersion":2.2},{"id":"8e43b277-9554-43e2-bc04-db84eadb6b51","name":"Extract structured docs (GPT-4o)","type":"@n8n/n8n-nodes-langchain.openAi","position":[16,544],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4o","cachedResultName":"GPT-4O"},"options":{},"responses":{"values":[{"role":"system","content":"You are a senior technical writer for embedded systems at XYZ Technologies. Given a C header (.h) file, extract and document everything as strict JSON — no markdown, no explanation, raw JSON only.\n\nReturn exactly this structure:\n{\n  \"overview\": \"<2-3 sentence summary>\",\n  \"functions\": [\n    {\n      \"name\": \"<function name>\",\n      \"signature\": \"<full prototype>\",\n      \"description\": \"<what it does>\",\n      \"parameters\": [\n        { \"name\": \"<param>\", \"type\": \"<type>\", \"description\": \"<desc>\" }\n      ],\n      \"returns\": \"<return type and meaning>\",\n      \"example\": \"<minimal C usage>\"\n    }\n  ],\n  \"enumerators\": [\n    {\n      \"name\": \"<enum type name>\",\n      \"description\": \"<purpose>\",\n      \"values\": [\n        { \"name\": \"<VALUE>\", \"value\": \"<integer>\", \"description\": \"<meaning>\" }\n      ]\n    }\n  ],\n  \"data_types\": [\n    {\n      \"name\": \"<type name>\",\n      \"kind\": \"<struct|union|typedef|bitfield>\",\n      \"description\": \"<purpose>\",\n      \"members\": [\n        { \"name\": \"<field>\", \"type\": \"<type>\", \"description\": \"<meaning>\" }\n      ]\n    }\n  ],\n  \"constants\": [\n    { \"name\": \"<CONSTANT_NAME>\", \"value\": \"<value>\", \"description\": \"<meaning>\" }\n  ],\n  \"notes\": \"<usage notes, thread safety, prerequisites>\"\n}\n\nIf a section has no items return []. Return ONLY the JSON object."},{"content":"=File: {{ $('Process one file at a time').item.json.fileName }}\n\n{{ $('Extract from File2').item.json.data }}"}]},"builtInTools":{}},"typeVersion":2.1},{"id":"f463a4fa-5996-4e9d-8f10-bc2f9c54d4ad","name":"Parse JSON response","type":"n8n-nodes-base.code","position":[352,544],"parameters":{"jsCode":"// ── Parse JSON response — handles all known OpenAI/n8n response shapes ──────\nconst item = $input.first();\n\n// Log the full response so you can inspect it in n8n's output panel\n// (remove once working)\nconsole.log('RAW ITEM KEYS:', JSON.stringify(Object.keys(item.json || {})));\n\nconst raw =\n  // n8n OpenAI node (AI Agent / Chat Model)\n  item.json?.choices?.[0]?.message?.content ||\n  // OpenAI Responses API shape\n  item.json?.output?.[0]?.content?.[0]?.text ||\n  // Fallback: sometimes n8n wraps it under .text or .message\n  item.json?.message?.content ||\n  item.json?.text ||\n  // Last resort: if the whole json IS the parsed object already\n  (item.json?.overview ? JSON.stringify(item.json) : '{}');\n\nlet parsed;\ntry {\n  const cleaned = raw\n    .replace(/^```(?:json)?\\r?\\n?/, '')\n    .replace(/\\r?\\n?```$/, '')\n    .trim();\n  parsed = JSON.parse(cleaned);\n} catch (e) {\n  // If JSON parse fails, put the raw text in overview so you can see what GPT returned\n  parsed = {\n    overview: 'JSON parse failed. Raw response: ' + raw.slice(0, 500),\n    functions: [],\n    enumerators: [],\n    data_types: [],\n    constants: [],\n    notes: ''\n  };\n}\n\n// Try multiple places for fileName\nconst fileName =\n  $('Process one file at a time')?.item?.json?.fileName ||\n  $('Process one file at a time1')?.item?.json?.fileName ||\n  item.json?.fileName ||\n  'unknown.h';\n\nreturn [{ json: { ...parsed, fileName } }];\n"},"typeVersion":2},{"id":"f7a197b7-3470-4ad5-a2bb-fa913c461969","name":"Generate HTML","type":"n8n-nodes-base.code","position":[608,544],"parameters":{"jsCode":"// ═══════════════════════════════════════════════════════════════════════════\n// GENERATE HTML FILE — outputs binary HTML file → save directly to Google Drive\n// Replace \"Generate PDF (zero dependencies)1\" with this node.\n// Then wire: this node → Save to Google Drive (upload file)\n// ═══════════════════════════════════════════════════════════════════════════\n\nconst doc = $input.first().json;\nconst fileName = doc.fileName || 'unknown.h';\nconst baseName = fileName.replace(/\\.[^/.]+$/, '');\nconst htmlName = 'DOC_' + baseName + '.html';\nconst today    = new Date().toISOString().slice(0, 10);\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\nconst esc = s => String(s || '')\n  .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\n\nfunction funcBlock(fn) {\n  const params = fn.parameters || [];\n  const paramRows = params.map(p => `\n    <tr>\n      <td><code>${esc(p.name)}</code></td>\n      <td><code>${esc(p.type)}</code></td>\n      <td>${esc(p.description)}</td>\n    </tr>`).join('');\n\n  return `\n  <div class=\"card fn-block\">\n    <div class=\"fn-name\">${esc(fn.name || '')}</div>\n    <p class=\"fn-desc\">${esc(fn.description || '')}</p>\n    ${fn.signature ? `<div class=\"label\">Signature</div><pre><code>${esc(fn.signature)}</code></pre>` : ''}\n    ${params.length ? `\n    <div class=\"label\">Parameters</div>\n    <div class=\"table-wrap\"><table>\n      <thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead>\n      <tbody>${paramRows}</tbody>\n    </table></div>` : ''}\n    ${fn.returns ? `<div class=\"label\">Returns</div><p class=\"returns\">${esc(fn.returns)}</p>` : ''}\n    ${fn.example ? `<div class=\"label\">Example</div><pre><code>${esc(fn.example)}</code></pre>` : ''}\n  </div>`;\n}\n\nfunction dtBlock(dt) {\n  const members = dt.members || [];\n  const rows = members.map(m => `\n    <tr>\n      <td><code>${esc(m.name)}</code></td>\n      <td><code>${esc(m.type)}</code></td>\n      <td>${esc(m.description)}</td>\n    </tr>`).join('');\n  return `\n  <div class=\"card\">\n    <div class=\"fn-name\">${esc(dt.name || '')} <span class=\"kind-tag\">${esc(dt.kind || '')}</span></div>\n    <p>${esc(dt.description || '')}</p>\n    ${members.length ? `\n    <div class=\"label\">Members</div>\n    <div class=\"table-wrap\"><table>\n      <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>\n      <tbody>${rows}</tbody>\n    </table></div>` : ''}\n  </div>`;\n}\n\nfunction enumBlock(en) {\n  const vals = en.values || [];\n  const rows = vals.map(v => `\n    <tr>\n      <td><code>${esc(v.name)}</code></td>\n      <td>${esc(v.value)}</td>\n      <td>${esc(v.description)}</td>\n    </tr>`).join('');\n  return `\n  <div class=\"card\">\n    <div class=\"fn-name\">${esc(en.name || '')}</div>\n    <p>${esc(en.description || '')}</p>\n    ${vals.length ? `\n    <div class=\"table-wrap\"><table>\n      <thead><tr><th>Enumerator</th><th>Value</th><th>Description</th></tr></thead>\n      <tbody>${rows}</tbody>\n    </table></div>` : ''}\n  </div>`;\n}\n\nconst fns    = doc.functions   || [];\nconst enums  = doc.enumerators || [];\nconst dts    = doc.data_types  || [];\nconst consts = doc.constants   || [];\n\nconst empty = label => `<p class=\"empty-state\">No ${label} found in this header file.</p>`;\n\nconst fnHtml    = fns.length    ? fns.map(funcBlock).join('')    : empty('functions');\nconst enumHtml  = enums.length  ? enums.map(enumBlock).join('')  : empty('enumerators');\nconst dtHtml    = dts.length    ? dts.map(dtBlock).join('')      : empty('data types');\nconst constHtml = consts.length ? `\n  <div class=\"card\">\n  <div class=\"table-wrap\"><table>\n    <thead><tr><th>Constant Name</th><th>Value</th><th>Description</th></tr></thead>\n    <tbody>${consts.map(c=>`<tr><td><code>${esc(c.name)}</code></td><td>${esc(c.value)}</td><td>${esc(c.description)}</td></tr>`).join('')}</tbody>\n  </table></div></div>` : empty('constants');\n\nconst tocItems = [\n  ['1','Overview'],['2','Functions'],['3','Enumerators'],\n  ['4','Data Types &amp; Structures'],['5','Constants'],['6','Notes']\n];\n\nconst html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>${esc(baseName)} — API Documentation</title>\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link href=\"https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,600;0,700;1,400&display=swap\" rel=\"stylesheet\">\n<style>\n  :root {\n    --navy:   #0D1C28;\n    --blue:   #1567BF;\n    --cyan:   #00B0FF;\n    --light:  #E8EFF9;\n    --muted:  #8C9BA5;\n    --text:   #1A2530;\n    --border: #CBD5E1;\n    --code-bg:#F0F7EC;\n    --code-fg:#1B5E20;\n    --white:  #FFFFFF;\n    --card-bg:#FAFCFF;\n    --sidebar-w: 240px;\n  }\n\n  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n  html { scroll-behavior: smooth; }\n\n  body {\n    font-family: 'IBM Plex Sans', sans-serif;\n    font-size: 14px;\n    line-height: 1.65;\n    color: var(--text);\n    background: #F4F7FB;\n    display: flex;\n    min-height: 100vh;\n  }\n\n  /* ── Sidebar ── */\n  .sidebar {\n    width: var(--sidebar-w);\n    min-height: 100vh;\n    background: var(--navy);\n    position: fixed;\n    top: 0; left: 0;\n    display: flex;\n    flex-direction: column;\n    z-index: 100;\n  }\n\n  .sidebar-logo {\n    padding: 24px 20px 16px;\n    border-bottom: 1px solid rgba(255,255,255,0.08);\n  }\n  .sidebar-logo .brand {\n    font-size: 17px;\n    font-weight: 700;\n    color: var(--white);\n    letter-spacing: 0.3px;\n  }\n  .sidebar-logo .brand span { color: var(--cyan); font-weight: 300; }\n  .sidebar-logo .file-name {\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 10px;\n    color: var(--muted);\n    margin-top: 4px;\n  }\n\n  .sidebar-nav {\n    padding: 16px 0;\n    flex: 1;\n    overflow-y: auto;\n  }\n  .nav-label {\n    font-size: 9px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1.2px;\n    color: var(--muted);\n    padding: 8px 20px 4px;\n  }\n  .nav-item {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    padding: 8px 20px;\n    color: rgba(255,255,255,0.65);\n    text-decoration: none;\n    font-size: 13px;\n    font-weight: 400;\n    transition: all 0.15s;\n    border-left: 3px solid transparent;\n  }\n  .nav-item:hover {\n    color: var(--white);\n    background: rgba(255,255,255,0.06);\n    border-left-color: var(--cyan);\n  }\n  .nav-item .num {\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 10px;\n    color: var(--cyan);\n    min-width: 16px;\n  }\n\n  .sidebar-footer {\n    padding: 14px 20px;\n    border-top: 1px solid rgba(255,255,255,0.08);\n    font-size: 10px;\n    color: var(--muted);\n  }\n\n  /* ── Main content ── */\n  .main {\n    margin-left: var(--sidebar-w);\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n  }\n\n  /* ── Top bar ── */\n  .topbar {\n    background: var(--white);\n    border-bottom: 1px solid var(--border);\n    padding: 14px 40px;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    position: sticky;\n    top: 0;\n    z-index: 50;\n  }\n  .topbar-title {\n    font-size: 15px;\n    font-weight: 600;\n    color: var(--navy);\n  }\n  .topbar-meta {\n    font-size: 11px;\n    color: var(--muted);\n    font-family: 'IBM Plex Mono', monospace;\n  }\n  .badge {\n    display: inline-flex;\n    align-items: center;\n    gap: 5px;\n    background: var(--light);\n    color: var(--blue);\n    font-size: 10px;\n    font-weight: 600;\n    padding: 3px 8px;\n    border-radius: 4px;\n    letter-spacing: 0.3px;\n  }\n\n  /* ── Cover hero ── */\n  .hero {\n    background: linear-gradient(135deg, var(--navy) 0%, #152E45 60%, #1B3A52 100%);\n    padding: 56px 40px 48px;\n    color: var(--white);\n    position: relative;\n    overflow: hidden;\n  }\n  .hero::before {\n    content: '';\n    position: absolute;\n    top: -40px; right: -40px;\n    width: 280px; height: 280px;\n    border-radius: 50%;\n    background: radial-gradient(circle, rgba(0,176,255,0.12) 0%, transparent 70%);\n    pointer-events: none;\n  }\n  .hero-eyebrow {\n    font-size: 10px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 2px;\n    color: var(--cyan);\n    margin-bottom: 12px;\n  }\n  .hero-title {\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 32px;\n    font-weight: 600;\n    letter-spacing: -0.5px;\n    margin-bottom: 8px;\n  }\n  .hero-subtitle {\n    font-size: 15px;\n    color: rgba(255,255,255,0.55);\n    font-weight: 300;\n    margin-bottom: 28px;\n  }\n  .hero-meta {\n    display: flex;\n    gap: 24px;\n    flex-wrap: wrap;\n  }\n  .hero-meta-item {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n  }\n  .hero-meta-item .meta-key {\n    font-size: 9px;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    color: var(--muted);\n  }\n  .hero-meta-item .meta-val {\n    font-size: 12px;\n    font-family: 'IBM Plex Mono', monospace;\n    color: rgba(255,255,255,0.8);\n  }\n  .hero-accent {\n    height: 2px;\n    background: linear-gradient(90deg, var(--cyan), transparent);\n    margin-bottom: 28px;\n    width: 80px;\n  }\n\n  /* ── Content area ── */\n  .content {\n    padding: 0 40px 60px;\n    max-width: 900px;\n  }\n\n  /* ── Section ── */\n  .section {\n    margin-top: 48px;\n  }\n  .section-header {\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    margin-bottom: 20px;\n    padding-bottom: 12px;\n    border-bottom: 2px solid var(--border);\n  }\n  .section-num {\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 11px;\n    font-weight: 600;\n    color: var(--cyan);\n    background: var(--navy);\n    padding: 3px 8px;\n    border-radius: 4px;\n  }\n  .section-title {\n    font-size: 18px;\n    font-weight: 700;\n    color: var(--navy);\n    letter-spacing: -0.3px;\n  }\n\n  /* ── Card ── */\n  .card {\n    background: var(--white);\n    border: 1px solid var(--border);\n    border-radius: 8px;\n    padding: 22px 24px;\n    margin-bottom: 14px;\n    box-shadow: 0 1px 3px rgba(0,0,0,0.05);\n  }\n\n  /* ── Function name ── */\n  .fn-name {\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 15px;\n    font-weight: 600;\n    color: var(--blue);\n    margin-bottom: 6px;\n  }\n  .fn-desc {\n    color: #4A5568;\n    margin-bottom: 14px;\n    font-size: 13.5px;\n  }\n  .kind-tag {\n    font-size: 11px;\n    font-weight: 400;\n    font-family: 'IBM Plex Sans', sans-serif;\n    color: var(--muted);\n    background: var(--light);\n    padding: 1px 7px;\n    border-radius: 3px;\n    vertical-align: middle;\n    margin-left: 6px;\n  }\n\n  /* ── Label ── */\n  .label {\n    font-size: 9px;\n    font-weight: 700;\n    text-transform: uppercase;\n    letter-spacing: 1.2px;\n    color: var(--blue);\n    margin: 14px 0 6px;\n  }\n\n  /* ── Code ── */\n  pre {\n    background: var(--code-bg);\n    border-left: 3px solid var(--cyan);\n    border-radius: 0 6px 6px 0;\n    padding: 12px 16px;\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 12px;\n    color: var(--code-fg);\n    white-space: pre-wrap;\n    word-break: break-all;\n    margin-bottom: 6px;\n    overflow-x: auto;\n  }\n  code {\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 12px;\n    background: var(--code-bg);\n    color: var(--code-fg);\n    padding: 1px 5px;\n    border-radius: 3px;\n  }\n  pre code { background: none; padding: 0; }\n\n  /* ── Returns ── */\n  .returns {\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 12px;\n    color: #555;\n  }\n\n  /* ── Tables ── */\n  .table-wrap { overflow-x: auto; }\n  table {\n    width: 100%;\n    border-collapse: collapse;\n    font-size: 13px;\n  }\n  thead tr {\n    background: var(--navy);\n    color: var(--white);\n  }\n  thead th {\n    padding: 9px 12px;\n    text-align: left;\n    font-weight: 600;\n    font-size: 11px;\n    letter-spacing: 0.3px;\n    white-space: nowrap;\n  }\n  thead th:first-child { border-radius: 6px 0 0 0; }\n  thead th:last-child  { border-radius: 0 6px 0 0; }\n  tbody tr:nth-child(odd)  { background: var(--white); }\n  tbody tr:nth-child(even) { background: var(--light); }\n  tbody td {\n    padding: 8px 12px;\n    border-bottom: 1px solid var(--border);\n    vertical-align: top;\n    font-size: 12.5px;\n    color: var(--text);\n  }\n\n  /* ── Empty state ── */\n  .empty-state {\n    color: var(--muted);\n    font-style: italic;\n    font-size: 13px;\n    padding: 20px 0 4px;\n  }\n\n  /* ── Notes footer ── */\n  .notes-footer {\n    margin-top: 24px;\n    padding-top: 14px;\n    border-top: 1px solid var(--cyan);\n    font-size: 11px;\n    color: var(--muted);\n    font-style: italic;\n    text-align: center;\n  }\n\n  /* ── Scrollbar ── */\n  ::-webkit-scrollbar { width: 6px; height: 6px; }\n  ::-webkit-scrollbar-track { background: transparent; }\n  ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }\n</style>\n</head>\n<body>\n\n<!-- SIDEBAR -->\n<aside class=\"sidebar\">\n  <div class=\"sidebar-logo\">\n    <div class=\"brand\">XYZ <span>Technologies</span></div>\n    <div class=\"file-name\">${esc(fileName)}</div>\n  </div>\n  <nav class=\"sidebar-nav\">\n    <div class=\"nav-label\">Contents</div>\n    ${tocItems.map(([n,t]) => `\n    <a class=\"nav-item\" href=\"#sec${n}\">\n      <span class=\"num\">${n}</span>${t}\n    </a>`).join('')}\n  </nav>\n  <div class=\"sidebar-footer\">\n    v1.0 &nbsp;·&nbsp; Internal<br>Generated: ${today}\n  </div>\n</aside>\n\n<!-- MAIN -->\n<div class=\"main\">\n\n  <!-- TOP BAR -->\n  <div class=\"topbar\">\n    <div class=\"topbar-title\">API Documentation &mdash; <span style=\"font-family:'IBM Plex Mono',monospace;font-weight:400\">${esc(fileName)}</span></div>\n    <div style=\"display:flex;gap:8px;align-items:center\">\n      <span class=\"badge\">C Embedded</span>\n      <span class=\"topbar-meta\">${today}</span>\n    </div>\n  </div>\n\n  <!-- HERO -->\n  <div class=\"hero\">\n    <div class=\"hero-eyebrow\">XYZ Technologies &nbsp;·&nbsp; Header File Documentation</div>\n    <div class=\"hero-title\">${esc(baseName.toUpperCase())}</div>\n    <div class=\"hero-accent\"></div>\n    <div class=\"hero-subtitle\">C Embedded Systems Interface Reference</div>\n    <div class=\"hero-meta\">\n      <div class=\"hero-meta-item\"><span class=\"meta-key\">Source</span><span class=\"meta-val\">${esc(fileName)}</span></div>\n      <div class=\"hero-meta-item\"><span class=\"meta-key\">Prepared by</span><span class=\"meta-val\">XYZ Technologies</span></div>\n      <div class=\"hero-meta-item\"><span class=\"meta-key\">Date</span><span class=\"meta-val\">${today}</span></div>\n      <div class=\"hero-meta-item\"><span class=\"meta-key\">Version</span><span class=\"meta-val\">1.0</span></div>\n      <div class=\"hero-meta-item\"><span class=\"meta-key\">Classification</span><span class=\"meta-val\">Internal</span></div>\n    </div>\n  </div>\n\n  <!-- CONTENT -->\n  <div class=\"content\">\n\n    <!-- 1. OVERVIEW -->\n    <div class=\"section\" id=\"sec1\">\n      <div class=\"section-header\">\n        <span class=\"section-num\">01</span>\n        <span class=\"section-title\">Overview</span>\n      </div>\n      <div class=\"card\">\n        <p>${esc(doc.overview || 'No overview available.')}</p>\n      </div>\n    </div>\n\n    <!-- 2. FUNCTIONS -->\n    <div class=\"section\" id=\"sec2\">\n      <div class=\"section-header\">\n        <span class=\"section-num\">02</span>\n        <span class=\"section-title\">Functions</span>\n      </div>\n      ${fnHtml}\n    </div>\n\n    <!-- 3. ENUMERATORS -->\n    <div class=\"section\" id=\"sec3\">\n      <div class=\"section-header\">\n        <span class=\"section-num\">03</span>\n        <span class=\"section-title\">Enumerators</span>\n      </div>\n      ${enumHtml}\n    </div>\n\n    <!-- 4. DATA TYPES & STRUCTURES -->\n    <div class=\"section\" id=\"sec4\">\n      <div class=\"section-header\">\n        <span class=\"section-num\">04</span>\n        <span class=\"section-title\">Data Types &amp; Structures</span>\n      </div>\n      ${dtHtml}\n    </div>\n\n    <!-- 5. CONSTANTS -->\n    <div class=\"section\" id=\"sec5\">\n      <div class=\"section-header\">\n        <span class=\"section-num\">05</span>\n        <span class=\"section-title\">Constants</span>\n      </div>\n      ${constHtml}\n    </div>\n\n    <!-- 6. NOTES -->\n    <div class=\"section\" id=\"sec6\">\n      <div class=\"section-header\">\n        <span class=\"section-num\">06</span>\n        <span class=\"section-title\">Notes</span>\n      </div>\n      <div class=\"card\">\n        <p>${esc(doc.notes || 'No additional notes.')}</p>\n        <div class=\"notes-footer\">This document was automatically generated by XYZ Technologies.</div>\n      </div>\n    </div>\n\n  </div><!-- /content -->\n</div><!-- /main -->\n\n</body>\n</html>`;\n\n// Convert HTML string to base64 binary for Google Drive upload\nconst b64 = Buffer.from(html, 'utf8').toString('base64');\n\nreturn [{\n  json: {\n    htmlName,\n    fileName,\n    timestamp: today\n  },\n  binary: {\n    data: {\n      data: b64,\n      mimeType: 'text/html',\n      fileName: htmlName,\n      fileExtension: 'html'\n    }\n  }\n}];\n"},"typeVersion":2},{"id":"0b63f327-e409-4fe9-8716-bf4d53697441","name":"Save PDF to Google Drive","type":"n8n-nodes-base.googleDrive","position":[928,544],"parameters":{"name":"={{ $json.pdfName }}","driveId":{"__rl":true,"mode":"list","value":"MyDrive"},"options":{},"folderId":{"__rl":true,"mode":"id","value":"YOUR_OUTPUT_FOLDER_ID"}},"typeVersion":3}],"active":false,"pinData":{},"settings":{"binaryMode":"separate","availableInMCP":false,"executionOrder":"v1"},"connections":{"Download file":{"main":[[{"node":"Extract from File2","type":"main","index":0}]]},"Generate HTML":{"main":[[{"node":"Save PDF to Google Drive","type":"main","index":0}]]},"Build email body":{"main":[[{"node":"Send completion email","type":"main","index":0}]]},"Extract from File2":{"main":[[{"node":"Extract structured docs (GPT-4o)","type":"main","index":0}]]},"Parse JSON response":{"main":[[{"node":"Generate HTML","type":"main","index":0}]]},"Filter .h files only":{"main":[[{"node":"Process one file at a time","type":"main","index":0}]]},"Save PDF to Google Drive":{"main":[[{"node":"Process one file at a time","type":"main","index":0}]]},"Get all files from folder":{"main":[[{"node":"Filter .h files only","type":"main","index":0}]]},"Process one file at a time":{"main":[[{"node":"Build email body","type":"main","index":0}],[{"node":"Download file","type":"main","index":0}]]},"Extract structured docs (GPT-4o)":{"main":[[{"node":"Parse JSON response","type":"main","index":0}]]},"When clicking ‘Execute workflow’":{"main":[[{"node":"Get all files from folder","type":"main","index":0}]]}}},"lastUpdatedBy":1,"workflowInfo":{"nodeCount":21,"nodeTypes":{"n8n-nodes-base.code":{"count":4},"n8n-nodes-base.gmail":{"count":1},"n8n-nodes-base.stickyNote":{"count":9},"n8n-nodes-base.googleDrive":{"count":3},"n8n-nodes-base.manualTrigger":{"count":1},"n8n-nodes-base.splitInBatches":{"count":1},"n8n-nodes-base.extractFromFile":{"count":1},"@n8n/n8n-nodes-langchain.openAi":{"count":1}}},"status":"published","readyToDemo":null,"user":{"name":"Tejasv Makkar","username":"tmakkar","bio":"https://www.linkedin.com/in/tejasv-makkar/","verified":true,"links":[""],"avatar":"https://gravatar.com/avatar/0f31fdbbb21077b8977fa34ab0f71b60970572462574cede8ff1c0b8b4938003?r=pg&d=retro&size=200"},"nodes":[{"id":39,"icon":"fa:sync","name":"n8n-nodes-base.splitInBatches","codex":{"data":{"alias":["Loop","Concatenate","Batch","Split","Split In Batches"],"resources":{"generic":[{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.splitinbatches/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow"]}}},"group":"[\"organization\"]","defaults":{"name":"Loop Over Items","color":"#007755"},"iconData":{"icon":"sync","type":"icon"},"displayName":"Loop Over Items (Split in Batches)","typeVersion":3,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":58,"icon":"file:googleDrive.svg","name":"n8n-nodes-base.googleDrive","codex":{"data":{"resources":{"generic":[{"url":"https://n8n.io/blog/your-business-doesnt-need-you-to-operate/","icon":" 🖥️","label":"Hey founders! Your business doesn't need you to operate"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googledrive/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"}]},"categories":["Data & Storage"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"input\"]","defaults":{"name":"Google Drive"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiB2aWV3Qm94PSIwIDAgODEgNzMiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSI+PHBhdGggZmlsbD0iIzAwNjZkYSIgZD0ibTYuMDQ4IDYxLjI2IDMuNTI4IDYuMDk0Yy43MzMgMS4yODMgMS43ODcgMi4yOTEgMy4wMjQgMy4wMjRsMTIuNi0yMS44MUgwYTguMyA4LjMgMCAwIDAgMS4xIDQuMTI0eiIvPjxwYXRoIGZpbGw9IiMwMGFjNDciIGQ9Ik00MCAyMi45MSAyNy40IDEuMWMtMS4yMzcuNzMzLTIuMjkxIDEuNzQxLTMuMDI0IDMuMDI0TDEuMSA0NC40NDVBOC4zIDguMyAwIDAgMCAwIDQ4LjU2OGgyNS4yeiIvPjxwYXRoIGZpbGw9IiNlYTQzMzUiIGQ9Ik02Ny40IDcwLjM3OGMxLjIzNy0uNzMzIDIuMjkxLTEuNzQxIDMuMDI0LTMuMDI0bDEuNDY2LTIuNTIgNy4wMS0xMi4xNDJhOC4zIDguMyAwIDAgMCAxLjEtNC4xMjRINTQuNzk4bDUuMzYzIDEwLjUzOHoiLz48cGF0aCBmaWxsPSIjMDA4MzJkIiBkPSJNNDAgMjIuOTEgNTIuNiAxLjFDNTEuMzYzLjM2NyA0OS45NDMgMCA0OC40NzcgMEgzMS41MjRjLTEuNDY2IDAtMi44ODcuNDEyLTQuMTI0IDEuMXoiLz48cGF0aCBmaWxsPSIjMjY4NGZjIiBkPSJNNTQuNzk5IDQ4LjU2OEgyNS4ybC0xMi42IDIxLjgxYzEuMjM3LjczMyAyLjY1NyAxLjEgNC4xMjQgMS4xaDQ2LjU1MmMxLjQ2NiAwIDIuODg3LS40MTIgNC4xMjQtMS4xeiIvPjxwYXRoIGZpbGw9IiNmZmJhMDAiIGQ9Ik02Ny4yNjIgMjQuMjg0IDU1LjYyNCA0LjEyNEM1NC44OTEgMi44NDEgNTMuODM3IDEuODMzIDUyLjYgMS4xTDQwIDIyLjkxbDE0LjggMjUuNjU5aDI1LjE1NWE4LjMgOC4zIDAgMCAwLTEuMS00LjEyNHoiLz48L2c+PC9zeW1ib2w+PC9zdmc+"},"displayName":"Google Drive","typeVersion":3,"nodeCategories":[{"id":3,"name":"Data & Storage"}]},{"id":356,"icon":"file:gmail.svg","name":"n8n-nodes-base.gmail","codex":{"data":{"alias":["email","human","form","wait","hitl","approval"],"resources":{"generic":[{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/","icon":"🎫","label":"Supercharging your conference registration process with n8n"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/your-business-doesnt-need-you-to-operate/","icon":" 🖥️","label":"Hey founders! Your business doesn't need you to operate"},{"url":"https://n8n.io/blog/using-automation-to-boost-productivity-in-the-workplace/","icon":"💪","label":"Using Automation to Boost Productivity in the Workplace"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.gmail/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"}]},"categories":["Communication","HITL"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"HITL":["Human in the Loop"]}}},"group":"[\"transform\"]","defaults":{"name":"Gmail"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTYiIGhlaWdodD0iMTkzIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZmlsbD0iIzQyODVGNCIgZD0iTTU4LjE4MiAxOTIuMDVWOTMuMTRMMjcuNTA3IDY1LjA3NyAwIDQ5LjUwNHYxMjUuMDkxYzAgOS42NTggNy44MjUgMTcuNDU1IDE3LjQ1NSAxNy40NTV6Ii8+PHBhdGggZmlsbD0iIzM0QTg1MyIgZD0iTTE5Ny44MTggMTkyLjA1aDQwLjcyN2M5LjY1OSAwIDE3LjQ1NS03LjgyNiAxNy40NTUtMTcuNDU1VjQ5LjUwNWwtMzEuMTU2IDE3LjgzNy0yNy4wMjYgMjUuNzk4eiIvPjxwYXRoIGZpbGw9IiNFQTQzMzUiIGQ9Im01OC4xODIgOTMuMTQtNC4xNzQtMzguNjQ3IDQuMTc0LTM2Ljk4OUwxMjggNjkuODY4bDY5LjgxOC01Mi4zNjQgNC42NyAzNC45OTItNC42NyA0MC42NDRMMTI4IDE0NS41MDR6Ii8+PHBhdGggZmlsbD0iI0ZCQkMwNCIgZD0iTTE5Ny44MTggMTcuNTA0VjkzLjE0TDI1NiA0OS41MDRWMjYuMjMxYzAtMjEuNTg1LTI0LjY0LTMzLjg5LTQxLjg5LTIwLjk0NXoiLz48cGF0aCBmaWxsPSIjQzUyMjFGIiBkPSJtMCA0OS41MDQgMjYuNzU5IDIwLjA3TDU4LjE4MiA5My4xNFYxNy41MDRMNDEuODkgNS4yODZDMjQuNjEtNy42NiAwIDQuNjQ2IDAgMjYuMjN6Ii8+PC9zdmc+"},"displayName":"Gmail","typeVersion":2,"nodeCategories":[{"id":6,"name":"Communication"},{"id":28,"name":"HITL"}]},{"id":565,"icon":"fa:sticky-note","name":"n8n-nodes-base.stickyNote","codex":{"data":{"alias":["Comments","Notes","Sticky"],"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"input\"]","defaults":{"name":"Sticky Note","color":"#FFD233"},"iconData":{"icon":"sticky-note","type":"icon"},"displayName":"Sticky Note","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":834,"icon":"file:code.svg","name":"n8n-nodes-base.code","codex":{"data":{"alias":["cpde","Javascript","JS","Python","Script","Custom Code","Function"],"details":"The Code node allows you to execute JavaScript in your workflow.","resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Code"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTcxXzQ0MSkiPgo8cGF0aCBkPSJNMTcwLjI4MyA0OEgxOTYuNUMyMDMuMTI3IDQ4IDIwOC41IDQyLjYyNzQgMjA4LjUgMzZWMTJDMjA4LjUgNS4zNzI1OCAyMDMuMTI3IDAgMTk2LjUgMEgxNzAuMjgzQzEyNi4xIDAgOTAuMjgzIDM1LjgxNzIgOTAuMjgzIDgwVjE3NkM5MC4yODMgMjA2LjkyOCA2NS4yMTA5IDIzMiAzNC4yODMgMjMySDIzQzE2LjM3MjYgMjMyIDExIDIzNy4zNzIgMTEgMjQ0VjI2OEMxMSAyNzQuNjI3IDE2LjM3MjQgMjgwIDIyLjk5OTYgMjgwTDM0LjI4MyAyODBDNjUuMjEwOSAyODAgOTAuMjgzIDMwNS4wNzIgOTAuMjgzIDMzNlY0NDBDOTAuMjgzIDQ3OS43NjQgMTIyLjUxOCA1MTIgMTYyLjI4MyA1MTJIMTk2LjVDMjAzLjEyNyA1MTIgMjA4LjUgNTA2LjYyNyAyMDguNSA1MDBWNDc2QzIwOC41IDQ2OS4zNzMgMjAzLjEyNyA0NjQgMTk2LjUgNDY0SDE2Mi4yODNDMTQ5LjAyOCA0NjQgMTM4LjI4MyA0NTMuMjU1IDEzOC4yODMgNDQwVjMzNkMxMzguMjgzIDMwOS4wMjIgMTI4LjAxMSAyODQuNDQzIDExMS4xNjQgMjY1Ljk2MUMxMDYuMTA5IDI2MC40MTYgMTA2LjEwOSAyNTEuNTg0IDExMS4xNjQgMjQ2LjAzOUMxMjguMDExIDIyNy41NTcgMTM4LjI4MyAyMDIuOTc4IDEzOC4yODMgMTc2VjgwQzEzOC4yODMgNjIuMzI2OSAxNTIuNjEgNDggMTcwLjI4MyA0OFoiIGZpbGw9IiNGRjk5MjIiLz4KPHBhdGggZD0iTTMwNSAzNkMzMDUgNDIuNjI3NCAzMTAuMzczIDQ4IDMxNyA0OEgzNDIuOTc5QzM2MC42NTIgNDggMzc0Ljk3OCA2Mi4zMjY5IDM3NC45NzggODBWMTc2QzM3NC45NzggMjAyLjk3OCAzODUuMjUxIDIyNy41NTcgNDAyLjA5OCAyNDYuMDM5QzQwNy4xNTMgMjUxLjU4NCA0MDcuMTUzIDI2MC40MTYgNDAyLjA5OCAyNjUuOTYxQzM4NS4yNTEgMjg0LjQ0MyAzNzQuOTc4IDMwOS4wMjIgMzc0Ljk3OCAzMzZWNDMyQzM3NC45NzggNDQ5LjY3MyAzNjAuNjUyIDQ2NCAzNDIuOTc5IDQ2NEgzMTdDMzEwLjM3MyA0NjQgMzA1IDQ2OS4zNzMgMzA1IDQ3NlY1MDBDMzA1IDUwNi42MjcgMzEwLjM3MyA1MTIgMzE3IDUxMkgzNDIuOTc5QzM4Ny4xNjEgNTEyIDQyMi45NzggNDc2LjE4MyA0MjIuOTc4IDQzMlYzMzZDNDIyLjk3OCAzMDUuMDcyIDQ0OC4wNTEgMjgwIDQ3OC45NzkgMjgwSDQ5MEM0OTYuNjI3IDI4MCA1MDIgMjc0LjYyOCA1MDIgMjY4VjI0NEM1MDIgMjM3LjM3MyA0OTYuNjI4IDIzMiA0OTAgMjMyTDQ3OC45NzkgMjMyQzQ0OC4wNTEgMjMyIDQyMi45NzggMjA2LjkyOCA0MjIuOTc4IDE3NlY4MEM0MjIuOTc4IDM1LjgxNzIgMzg3LjE2MSAwIDM0Mi45NzkgMEgzMTdDMzEwLjM3MyAwIDMwNSA1LjM3MjU4IDMwNSAxMlYzNloiIGZpbGw9IiNGRjk5MjIiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTcxXzQ0MSI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Code","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":838,"icon":"fa:mouse-pointer","name":"n8n-nodes-base.manualTrigger","codex":{"data":{"resources":{"generic":[],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.manualworkflowtrigger/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"trigger\"]","defaults":{"name":"When clicking ‘Execute workflow’","color":"#909298"},"iconData":{"icon":"mouse-pointer","type":"icon"},"displayName":"Manual Trigger","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":1235,"icon":"file:extractFromFile.svg","name":"n8n-nodes-base.extractFromFile","codex":{"data":{"alias":["CSV","Spreadsheet","Excel","xls","xlsx","ods","tabular","decode","decoding","Move Binary Data","Binary","File","PDF","JSON","HTML","ICS","iCal","txt","Text","RTF","XML","64","Base64","Convert"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.extractfromfile/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Files","Data Transformation"]}}},"group":"[\"input\"]","defaults":{"name":"Extract from File"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAuOTM3NSAyQzAuNDE5NzMzIDIgMCAyLjQxOTczIDAgMi45Mzc1VjM3LjMyMjFDMCAzNy44Mzk5IDAuNDE5NzMzIDM4LjI1OTYgMC45Mzc1IDM4LjI1OTZIMjYuMjE1NEMyNi43MzMyIDM4LjI1OTYgMjcuMTUyOSAzNy44Mzk5IDI3LjE1MjkgMzcuMzIyMUwyNy4xNTI5IDMwLjY3MTlMMTYuNzk2OSAzMC42NzE5QzE0Ljg5ODQgMzAuNjcxOSAxMy4zNTk0IDI5LjEzMjkgMTMuMzU5NCAyNy4yMzQ0VjI1LjM1OTRDMTMuMzU5NCAyMy40NjA5IDE0Ljg5ODQgMjEuOTIxOSAxNi43OTY5IDIxLjkyMTlIMjcuMTUyOUwyNy4xNTI5IDE1Ljc4MjFIMTQuMzA4M0MxMy43OTA2IDE1Ljc4MjEgMTMuMzcwOCAxNS4zNjI0IDEzLjM3MDggMTQuODQ0NlYySDAuOTM3NVoiIGZpbGw9IiMzNTNGNkUiLz4KPHBhdGggZD0iTTE2LjAyNzEgMkMxNS45NDA4IDIgMTUuODcwOCAyLjA2OTk2IDE1Ljg3MDggMi4xNTYyNVYxMi44MTM0QzE1Ljg3MDggMTMuMDcyMyAxNi4wODA3IDEzLjI4MjEgMTYuMzM5NiAxMy4yODIxSDI2Ljk5NjdDMjcuMDgzIDEzLjI4MjEgMjcuMTUyOSAxMy4yMTIyIDI3LjE1MjkgMTMuMTI1OUwyNy4xNTI5IDEyLjYxNzFDMjcuMTUyOSAxMi4zNjg4IDI3LjA1NDUgMTIuMTMwNyAyNi44NzkxIDExLjk1NUwxNy4yMjI1IDIuMjc1MzhDMTcuMDQ2NiAyLjA5OTA4IDE2LjgwNzkgMiAxNi41NTg4IDJIMTYuMDI3MVoiIGZpbGw9IiMzNTNGNkUiLz4KPHBhdGggZD0iTTI5Ljc2NDIgMzQuNjUwM0MyOS4wMzQgMzMuOTE2IDI5LjAzNzQgMzIuNzI4OCAyOS43NzE2IDMxLjk5ODZMMzMuNjE5NyAyOC4xNzE5TDE2Ljc5NjkgMjguMTcxOUMxNi4yNzkxIDI4LjE3MTkgMTUuODU5NCAyNy43NTIxIDE1Ljg1OTQgMjcuMjM0NFYyNS4zNTk0QzE1Ljg1OTQgMjQuODQxNiAxNi4yNzkxIDI0LjQyMTkgMTYuNzk2OSAyNC40MjE5TDMzLjU0MTIgMjQuNDIxOUwyOS43NzE2IDIwLjY3MzNDMjkuMDM3NCAxOS45NDMxIDI5LjAzNCAxOC43NTU5IDI5Ljc2NDIgMTguMDIxNkMzMC40OTQ0IDE3LjI4NzQgMzEuNjgxNiAxNy4yODQgMzIuNDE1OSAxOC4wMTQyTDM5LjQ0NzEgMjUuMDA2NEMzOS44MDEgMjUuMzU4MyA0MCAyNS44MzY4IDQwIDI2LjMzNTlDNDAgMjYuODM1IDM5LjgwMSAyNy4zMTM1IDM5LjQ0NzEgMjcuNjY1NUwzMi40MTU5IDM0LjY1NzZDMzEuNjgxNiAzNS4zODc4IDMwLjQ5NDQgMzUuMzg0NSAyOS43NjQyIDM0LjY1MDNaIiBmaWxsPSIjMzUzRjZFIi8+Cjwvc3ZnPgo="},"displayName":"Extract from File","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":1250,"icon":"file:openAi.svg","name":"@n8n/n8n-nodes-langchain.openAi","codex":{"data":{"alias":["LangChain","ChatGPT","Sora","DallE","whisper","audio","transcribe","tts","assistant"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-langchain.openai/"}]},"categories":["AI","Langchain"],"subcategories":{"AI":["Agents","Miscellaneous","Root Nodes"]}}},"group":"[\"transform\"]","defaults":{"name":"OpenAI"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTM2Ljg2NzEgMTYuMzcxOEMzNy43NzQ2IDEzLjY0OCAzNy40NjIxIDEwLjY2NDIgMzYuMDEwOCA4LjE4NjYxQzMzLjgyODIgNC4zODY1MyAyOS40NDA3IDIuNDMxNDkgMjUuMTU1NiAzLjM1MTUxQzIzLjI0OTMgMS4yMDM5NiAyMC41MTA1IC0wLjAxNzMxNDggMTcuNjM5MiAwLjAwMDE4NTUzM0MxMy4yNTkxIC0wLjAwOTgxNDY4IDkuMzcyNzMgMi44MTAyNSA4LjAyNTIgNi45Nzc4M0M1LjIxMTM5IDcuNTU0MSAyLjc4MjU4IDkuMzE1MzggMS4zNjEzIDExLjgxMTdDLTAuODM3NDkzIDE1LjYwMTggLTAuMzM2MjMyIDIwLjM3OTQgMi42MDEzMyAyMy42Mjk0QzEuNjkzODEgMjYuMzUzMiAyLjAwNjMyIDI5LjMzNzEgMy40NTc2IDMxLjgxNDZDNS42NDAxNSAzNS42MTQ3IDEwLjAyNzcgMzcuNTY5NyAxNC4zMTI4IDM2LjY0OTdDMTYuMjE3OSAzOC43OTczIDE4Ljk1NzkgNDAuMDE4NSAyMS44MjkyIDM5Ljk5OThDMjYuMjExOCA0MC4wMTEgMzAuMDk5NCAzNy4xODg1IDMxLjQ0NjkgMzMuMDE3MUMzNC4yNjA4IDMyLjQ0MDkgMzYuNjg5NiAzMC42Nzk2IDM4LjExMDggMjguMTgzM0M0MC4zMDcxIDI0LjM5MzIgMzkuODA0NiAxOS42MTk0IDM2Ljg2ODMgMTYuMzY5M0wzNi44NjcxIDE2LjM3MThaTTIxLjgzMTcgMzcuMzg2QzIwLjA3OCAzNy4zODg1IDE4LjM3OTIgMzYuNzc0NyAxNy4wMzI5IDM1LjY1MDlDMTcuMDk0MSAzNS42MTg1IDE3LjIwMDQgMzUuNTU5NyAxNy4yNjkxIDM1LjUxNzJMMjUuMjM0MyAzMC45MTcxQzI1LjY0MTggMzAuNjg1OCAyNS44OTE4IDMwLjI1MjEgMjUuODg5MyAyOS43ODMzVjE4LjU1NDNMMjkuMjU1NiAyMC40OTgxQzI5LjI5MTkgMjAuNTE1NiAyOS4zMTU3IDIwLjU1MDYgMjkuMzIwNyAyMC41OTA2VjI5Ljg4OTZDMjkuMzE1NyAzNC4wMjQ3IDI1Ljk2NjggMzcuMzc3MiAyMS44MzE3IDM3LjM4NlpNNS43MjY0IDMwLjUwNzFDNC44NDc2MyAyOC45ODk2IDQuNTMxMzcgMjcuMjEwOCA0LjgzMjYzIDI1LjQ4NDVDNC44OTEzOCAyNS41MTk1IDQuOTk1MTMgMjUuNTgzMiA1LjA2ODg4IDI1LjYyNTdMMTMuMDM0MSAzMC4yMjU4QzEzLjQzNzggMzAuNDYyMSAxMy45Mzc4IDMwLjQ2MjEgMTQuMzQyOCAzMC4yMjU4TDI0LjA2NjggMjQuNjEwN1YyOC40OTgzQzI0LjA2OTMgMjguNTM4MyAyNC4wNTA1IDI4LjU3NyAyNC4wMTkzIDI4LjYwMkwxNS45Njc5IDMzLjI1MDlDMTIuMzgxNSAzNS4zMTU5IDcuODAxNDQgMzQuMDg4NCA1LjcyNzY1IDMwLjUwNzFINS43MjY0Wk0zLjYzMDEgMTMuMTIwNUM0LjUwNTEyIDExLjYwMDQgNS44ODY0IDEwLjQzNzkgNy41MzE0NCA5LjgzNDE1QzcuNTMxNDQgOS45MDI5IDcuNTI3NjkgMTAuMDI0MSA3LjUyNzY5IDEwLjEwOTJWMTkuMzEwNkM3LjUyNTE5IDE5Ljc3ODEgNy43NzUxOSAyMC4yMTE5IDguMTgxNDUgMjAuNDQzMUwxNy45MDU0IDI2LjA1N0wxNC41MzkxIDI4LjAwMDhDMTQuNTA1MyAyOC4wMjMzIDE0LjQ2MjggMjguMDI3IDE0LjQyNTMgMjguMDEwOEw2LjM3MjY2IDIzLjM1ODJDMi43OTM4MyAyMS4yODU2IDEuNTY2MzEgMTYuNzA2OCAzLjYyODg1IDEzLjEyMTdMMy42MzAxIDEzLjEyMDVaTTMxLjI4ODIgMTkuNTU2OUwyMS41NjQyIDEzLjk0MTdMMjQuOTMwNiAxMS45OTkyQzI0Ljk2NDMgMTEuOTc2NyAyNS4wMDY4IDExLjk3MjkgMjUuMDQ0MyAxMS45ODkyTDMzLjA5NyAxNi42MzhDMzYuNjgyMSAxOC43MDkzIDM3LjkxMDggMjMuMjk1NyAzNS44Mzk1IDI2Ljg4MDhDMzQuOTYzMyAyOC4zOTgzIDMzLjU4MzIgMjkuNTYwOCAzMS45Mzk1IDMwLjE2NThWMjAuNjg5NEMzMS45NDMyIDIwLjIyMTkgMzEuNjk0NSAxOS43ODk0IDMxLjI4OTQgMTkuNTU2OUgzMS4yODgyWk0zNC42MzgzIDE0LjUxNDJDMzQuNTc5NSAxNC40NzggMzQuNDc1OCAxNC40MTU1IDM0LjQwMiAxNC4zNzNMMjYuNDM2OCA5Ljc3Mjg5QzI2LjAzMzEgOS41MzY2NCAyNS41MzMxIDkuNTM2NjQgMjUuMTI4MSA5Ljc3Mjg5TDE1LjQwNDEgMTUuMzg4VjExLjUwMDRDMTUuNDAxNiAxMS40NjA0IDE1LjQyMDQgMTEuNDIxNyAxNS40NTE2IDExLjM5NjdMMjMuNTAzIDYuNzUxNThDMjcuMDg5NCA0LjY4Mjc5IDMxLjY3NDUgNS45MTQwNiAzMy43NDIgOS41MDE2NEMzNC42MTU4IDExLjAxNjcgMzQuOTMyIDEyLjc5MDUgMzQuNjM1OCAxNC41MTQySDM0LjYzODNaTTEzLjU3NDEgMjEuNDQzMUwxMC4yMDY1IDE5LjQ5OTRDMTAuMTcwMiAxOS40ODE5IDEwLjE0NjUgMTkuNDQ2OCAxMC4xNDE1IDE5LjQwNjhWMTAuMTA3OUMxMC4xNDQgNS45Njc4MSAxMy41MDI4IDIuNjEyNzQgMTcuNjQyOSAyLjYxNTI0QzE5LjM5NDIgMi42MTUyNCAyMS4wODkyIDMuMjMwMjUgMjIuNDM1NSA0LjM1MDI4QzIyLjM3NDMgNC4zODI3OCAyMi4yNjkzIDQuNDQxNTMgMjIuMTk5MiA0LjQ4NDAzTDE0LjIzNDEgOS4wODQxM0MxMy44MjY2IDkuMzE1MzggMTMuNTc2NiA5Ljc0Nzg5IDEzLjU3OTEgMTAuMjE2N0wxMy41NzQxIDIxLjQ0MDZWMjEuNDQzMVpNMTUuNDAyOSAxNy41MDA2TDE5LjczNDIgMTQuOTk5M0wyNC4wNjU1IDE3LjQ5OTNWMjIuNTAwN0wxOS43MzQyIDI1LjAwMDdMMTUuNDAyOSAyMi41MDA3VjE3LjUwMDZaIiBmaWxsPSJibGFjayIvPgo8L3N2Zz4K"},"displayName":"OpenAI","typeVersion":2,"nodeCategories":[{"id":25,"name":"AI"},{"id":26,"name":"Langchain"}]}],"categories":[{"id":35,"name":"Document Extraction"},{"id":49,"name":"AI Summarization"}],"image":[]}}