AutomationFlowsWeb Scraping › Generate Merged Onboarding PDF Packets with Pdfops and Google Drive

Generate Merged Onboarding PDF Packets with Pdfops and Google Drive

ByPDFops @pdfops on n8n.io

This workflow receives a webhook request with two fillable PDF form URLs and field values, fills and merges the PDFs using PDFops, uploads the combined onboarding packet to Google Drive, and returns a JSON success or error response to the caller. Receives a POST webhook request…

Webhook trigger★★★★☆ complexity17 nodesHTTP RequestN8N Nodes PdfopsGoogle Drive
Web Scraping Trigger: Webhook Nodes: 17 Complexity: ★★★★☆ Added:

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

This workflow follows the Google Drive → HTTP Request 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": "pdfops-onboarding-packet-merge",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Generate onboarding packets from two PDF forms and upload to Google Drive",
  "nodes": [
    {
      "id": "note-overview",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -800,
        -180
      ],
      "parameters": {
        "width": 560,
        "height": 1260,
        "content": "## Generate onboarding packets from two PDF forms and upload to Google Drive\n\nTurn a single webhook call (new hire, new tenant) into a finished onboarding packet: two blank PDF forms filled, merged and uploaded to Google Drive.\n\n### How it works\n- **New Record Submitted** receives `formAUrl`/`formBUrl` (https URLs to two blank fillable PDFs), `fieldsA`/`fieldsB` and `packetName`.\n- **Payload Valid?** rejects anything without two https URLs and two field objects, before a single fetch happens.\n- Each blank is fetched and filled by its own **PDFops Fill Form** node.\n- **Combine Filled Forms** (core Merge node) waits for both branches; Form A is always page 1.\n- **Both Forms Filled?** refuses to build a packet unless both PDFs arrived, so a half packet is never uploaded.\n- **PDFops Merge Packet** joins the two PDFs, **Upload Packet to Drive** stores it, and the caller gets JSON \u2014 400 on any failure, not a misleading 200.\n\n### Setup\n1. Install the **PDFops** community node (`n8n-nodes-pdfops`) \u2014 verified: one-click install on n8n Cloud (search \"PDFops\" in the nodes panel), or Settings \u2192 Community Nodes on self-hosted.\n2. *(Optional)* Add a **PDFops API** credential for a higher quota \u2014 keyless works at 100 req/IP/month.\n3. Connect a **Google Drive** credential on the upload node.\n4. Field values must be **strings** \u2014 `{\"salary\": \"50000\"}`, not `50000`.\n5. The webhook is public as shipped. Add header auth before production: it fetches whatever URLs it is sent.\n\n### Customization tips\n- Run PDFops **Inspect Fields** on each blank once to get exact field names.\n- Add a third form by duplicating a fetch \u2192 fill branch and raising Merge inputs to 3.\n- Swapping Drive for S3 or email? Use `.first()`, not `.item`, after the merge."
      },
      "typeVersion": 1
    },
    {
      "id": "note-section-1",
      "name": "Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -160,
        -40
      ],
      "parameters": {
        "color": 7,
        "width": 800,
        "height": 700,
        "content": "## 1. Receive the record & fetch both blanks\nOne webhook call carries URLs and field values for both forms; the HTTP nodes download the blank PDFs."
      },
      "typeVersion": 1
    },
    {
      "id": "note-section-2",
      "name": "Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        660,
        -40
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 700,
        "content": "## 2. Fill both forms\nEach PDFops node fills one form. n8n runs the branches one after the other, then the Merge node waits for both."
      },
      "typeVersion": 1
    },
    {
      "id": "note-section-3",
      "name": "Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1120,
        -40
      ],
      "parameters": {
        "color": 7,
        "width": 900,
        "height": 700,
        "content": "## 3. Verify, merge, upload, answer\nOnly a complete pair becomes a packet. PDFops joins the PDFs, Drive stores it, and the caller always gets a status code that means something."
      },
      "typeVersion": 1
    },
    {
      "id": "trigger-webhook",
      "name": "New Record Submitted",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -80,
        310
      ],
      "parameters": {
        "path": "onboarding-packet-merge",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "http-fetch-a",
      "name": "Fetch Blank Form A",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        400,
        180
      ],
      "parameters": {
        "url": "={{ $json.body.formAUrl }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "data"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "http-fetch-b",
      "name": "Fetch Blank Form B",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        400,
        440
      ],
      "parameters": {
        "url": "={{ $json.body.formBUrl }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "data"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "pdfops-fill-a",
      "name": "PDFops Fill Form A",
      "type": "n8n-nodes-pdfops.pdfops",
      "onError": "continueRegularOutput",
      "position": [
        660,
        180
      ],
      "parameters": {
        "fields": "={{ $('New Record Submitted').item.json.body.fieldsA }}",
        "fileName": "form-a-filled.pdf",
        "operation": "fill",
        "binaryPropertyName": "data",
        "outputBinaryPropertyName": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "pdfops-fill-b",
      "name": "PDFops Fill Form B",
      "type": "n8n-nodes-pdfops.pdfops",
      "onError": "continueRegularOutput",
      "position": [
        660,
        440
      ],
      "parameters": {
        "fields": "={{ $('New Record Submitted').item.json.body.fieldsB }}",
        "fileName": "form-b-filled.pdf",
        "operation": "fill",
        "binaryPropertyName": "data",
        "outputBinaryPropertyName": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "combine-filled-forms",
      "name": "Combine Filled Forms",
      "type": "n8n-nodes-base.merge",
      "position": [
        920,
        310
      ],
      "parameters": {
        "mode": "append",
        "numberInputs": 2
      },
      "typeVersion": 3.2
    },
    {
      "id": "pdfops-merge",
      "name": "PDFops Merge Packet",
      "type": "n8n-nodes-pdfops.pdfops",
      "onError": "continueErrorOutput",
      "position": [
        1360,
        310
      ],
      "parameters": {
        "fileName": "={{ $('New Record Submitted').first().json.body.packetName || 'onboarding-packet.pdf' }}",
        "operation": "merge",
        "binaryPropertyName": "data",
        "outputBinaryPropertyName": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "gdrive-upload",
      "name": "Upload Packet to Drive",
      "type": "n8n-nodes-base.googleDrive",
      "onError": "continueErrorOutput",
      "position": [
        1580,
        310
      ],
      "parameters": {
        "name": "={{ $('New Record Submitted').first().json.body.packetName || 'onboarding-packet.pdf' }}",
        "driveId": {
          "mode": "list",
          "value": "My Drive",
          "cachedResultName": "My Drive"
        },
        "options": {},
        "folderId": {
          "mode": "list",
          "value": "root",
          "cachedResultName": "/ (Root folder)"
        },
        "resource": "file",
        "operation": "upload",
        "inputDataFieldName": "data"
      },
      "typeVersion": 3
    },
    {
      "id": "if-payload-valid",
      "name": "Payload Valid?",
      "type": "n8n-nodes-base.if",
      "position": [
        140,
        310
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-payload",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ /^https:\\/\\//.test($json.body.formAUrl || '') && /^https:\\/\\//.test($json.body.formBUrl || '') && typeof $json.body.fieldsA === 'object' && $json.body.fieldsA !== null && !Array.isArray($json.body.fieldsA) && typeof $json.body.fieldsB === 'object' && $json.body.fieldsB !== null && !Array.isArray($json.body.fieldsB) }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "note-section-4",
      "name": "Section 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1080,
        700
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 280,
        "content": "## Failure path\nInvalid payloads, a form that won't fetch or fill, and upload errors all answer the caller here \u2014 a half packet is never uploaded."
      },
      "typeVersion": 1
    },
    {
      "id": "if-both-forms",
      "name": "Both Forms Filled?",
      "type": "n8n-nodes-base.if",
      "position": [
        1140,
        310
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-both-forms",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $input.all().filter(i => i.binary && i.binary.data).length === 2 }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "respond-ok",
      "name": "Respond Success",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1800,
        310
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ status: 'ok', packet: $('New Record Submitted').first().json.body.packetName || 'onboarding-packet.pdf' }) }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "respond-err",
      "name": "Respond With Error",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1140,
        820
      ],
      "parameters": {
        "options": {
          "responseCode": 400
        },
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ status: 'error', reason: ($json.error ? 'step_failed' : ($json.body ? 'invalid_payload' : 'incomplete_packet')), message: ($json.error ? ($json.error.message || String($json.error)) : 'Both form URLs must be https and fieldsA/fieldsB must be objects; a packet is only built when both forms fill successfully.') }) }}"
      },
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Payload Valid?": {
      "main": [
        [
          {
            "node": "Fetch Blank Form A",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Blank Form B",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond With Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Both Forms Filled?": {
      "main": [
        [
          {
            "node": "PDFops Merge Packet",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond With Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Blank Form A": {
      "main": [
        [
          {
            "node": "PDFops Fill Form A",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Blank Form B": {
      "main": [
        [
          {
            "node": "PDFops Fill Form B",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PDFops Fill Form A": {
      "main": [
        [
          {
            "node": "Combine Filled Forms",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PDFops Fill Form B": {
      "main": [
        [
          {
            "node": "Combine Filled Forms",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "PDFops Merge Packet": {
      "main": [
        [
          {
            "node": "Upload Packet to Drive",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond With Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Filled Forms": {
      "main": [
        [
          {
            "node": "Both Forms Filled?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New Record Submitted": {
      "main": [
        [
          {
            "node": "Payload Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Packet to Drive": {
      "main": [
        [
          {
            "node": "Respond Success",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond With Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

This workflow receives a webhook request with two fillable PDF form URLs and field values, fills and merges the PDFs using PDFops, uploads the combined onboarding packet to Google Drive, and returns a JSON success or error response to the caller. Receives a POST webhook request…

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

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

AI Background Change – VideoBGRemover + Wan2.2 Vace (fal). Uses httpRequest, googleDrive. Webhook trigger; 32 nodes.

HTTP Request, Google Drive
Web Scraping

Remove Video Background & Compose on Custom Background with Google Drive. Uses httpRequest, googleDrive. Webhook trigger; 25 nodes.

HTTP Request, Google Drive
Web Scraping

• Upload foreground video (AI actors, product demos, webcam footage) • Provide custom background video URL • API removes video background with videobgremover.com • Composites foreground onto backgroun

HTTP Request, Google Drive
Web Scraping

Automated workflow to remove video backgrounds and composite foreground video on static image backgrounds. Perfect for creating branded content, professional presentations, and consistent visual brand

HTTP Request, Google Drive
Web Scraping

A comprehensive n8n workflow template that completely automates the startup pitch deck submission process for accelerators, incubators, VC firms, and startup competitions. This workflow validates foun

Google Drive, Gmail, N8N Nodes Verifiemail +2