AutomationFlowsAI & RAG › Create Fillable Document Templates From PDF or Docx with Gpt-4o and Google Drive

Create Fillable Document Templates From PDF or Docx with Gpt-4o and Google Drive

ByAI Solutions @legalgpts on n8n.io

This template is built for legal professionals, operations teams, and agencies that work with recurring document types — contracts, NDAs, invoices, lease agreements, proposals — and want to stop rebuilding templates from scratch. If you have an OpenAI API key, a Google Drive…

Webhook trigger★★★★☆ complexityAI-powered22 nodesN8N Nodes Word2TextAgentOpenAI ChatHTTP RequestGoogle Drive
AI & RAG Trigger: Webhook Nodes: 22 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → Google Drive recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "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
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This template is built for legal professionals, operations teams, and agencies that work with recurring document types — contracts, NDAs, invoices, lease agreements, proposals — and want to stop rebuilding templates from scratch. If you have an OpenAI API key, a Google Drive…

Source: https://n8n.io/workflows/14992/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

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

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

What if AI didn't just write content—but actually thought about how to write it? This n8n workflow revolutionizes content creation by deploying multiple specialized AI agents that handle every aspect

Tool Http Request, Anthropic Chat, Airtable +7
AI & RAG

🧠 Gwen – The AI Voice Marketing Agent Gwen is your intelligent voice-powered marketing assistant built in n8n. She combines the power of OpenAI, ElevenLabs, and automation workflows to handle content

Tool Workflow, Memory Buffer Window, Agent +10
AI & RAG

Tired of grinding out YouTube content? This n8n workflow turns AI into your personal video factory—creating engaging, faceless shorts on autopilot. Perfect for creators, marketers, or side-hustlers lo

HTTP Request, Google Drive, Google Sheets +6
AI & RAG

Faceless YouTube Generator. Uses httpRequest, limit, googleDrive, googleSheets. Webhook trigger; 49 nodes.

HTTP Request, Google Drive, Google Sheets +7