This workflow corresponds to n8n.io template #18026 — we link there as the canonical source.
This workflow follows the Form → Form Trigger 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": "s55hiq5G5KtKtCmL",
"name": "Scheduled Social Post Packages",
"tags": [],
"nodes": [
{
"id": "5b20fc7a-e8e9-444d-85d6-6438a161cb67",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-16,
80
],
"parameters": {
"width": 480,
"height": 896,
"content": "## Scheduled Social Post Packages\n\n### How it works\n\nThis workflow collects details for a scheduled social post package through a form, validates the submitted inputs, and stops early with an error message when required data is missing or invalid. For valid submissions, it chooses a Google Drive destination, checks whether a matching package already exists, and either notifies the user about the duplicate or creates a new folder. It then prepares and uploads the package files, showing a success message when complete or cleaning up the partial folder and reporting an upload error if something fails.\n\n### Setup steps\n\n- Configure the form trigger fields for the post package details the workflow requires, such as package name, schedule information, captions, assets, or destination metadata.\n- Connect Google Drive credentials for the nodes that search, create folders, upload files, and remove incomplete packages.\n- Set the correct parentFolderId in the Set Destination Folder node, or update its expression to choose a folder dynamically.\n- Review the validation and file-preparation code nodes so their required fields, naming conventions, and generated files match your social post package format.\n- Customize the user-facing form response nodes for input errors, duplicate package notices, successful creation, and upload failures.\n\n### Customization\n\nCommon customization points include the validation rules, Google Drive parent folder selection, duplicate-detection naming logic, generated package file structure, and the wording of each form response."
},
"typeVersion": 1
},
{
"id": "c0657e70-e1be-485b-8243-6b4e92c5d83f",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
528,
80
],
"parameters": {
"color": 7,
"width": 896,
"height": 576,
"content": "## Collect and validate inputs\n\nStarts the workflow from a form submission, validates the package details, branches based on validity, and displays an input error for invalid submissions."
},
"typeVersion": 1
},
{
"id": "de9b63e8-3047-4953-8308-4f26366cfad3",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1472,
80
],
"parameters": {
"color": 7,
"width": 720,
"height": 352,
"content": "## Check destination duplicates\n\nSets the target Google Drive parent folder, searches for an existing package, and decides whether the submission is a duplicate."
},
"typeVersion": 1
},
{
"id": "eb105169-2abe-4738-bfbe-bd5e75822195",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2240,
80
],
"parameters": {
"color": 7,
"width": 496,
"height": 560,
"content": "## Handle duplicate or create\n\nHandles the duplicate-check branch by either showing a duplicate notice or creating the new package folder in Google Drive."
},
"typeVersion": 1
},
{
"id": "afb45b5b-0884-47d2-bbd7-e40fdc2969e7",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2784,
80
],
"parameters": {
"color": 7,
"width": 496,
"height": 352,
"content": "## Prepare and upload files\n\nBuilds the package file payloads and uploads them into the newly created Google Drive folder."
},
"typeVersion": 1
},
{
"id": "d3e46d2a-0c2e-4dd1-b3da-83cb8bd0b429",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
3328,
80
],
"parameters": {
"color": 7,
"width": 304,
"height": 352,
"content": "## Show creation success\n\nDisplays the final confirmation when all package files upload successfully."
},
"typeVersion": 1
},
{
"id": "2eda94a1-9d89-48b7-aa78-c9304dda9796",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
3328,
496
],
"parameters": {
"color": 7,
"width": 496,
"height": 352,
"content": "## Clean failed upload\n\nRemoves the incomplete Google Drive package after an upload problem and shows the user an upload error message."
},
"typeVersion": 1
},
{
"id": "7c5ef41b-fc07-41d4-b67e-85cb0a486e80",
"name": "When Post Details Collected",
"type": "n8n-nodes-base.formTrigger",
"position": [
608,
240
],
"parameters": {
"options": {
"path": "social-post-package",
"buttonLabel": "Create package",
"appendAttribution": false
},
"formTitle": "Create a Scheduled Social Post Package",
"formFields": {
"values": [
{
"fieldName": "scheduled_date",
"fieldType": "date",
"fieldLabel": "Scheduled date",
"requiredField": true
},
{
"fieldName": "scheduled_time",
"fieldLabel": "Scheduled time (24h HH:MM)",
"placeholder": "e.g. 18:00",
"requiredField": true
},
{
"fieldName": "post_text",
"fieldType": "textarea",
"fieldLabel": "Post text",
"requiredField": true
},
{
"fieldName": "folder_slug",
"fieldLabel": "Package label (optional)",
"placeholder": "e.g. sakura-river"
},
{
"fieldName": "images",
"fieldType": "file",
"fieldLabel": "Images (1\u20134; PNG/JPG)",
"requiredField": true,
"acceptFileTypes": ".png,.jpg,.jpeg"
}
]
},
"responseMode": "lastNode",
"formDescription": "Choose a date and time, add your post text, and upload 1\u20134 PNG/JPG images. The workflow creates a tidy Google Drive package containing tweet.txt and numbered images."
},
"typeVersion": 2.6
},
{
"id": "c382f885-ca7b-4a77-a1d8-43886143b4b3",
"name": "Validate Inputs with Code",
"type": "n8n-nodes-base.code",
"position": [
848,
240
],
"parameters": {
"jsCode": "const j = $json;\nconst binary = $input.first().binary || {};\nconst errors = [];\n\nconst dateStr = (j.scheduled_date || '').toString().trim();\nconst timeStr = (j.scheduled_time || '').toString().trim();\nconst caption = (j.post_text || '').toString();\nconst slugRaw = (j.folder_slug || '').toString().trim();\n\n// Date: strict YYYY-MM-DD and a real calendar date.\nconst dm = dateStr.match(/^(\\d{4})-(\\d{2})-(\\d{2})$/);\nif (!dm) {\n errors.push('Invalid scheduled date (use YYYY-MM-DD).');\n} else {\n const year = Number(dm[1]);\n const month = Number(dm[2]);\n const day = Number(dm[3]);\n const parsed = new Date(Date.UTC(year, month - 1, day));\n const isRealDate =\n parsed.getUTCFullYear() === year &&\n parsed.getUTCMonth() === month - 1 &&\n parsed.getUTCDate() === day;\n\n if (!isRealDate) {\n errors.push('Invalid scheduled date (choose a real calendar date).');\n }\n}\n\n// Time: HH:MM, 24-hour.\nconst tm = timeStr.match(/^([01]?\\d|2[0-3]):([0-5]\\d)$/);\nif (!tm) {\n errors.push('Invalid scheduled time (use 24-hour HH:MM, e.g. 18:00).');\n}\n\n// Post text must not be empty.\nif (!caption.trim()) {\n errors.push('Post text is empty.');\n}\n\n// Images: 1\u20134 PNG/JPG files.\nconst keys = Object.keys(binary);\nconst allowedExt = ['png', 'jpg', 'jpeg'];\n\nif (keys.length === 0) {\n errors.push('At least one image is required.');\n}\nif (keys.length > 4) {\n errors.push('No more than four images are allowed.');\n}\n\nfor (const k of keys) {\n const b = binary[k] || {};\n const ext = (b.fileExtension || (b.fileName || '').split('.').pop() || '').toLowerCase();\n const mime = (b.mimeType || '').toLowerCase();\n const okExt = allowedExt.includes(ext);\n const okMime = mime === 'image/png' || mime === 'image/jpeg' || mime === 'image/jpg';\n\n if (!okExt && !okMime) {\n errors.push('Unsupported file type: ' + (b.fileName || k) + ' (only PNG/JPG allowed).');\n }\n}\n\n// Canonical package folder: YYYY-MM-DD_HHMM[__label]\nconst hhmm = tm ? (tm[1].padStart(2, '0') + tm[2]) : '0000';\nconst slug = slugRaw\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\nlet folderName = dateStr + '_' + hhmm;\nif (slug) folderName += '__' + slug;\n\nreturn [{\n json: {\n valid: errors.length === 0,\n errorMessage: errors.join(' '),\n folderName,\n caption,\n slug,\n },\n}];"
},
"typeVersion": 2
},
{
"id": "4e239762-b2e8-45dd-97b7-8a9fdff911c0",
"name": "If Inputs Valid",
"type": "n8n-nodes-base.if",
"position": [
1088,
240
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "valid",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.valid }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "20e89c46-2d18-4783-920f-81400cf2d530",
"name": "Search for Package in Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
1776,
240
],
"parameters": {
"filter": {
"folderId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Google Drive Folder ID').first().json.parentFolderId }}"
},
"whatToSearch": "folders"
},
"options": {
"fields": [
"id",
"name"
]
},
"resource": "fileFolder",
"returnAll": true,
"queryString": "={{ $('Validate Inputs with Code').first().json.folderName }}"
},
"typeVersion": 3,
"alwaysOutputData": true
},
{
"id": "f2b596d1-ca0d-4315-9817-9b881b9179a7",
"name": "Check Duplicate Package",
"type": "n8n-nodes-base.if",
"position": [
2016,
240
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "exists",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $('Search for Package in Drive').all().filter(f => f.json.name === $('Validate Inputs with Code').first().json.folderName).length > 0 }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "51e3a389-b567-45cd-8496-77dfe891b78a",
"name": "Display Duplicate Message",
"type": "n8n-nodes-base.form",
"position": [
2432,
240
],
"parameters": {
"options": {},
"operation": "completion",
"completionTitle": "Nothing was created",
"completionMessage": "=A package named \"{{ $('Validate Inputs with Code').first().json.folderName }}\" already exists in the destination folder. Nothing new was created."
},
"typeVersion": 2.5
},
{
"id": "c47f80b7-ba1d-4927-b33c-7ddb3fc7de4b",
"name": "Create Folder in Google Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
2432,
464
],
"parameters": {
"name": "={{ $('Validate Inputs with Code').first().json.folderName }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Google Drive Folder ID').first().json.parentFolderId }}"
},
"resource": "folder"
},
"typeVersion": 3
},
{
"id": "49c790a5-d035-4c1e-9067-6b8195565eee",
"name": "Prepare Files with Code",
"type": "n8n-nodes-base.code",
"position": [
2864,
240
],
"parameters": {
"jsCode": "\nconst folderId = $('Create Folder in Google Drive').first().json.id;\nconst caption = $('Validate Inputs with Code').first().json.caption;\nconst trigger = $('When Post Details Collected').first();\nconst binary = trigger.binary || {};\n\nconst items = [];\n\n// tweet.txt from the caption\nconst txtBinary = await this.helpers.prepareBinaryData(\n Buffer.from(caption, 'utf8'),\n 'tweet.txt',\n 'text/plain',\n);\nitems.push({ json: { fileName: 'tweet.txt', folderId }, binary: { data: txtBinary } });\n\n// images -> 01.<ext>, 02.<ext> ...\nconst keys = Object.keys(binary).sort();\nlet i = 0;\nfor (const k of keys) {\n i++;\n const b = binary[k];\n const ext = (b.fileExtension || (b.fileName || '').split('.').pop() || 'png').toLowerCase();\n const idx = String(i).padStart(2, '0');\n items.push({ json: { fileName: idx + '.' + ext, folderId }, binary: { data: b } });\n}\n\nreturn items;\n"
},
"typeVersion": 2
},
{
"id": "17f2f8b1-32ce-43ee-9553-357ed6b4aee2",
"name": "Upload Files to Google Drive",
"type": "n8n-nodes-base.googleDrive",
"onError": "continueErrorOutput",
"position": [
3104,
240
],
"parameters": {
"name": "={{ $json.fileName }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.folderId }}"
}
},
"typeVersion": 3
},
{
"id": "5863bc59-f00e-4699-bbf6-d7e1512edf5f",
"name": "Display Package Creation Notice",
"type": "n8n-nodes-base.form",
"position": [
3424,
240
],
"parameters": {
"options": {},
"operation": "completion",
"completionTitle": "Package created",
"completionMessage": "=Created \"{{ $('Validate Inputs with Code').first().json.folderName }}\" with tweet.txt and your numbered images in Google Drive."
},
"typeVersion": 2.5
},
{
"id": "4fa8030e-2387-4b45-9856-544a9fe3bfa0",
"name": "Delete Incomplete Package",
"type": "n8n-nodes-base.googleDrive",
"position": [
3408,
656
],
"parameters": {
"options": {
"deletePermanently": false
},
"resource": "folder",
"operation": "deleteFolder",
"folderNoRootId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Create Folder in Google Drive').first().json.id }}"
}
},
"executeOnce": true,
"typeVersion": 3
},
{
"id": "5b63f140-339d-458a-9b53-703cc4394aea",
"name": "Display Upload Error Form",
"type": "n8n-nodes-base.form",
"position": [
3648,
656
],
"parameters": {
"options": {},
"operation": "completion",
"completionTitle": "Package creation failed",
"completionMessage": "An upload failed, so the incomplete folder was removed. Nothing was left behind. Please try again."
},
"typeVersion": 2.5
},
{
"id": "57b89aa2-334c-405b-bb30-96ddd4b3c394",
"name": "Display Input Error Form",
"type": "n8n-nodes-base.form",
"position": [
1264,
464
],
"parameters": {
"options": {},
"operation": "completion",
"completionTitle": "Nothing was created",
"completionMessage": "=Please fix the following and submit again: {{ $json.errorMessage }}"
},
"typeVersion": 2.5
},
{
"id": "ece5c33e-80a4-449a-9c04-eb1a99fee5e9",
"name": "Set Google Drive Folder ID",
"type": "n8n-nodes-base.set",
"position": [
1536,
240
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "1d86d3a9-3852-489a-ba50-ca866a147bff",
"name": "parentFolderId",
"type": "string",
"value": "PASTE_GOOGLE_DRIVE_PARENT_FOLDER_ID_HERE"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "f7e3405b-e7eb-4a58-a6d5-18faefc26d6e",
"nodeGroups": [],
"connections": {
"If Inputs Valid": {
"main": [
[
{
"node": "Set Google Drive Folder ID",
"type": "main",
"index": 0
}
],
[
{
"node": "Display Input Error Form",
"type": "main",
"index": 0
}
]
]
},
"Check Duplicate Package": {
"main": [
[
{
"node": "Display Duplicate Message",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Folder in Google Drive",
"type": "main",
"index": 0
}
]
]
},
"Prepare Files with Code": {
"main": [
[
{
"node": "Upload Files to Google Drive",
"type": "main",
"index": 0
}
]
]
},
"Delete Incomplete Package": {
"main": [
[
{
"node": "Display Upload Error Form",
"type": "main",
"index": 0
}
]
]
},
"Validate Inputs with Code": {
"main": [
[
{
"node": "If Inputs Valid",
"type": "main",
"index": 0
}
]
]
},
"Set Google Drive Folder ID": {
"main": [
[
{
"node": "Search for Package in Drive",
"type": "main",
"index": 0
}
]
]
},
"Search for Package in Drive": {
"main": [
[
{
"node": "Check Duplicate Package",
"type": "main",
"index": 0
}
]
]
},
"When Post Details Collected": {
"main": [
[
{
"node": "Validate Inputs with Code",
"type": "main",
"index": 0
}
]
]
},
"Upload Files to Google Drive": {
"main": [
[
{
"node": "Display Package Creation Notice",
"type": "main",
"index": 0
}
],
[
{
"node": "Delete Incomplete Package",
"type": "main",
"index": 0
}
]
]
},
"Create Folder in Google Drive": {
"main": [
[
{
"node": "Prepare Files with Code",
"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 collects scheduled social post details via an n8n form and creates a Google Drive package containing a tweet.txt file and 1–4 numbered images, while validating inputs, preventing duplicate package folders, and cleaning up incomplete uploads. Receives a submission…
Source: https://n8n.io/workflows/18026/ — 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.
This interactive tutorial teaches you how to build in n8n from scratch, using a live walkthrough with real-time examples. Rather than static documentation, this guided workflow explains key n8n concep
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
This workflow exposes an n8n Form that calculates a product’s selling price from cost, tax, and marketplace fees using either a target profit margin or a target markup, then displays the resulting pri
Credentials Transfer. Uses form, httpRequest, executeCommand, readWriteFile. Event-driven trigger; 22 nodes.
Waitlist Form Stored In Googlesheet With Email Verification Step. Uses googleSheets, emailSend, form, stickyNote. Event-driven trigger; 19 nodes.