{
  "id": "OgbbC37jdxoezN2E",
  "name": "Template Creator",
  "tags": [
    {
      "id": "vHVvr905dk6aqmKq",
      "name": "Template",
      "createdAt": "2025-05-16T19:05:27.035Z",
      "updatedAt": "2025-05-16T19:05:27.035Z"
    },
    {
      "id": "v0qdZQrIENz1TXgD",
      "name": "PDF",
      "createdAt": "2025-08-09T22:56:54.358Z",
      "updatedAt": "2025-08-09T22:56:54.358Z"
    },
    {
      "id": "DzVAQjk8ZZxDPLam",
      "name": "Word",
      "createdAt": "2026-03-22T19:08:05.375Z",
      "updatedAt": "2026-03-22T19:08:05.375Z"
    },
    {
      "id": "refSV3bBr5Plhm0A",
      "name": "Drafting",
      "createdAt": "2026-03-22T19:08:34.897Z",
      "updatedAt": "2026-03-22T19:08:34.897Z"
    }
  ],
  "nodes": [
    {
      "id": "979b6ee5-34b0-4f0a-be3d-d03b55d83df6",
      "name": "Sticky \u2014 Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -32
      ],
      "parameters": {
        "width": 500,
        "height": 816,
        "content": "## \ud83d\udcc4Template Creator\n\n### How it works\nThis workflow accepts any uploaded document (PDF or DOCX) via webhook and automatically converts it into a reusable fill-in-the-blank template.\n\n**Step 1 \u2014 Identify:** GPT-4o first reads the document and determines the document type (e.g., Employment Contract, Invoice, NDA, Lease Agreement, Project Proposal) and the specific variable fields that type of document typically contains.\n\n**Step 2 \u2014 Templatize:** A second AI pass uses the identified document type and field list to replace all variable content with clearly labeled `[BRACKET]` placeholders while preserving all static boilerplate and structure verbatim.\n\n**Step 3 \u2014 Deliver:** The cleaned template is rendered to PDF via Gotenberg, uploaded to Google Drive, made publicly accessible, and a JSON response with file URLs is returned to the caller.\n\n### Setup\n1. Configure the **Webhook** path if needed (default: `general-template-creator`)\n2. Set your **OpenAI** credential on both LLM nodes\n3. Set your **Google Drive** credential and confirm the target folder ID in the Upload node\n4. Confirm the **Gotenberg** URL matches your self-hosted instance\n5. Install the community node `n8n-nodes-word2text` (see \u26a0\ufe0f warning sticky)\n\n### Customization\n- Swap GPT-4o for GPT-4.1 or GPT-4.1-mini on the Identify node to reduce cost on the lighter classification task\n- Add a Switch node after identification to route different document types to type-specific prompts\n- Modify the Drive folder ID to sort templates into subfolders by document type\n\nDocument accepts input from a form such as the one found here:\n[Sample Form](https://iportgpt.com/n8n_assets/template_creator_form.html)\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d8eea0b3-cc82-4b8b-b557-20ae9d420aaa",
      "name": "Sticky \u2014 Community Node Warning",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        496
      ],
      "parameters": {
        "color": 3,
        "width": 290,
        "height": 238,
        "content": "## \u26a0\ufe0f Community Node Required\n`n8n-nodes-word2text` must be installed on your self-hosted n8n instance.\n\n**Settings \u2192 Community Nodes \u2192 Install**\nPackage name: `n8n-nodes-word2text`\n\nHandles the .docx \u2192 plain text extraction path."
      },
      "typeVersion": 1
    },
    {
      "id": "987863d3-5ef2-4bce-86bc-4593b7a10186",
      "name": "Sticky \u2014 Intake",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        512,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 502,
        "content": "## 1 \u00b7 Intake\nReceive document upload via POST webhook.\nExpects `fileName` and a binary `file` attachment.\nOptional: `additionalContext` for hints."
      },
      "typeVersion": 1
    },
    {
      "id": "e5d9ff12-601d-451d-ba62-f5b5a6d868c6",
      "name": "Sticky \u2014 Text Extraction",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        752,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 356,
        "height": 502,
        "content": "## 2 \u00b7 Text Extraction\nRoute by file extension (.docx or .pdf) and extract plain text. Both paths feed into the AI identification step."
      },
      "typeVersion": 1
    },
    {
      "id": "f55dcec1-3fb6-49e6-83b3-ff82d4f67c4c",
      "name": "Sticky \u2014 Identify",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1120,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 312,
        "height": 496,
        "content": "## 3 \u00b7 Identify Document Type\nGPT-4o reads the document and returns a structured JSON object containing the document type name and the full list of variable field placeholders specific to that document type."
      },
      "typeVersion": 1
    },
    {
      "id": "1fd8e8e2-92de-4f55-b99c-3f926cfaf393",
      "name": "Sticky \u2014 Parse Classification",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1456,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 208,
        "height": 502,
        "content": "## 4 \u00b7 Parse AI Classification\nExtracts `documentType` and `fields` array from the JSON returned by the Identify step. Passes both forward alongside the original document text."
      },
      "typeVersion": 1
    },
    {
      "id": "50a30ab0-229e-4480-a065-e23b7eddd6f4",
      "name": "Sticky \u2014 Generate Template",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 280,
        "height": 496,
        "content": "## 5 \u00b7 Generate Template\nA second GPT-4o pass uses the identified document type and field list to replace all variable content with [BRACKET] placeholders. Boilerplate and structure are preserved verbatim."
      },
      "typeVersion": 1
    },
    {
      "id": "d454844d-9ce3-42bb-aa04-0643157842ee",
      "name": "Sticky \u2014 Build and Deliver",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1968,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 916,
        "height": 486,
        "content": "## 6 \u00b7 Build, Convert & Deliver\nClean AI output, wrap in HTML with standard letter formatting, render to PDF via Gotenberg, upload to Google Drive, set public viewer link, and return JSON response with file URLs."
      },
      "typeVersion": 1
    },
    {
      "id": "73d3b058-e22f-4b90-beae-db324d40987e",
      "name": "Webhook \u2014 Receive Document",
      "type": "n8n-nodes-base.webhook",
      "position": [
        560,
        176
      ],
      "parameters": {
        "path": "general-template-creator",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "73f17329-0307-43e6-b09f-a995fe236796",
      "name": "Switch \u2014 Route by File Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        800,
        176
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "rule-docx",
                    "operator": {
                      "type": "string",
                      "operation": "endsWith"
                    },
                    "leftValue": "={{ $json.body.fileName.toLowerCase() }}",
                    "rightValue": ".docx"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "rule-pdf",
                    "operator": {
                      "type": "string",
                      "operation": "endsWith"
                    },
                    "leftValue": "={{ $json.body.fileName.toLowerCase() }}",
                    "rightValue": ".pdf"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "bd11b29d-13cb-49ed-a0d4-7aba17260c8d",
      "name": "Extract \u2014 Word to Text",
      "type": "n8n-nodes-word2text.word2text",
      "position": [
        960,
        96
      ],
      "parameters": {
        "binaryPropertyName": "file"
      },
      "typeVersion": 1
    },
    {
      "id": "a8c97b56-5cc3-429a-bb13-3be0075d3e47",
      "name": "Extract \u2014 PDF to Text",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        960,
        256
      ],
      "parameters": {
        "options": {},
        "operation": "pdf",
        "binaryPropertyName": "file"
      },
      "typeVersion": 1.1
    },
    {
      "id": "6936cf79-77f9-4c48-934a-c51bbaef5ad7",
      "name": "AI \u2014 Identify Document Type",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1152,
        176
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "options": {
          "systemMessage": "You are a document classification expert. Your sole job is to read a document and output a JSON object \u2014 nothing else.\n\nAnalyze the document and return ONLY a valid JSON object in this exact format:\n{\n  \"documentType\": \"<concise document type name, e.g. Employment Contract, Invoice, Non-Disclosure Agreement, Lease Agreement, Project Proposal, Purchase Order, Service Agreement, Letter of Intent>\",\n  \"documentTypeSlug\": \"<snake_case version of documentType, e.g. employment_contract>\",\n  \"description\": \"<one sentence describing what this document is and its purpose>\",\n  \"fields\": [\n    {\n      \"placeholder\": \"[FIELD_NAME]\",\n      \"description\": \"Brief description of what value goes here\"\n    }\n  ]\n}\n\nRules:\n- The fields array must contain EVERY variable element found in this specific document\n- Use ALL_CAPS_UNDERSCORE format for placeholder names inside brackets\n- Be specific: prefer [EMPLOYEE_NAME] over [NAME], [MONTHLY_RENT] over [AMOUNT]\n- Include ALL parties, ALL dates, ALL amounts, ALL addresses, ALL identifying numbers\n- Output ONLY the JSON object. No preamble, no explanation, no markdown fences."
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "291052c3-11e7-4e7c-a42c-668073e1be15",
      "name": "LLM \u2014 GPT-4o (Identify)",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1152,
        320
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "gpt-4o"
        },
        "options": {
          "maxTokens": 1500,
          "temperature": 0.1
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "66ef38c5-dca4-4188-a208-20a3cf571025",
      "name": "Code \u2014 Parse Classification",
      "type": "n8n-nodes-base.code",
      "position": [
        1504,
        176
      ],
      "parameters": {
        "jsCode": "// Pull the AI output and original webhook data\nconst rawOutput = $('AI \u2014 Identify Document Type').item.json.output || '';\nconst webhookBody = $('Webhook \u2014 Receive Document').item.json.body || {};\nconst extractedText = $('AI \u2014 Identify Document Type').item.json.output ? $input.item.json.output : '';\n\n// Strip markdown fences if the model wrapped it anyway\nlet cleaned = rawOutput.replace(/```json\\s*/gi, '').replace(/```\\s*/g, '').trim();\n\nlet classification;\ntry {\n  classification = JSON.parse(cleaned);\n} catch (e) {\n  // Attempt to extract JSON object from within the string\n  const match = cleaned.match(/\\{[\\s\\S]*\\}/);\n  if (match) {\n    classification = JSON.parse(match[0]);\n  } else {\n    throw new Error('Could not parse classification JSON from AI output: ' + cleaned.substring(0, 200));\n  }\n}\n\n// Build a readable placeholder list for the template prompt\nconst fieldList = (classification.fields || []).map(f => f.placeholder + ' \u2014 ' + f.description).join('\\n');\n\nreturn [{\n  json: {\n    documentType: classification.documentType || 'Document',\n    documentTypeSlug: classification.documentTypeSlug || 'document',\n    documentDescription: classification.description || '',\n    fields: classification.fields || [],\n    fieldList: fieldList,\n    fileName: webhookBody.fileName || 'unknown',\n    additionalContext: webhookBody.additionalContext || ''\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "caa6e7da-e76c-4561-8820-cbf9a7fd09ce",
      "name": "AI \u2014 Generate Template",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1712,
        176
      ],
      "parameters": {
        "text": "=You are an expert document template creator.\n\nYour task is to transform the provided document into a professional, reusable fill-in-the-blank template.\n\n**DOCUMENT TYPE:** {{ $json.documentType }}\n**DOCUMENT DESCRIPTION:** {{ $json.documentDescription }}\n**ORIGINAL FILENAME:** {{ $json.fileName }}\n\n**IDENTIFIED VARIABLE FIELDS FOR THIS DOCUMENT TYPE:**\n{{ $json.fieldList }}\n\n**DOCUMENT TEXT TO TEMPLATIZE:**\n{{ $('Extract \u2014 Word to Text').item.json.text || $('Extract \u2014 PDF to Text').item.json.text }}\n\n**YOUR INSTRUCTIONS:**\n1. Read the document carefully\n2. Replace ALL variable, instance-specific content with the appropriate [PLACEHOLDER] from the field list above\n3. If you encounter variable content NOT covered by the field list, create a new descriptive placeholder in [ALL_CAPS_UNDERSCORE] format\n4. Preserve ALL static boilerplate, standard language, and structural elements EXACTLY as written\n5. Maintain all section headings, numbering, indentation, and formatting conventions\n6. Keep signature blocks \u2014 replace signer names with [SIGNER_NAME] / [SIGNER_TITLE] placeholders\n\n**CRITICAL OUTPUT REQUIREMENTS:**\n- Output ONLY the template document text\n- Do NOT include any preamble, explanation, field list, or notes\n- Do NOT use markdown formatting (no **, no ```, no ###)\n- Do NOT add headers like \"TEMPLATE:\" or \"OUTPUT:\"\n- Start directly with the first line of the document\n- Use plain text only \u2014 output will be converted to PDF\n\nBegin output now.",
        "options": {
          "systemMessage": "You are a document template specialist. You convert real documents into reusable templates by replacing all variable, instance-specific content with clearly labeled [BRACKET] placeholders. You never alter boilerplate language, standard clauses, or the structural integrity of the document. You always output only the template document \u2014 no explanations, no metadata."
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "dd580e69-a34a-4161-ae8e-84ffeb2120a7",
      "name": "LLM \u2014 GPT-4o (Generate)",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1712,
        320
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "gpt-4o"
        },
        "options": {
          "maxTokens": 4000,
          "temperature": 0.2
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "9f99c51b-1291-4ee5-81d7-dc653d87af03",
      "name": "Code \u2014 Clean and Format Output",
      "type": "n8n-nodes-base.code",
      "position": [
        2016,
        176
      ],
      "parameters": {
        "jsCode": "const rawAiOutput = $('AI \u2014 Generate Template').item.json.output || '';\nconst meta = $('Code \u2014 Parse Classification').item.json;\n\nlet cleanOutput = rawAiOutput;\n\n// Strip markdown code fences\ncleanOutput = cleanOutput.replace(/```[a-z]*\\n?/gi, '');\ncleanOutput = cleanOutput.replace(/```\\s*$/g, '');\n\n// Strip any preamble headers the model might add\ncleanOutput = cleanOutput.replace(/^(TEMPLATE:|OUTPUT:|DOCUMENT TEMPLATE:)\\s*\\n/gim, '');\ncleanOutput = cleanOutput.replace(/^\\*\\*(TEMPLATE|OUTPUT|RESULT):\\*\\*\\s*\\n/gim, '');\n\n// Strip placeholder/field lists if the model included them before the doc\ncleanOutput = cleanOutput.replace(/^[\\s\\S]*?(?=^[A-Z\\[\\(])/m, '');\n\n// Remove markdown bold\ncleanOutput = cleanOutput.replace(/\\*\\*([^*]+)\\*\\*/g, '$1');\n\n// Normalize whitespace\ncleanOutput = cleanOutput.replace(/\\n{3,}/g, '\\n\\n').trim();\n\n// Build filename\nconst timestamp = new Date().toISOString().split('T')[0];\nconst filename = meta.documentTypeSlug + '_template_' + timestamp;\n\n// Build HTML for Gotenberg\nconst htmlBody = cleanOutput.replace(/\\n/g, '<br>');\n\nconst htmlContent = '<!DOCTYPE html>\\n<html>\\n<head>\\n  <meta charset=\"UTF-8\">\\n  <style>\\n    @page { size: letter; margin: 1in; }\\n    body { font-family: Arial, Helvetica, sans-serif; font-size: 11pt; line-height: 1.6; margin: 0; padding: 0; color: #222; }\\n    br { display: block; margin: 0; }\\n  </style>\\n</head>\\n<body>\\n  ' + htmlBody + '\\n</body>\\n</html>';\n\nconst binaryData = Buffer.from(htmlContent, 'utf-8');\n\nreturn [{\n  json: {\n    filename: filename,\n    documentType: meta.documentType,\n    documentTypeSlug: meta.documentTypeSlug,\n    documentDescription: meta.documentDescription,\n    fields: meta.fields,\n    originalFileName: meta.fileName,\n    createdDate: timestamp,\n    htmlContent: htmlContent,\n    originalLength: rawAiOutput.length,\n    cleanedLength: cleanOutput.length\n  },\n  binary: {\n    data: {\n      data: binaryData.toString('base64'),\n      mimeType: 'text/html',\n      fileName: 'index.html'\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "15c8d810-ce67-4d11-9abe-bc3dcd00f031",
      "name": "Gotenberg \u2014 HTML to PDF",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2192,
        176
      ],
      "parameters": {
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "index.html",
              "parameterType": "formBinaryData",
              "inputDataFieldName": "=data"
            }
          ]
        },
        "genericAuthType": "httpBasicAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Gotenberg-Output-Filename",
              "value": "={{ $('Code \u2014 Clean and Format Output').item.json.filename }}"
            }
          ]
        }
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "a3fb5e14-a5d0-44a7-9c63-15e95fc57e09",
      "name": "GDrive \u2014 Upload PDF",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2384,
        176
      ],
      "parameters": {
        "name": "={{ $('Code \u2014 Clean and Format Output').item.json.filename }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "16p8nurkJi1eog833Z5lsaKc6zMtvn6rL",
          "cachedResultUrl": "https://drive.google.com/drive/folders/16p8nurkJi1eog833Z5lsaKc6zMtvn6rL",
          "cachedResultName": "Templates"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "ae2e1e61-5e83-4b2e-9b45-9951421089b4",
      "name": "GDrive \u2014 Set Public Link",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2576,
        176
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "options": {},
        "operation": "share",
        "permissionsUi": {
          "permissionsValues": {
            "role": "reader",
            "type": "anyone"
          }
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "6baefca1-8050-48d9-b384-282ded4327ef",
      "name": "Webhook \u2014 Send Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2752,
        176
      ],
      "parameters": {
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"message\": \"Document template created successfully!\",\n  \"classification\": {\n    \"documentType\": \"{{ $('Code \u2014 Clean and Format Output').item.json.documentType }}\",\n    \"description\": \"{{ $('Code \u2014 Clean and Format Output').item.json.documentDescription }}\",\n    \"fieldsIdentified\": {{ $('Code \u2014 Clean and Format Output').item.json.fields.length }}\n  },\n  \"template\": {\n    \"filename\": \"{{ $('Code \u2014 Clean and Format Output').item.json.filename }}.pdf\",\n    \"originalDocument\": \"{{ $('Code \u2014 Clean and Format Output').item.json.originalFileName }}\",\n    \"createdDate\": \"{{ $('Code \u2014 Clean and Format Output').item.json.createdDate }}\",\n    \"googleDriveId\": \"{{ $('GDrive \u2014 Upload PDF').item.json.id }}\",\n    \"viewUrl\": \"{{ $('GDrive \u2014 Upload PDF').item.json.webViewLink }}\",\n    \"downloadUrl\": \"{{ $('GDrive \u2014 Upload PDF').item.json.webContentLink }}\",\n    \"directDownload\": \"https://drive.google.com/uc?export=download&id={{ $('GDrive \u2014 Upload PDF').item.json.id }}\"\n  },\n  \"fields\": {{ JSON.stringify($('Code \u2014 Clean and Format Output').item.json.fields) }},\n  \"instructions\": {\n    \"usage\": \"Your document template is ready. All variable content has been replaced with [PLACEHOLDER] tags.\",\n    \"howToUse\": [\n      \"Download the PDF to see the complete template with all placeholders\",\n      \"Use the fields array in this response as your fill-in guide\",\n      \"Replace each [PLACEHOLDER] with the actual value for your document\",\n      \"Save as a new file for each new instance of the document\"\n    ],\n    \"placeholderFormat\": \"All placeholders use [ALL_CAPS_UNDERSCORE] format\"\n  }\n}"
      },
      "typeVersion": 1.5
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "b872b98b-affd-41f5-8068-53e511494a91",
  "connections": {
    "GDrive \u2014 Upload PDF": {
      "main": [
        [
          {
            "node": "GDrive \u2014 Set Public Link",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract \u2014 PDF to Text": {
      "main": [
        [
          {
            "node": "AI \u2014 Identify Document Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI \u2014 Generate Template": {
      "main": [
        [
          {
            "node": "Code \u2014 Clean and Format Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract \u2014 Word to Text": {
      "main": [
        [
          {
            "node": "AI \u2014 Identify Document Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gotenberg \u2014 HTML to PDF": {
      "main": [
        [
          {
            "node": "GDrive \u2014 Upload PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM \u2014 GPT-4o (Generate)": {
      "ai_languageModel": [
        [
          {
            "node": "AI \u2014 Generate Template",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "LLM \u2014 GPT-4o (Identify)": {
      "ai_languageModel": [
        [
          {
            "node": "AI \u2014 Identify Document Type",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "GDrive \u2014 Set Public Link": {
      "main": [
        [
          {
            "node": "Webhook \u2014 Send Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook \u2014 Receive Document": {
      "main": [
        [
          {
            "node": "Switch \u2014 Route by File Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI \u2014 Identify Document Type": {
      "main": [
        [
          {
            "node": "Code \u2014 Parse Classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Parse Classification": {
      "main": [
        [
          {
            "node": "AI \u2014 Generate Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch \u2014 Route by File Type": {
      "main": [
        [
          {
            "node": "Extract \u2014 Word to Text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Extract \u2014 PDF to Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Clean and Format Output": {
      "main": [
        [
          {
            "node": "Gotenberg \u2014 HTML to PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}