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 →
{
"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
}
]
]
}
}
}
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 →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
AI Background Change – VideoBGRemover + Wan2.2 Vace (fal). Uses httpRequest, googleDrive. Webhook trigger; 32 nodes.
Remove Video Background & Compose on Custom Background with Google Drive. Uses httpRequest, googleDrive. Webhook trigger; 25 nodes.
• 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
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
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