{ "meta": { "instanceId": "workflow-fea98264", "versionId": "1.0.0", "createdAt": "2025-09-29T07:07:55.325307", "updatedAt": "2025-09-29T07:07:55.325317", "owner": "n8n-user", "license": "MIT", "category": "automation", "status": "active", "priority": "high", "environment": "production" }, "nodes": [ { "id": "38cd304e-e260-4bbd-ace1-57b5fd0e6344", "name": "When clicking \"Execute Workflow\"", "type": "n8n-nodes-base.manualTrigger", "position": [ -1300, 360 ], "parameters": {}, "typeVersion": 1, "notes": "This manualTrigger node performs automated tasks as part of the workflow." }, { "id": "087994ba-3b40-4337-b17a-e2ab4aa39963", "name": "Whether type is file", "type": "n8n-nodes-base.if", "position": [ 940, 780 ], "parameters": { "conditions": { "string": [ { "value1": "={{ $json.type }}", "value2": "file" } ] } }, "typeVersion": 1, "notes": "This if node performs automated tasks as part of the workflow." }, { "id": "ab2ec609-2c7a-4976-9ce0-57f6961578e1", "name": "Set new path for subfolder", "type": "n8n-nodes-base.set", "position": [ 1240, 900 ], "parameters": { "fields": { "values": [ { "name": "from", "stringValue": "={{ decodeURIComponent($json.path) }}" }, { "name": "to", "stringValue": "={{ decodeURIComponent($('Set folder-paths for from and to').item.json.to + '/' + $json.path.split('/').filter(Boolean).pop() + '/') }}" } ] }, "include": "none", "options": {} }, "typeVersion": 3.2, "notes": "This set node performs automated tasks as part of the workflow." }, { "id": "98099141-0e7f-49f0-bfc9-67eef67b13aa", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [ 540, 474.3406813627256 ], "parameters": { "width": 1861.9238476953906, "height": 665.3466933867735, "content": "## Get all files of subfolders\nIn this segment of the workflow, all files located within subfolders are collected. This includes the exploration of subfolders within subfolders, ensuring the identification of every file throughout the entire folder structure. Additionally, a corresponding folder is created in the destination structure for each identified subfolder." }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "8284d632-f0a0-437e-9f75-6995c72400c2", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [ 2440, 478.85370741482984 ], "parameters": { "width": 695.2464929859717, "height": 660.1721006751914, "content": "## Enrich the files\nIn this phase of the workflow, all identified files are processed and enriched with the correct path within the destination structure." }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "f8b151e4-f9c9-474c-94f3-1c0d340d8e36", "name": "Set new path for file", "type": "n8n-nodes-base.code", "position": [ 2900, 860 ], "parameters": { "jsCode": "for (const item of $input.all()) {\n const toPath = $('Set folder-paths for from and to').item.json.to;\n const fromPath = $('Set folder-paths for from and to').item.json.from;\n\n // Remove leading and trailing slashes\n path = fromPath.replace(/^\\/|\\/$/g, '');\n // Split the path into an array of folders\n const folders = path.split('/');\n // Remove empty strings (resulting from leading, trailing, or consecutive slashes)\n const nonEmptyFoldersCount = folders.filter(folder => folder !== '').length;\n\n newFilePathArray = item.json.path.replace(/^\\/|\\/$/g, '').split('/');\n \n item.json.newPath = toPath.replace(/^\\/|\\/$/g, '') + '/' + newFilePathArray.slice(nonEmptyFoldersCount).join(\"/\")\n}\n\nreturn $input.all();" }, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow." }, { "id": "638426c9-c736-4ba9-91a2-383049f15ee5", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [ 3180, 480 ], "parameters": { "width": 695.2464929859717, "height": 658.7966837968969, "content": "## Move files \nIn this stage of the workflow, the files are moved into the destination structure.\n\nIf the batch size remains at 1 in the Loop Over node, each file will be moved sequentially. If the batch size is increased, multiple files will be moved simultaneously." }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "96d83360-21ed-49f1-b273-47ee609f52fa", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [ 3920, 480 ], "parameters": { "width": 695.2464929859717, "height": 658.7966837968969, "content": "## (Optional) Delete *from*-folder\n" }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "cd5dbcf2-378e-4102-9db2-0627c829e2f2", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [ -380, 480 ], "parameters": { "width": 871.7450543093198, "height": 665.3466933867735, "content": "## Get the files and subfolders to move\nIn this segment of the workflow, all files and subfolders to be relocated are gathered. Additionally, the destination folder is created if it does not already exist." }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "91894912-7f54-447b-947b-4040fc92f094", "name": "Sticky Note5", "type": "n8n-nodes-base.stickyNote", "position": [ -1340, 60 ], "parameters": { "width": 723.2756594453772, "height": 463.596247600301, "content": "## Manual Start\nTo manually initiate the workflow, the Set Paths node requires the specification of the folder path to be moved and the destination folder path. Subfolders can be indicated using '/'.\n\nEnsure that the other workflow triggers are deactivated before initiating the workflow." }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "c1e7754f-6efa-4967-9b8d-6c1bcdb55355", "name": "Webhook", "type": "n8n-nodes-base.webhook", "disabled": true, "position": [ -1320, 880 ], "webhookId": "285b2cba-587b-4131-82a8-cdd35a8d49e1", "parameters": { "path": "285b2cba-587b-4131-82a8-cdd35a8d49e1", "options": {}, "httpMethod": "POST", "responseData": "noData", "responseMode": "lastNode" }, "typeVersion": 1, "notes": "This webhook node performs automated tasks as part of the workflow." }, { "id": "cb3e0c28-afa4-4847-b95e-5c7523f18df6", "name": "Sticky Note6", "type": "n8n-nodes-base.stickyNote", "position": [ -1340, 580 ], "parameters": { "width": 723.2756594453772, "height": 500.9028666051119, "content": "## Webhook trigger\nYou can also automate the workflow by configuring a webhook to trigger it. It is crucial that each request contains a JSON body with at least the two attributes 'from-path' and 'to-path' set. Here is an example:\n\n```\n{\n \"from\": \"Folder/to/move\",\n \"to\": \"New-Folder\"\n}\n```\n\nThe workflow will respond with an error, if the request is not valid.\n\nEnsure that the other workflow triggers are deactivated before initiating the workflow." }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "3c85f4a4-28b3-4315-b689-033e4af3f888", "name": "Sticky Note7", "type": "n8n-nodes-base.stickyNote", "position": [ -1340, 1140 ], "parameters": { "width": 723.2756594453772, "height": 498.6039613328509, "content": "## Trigger by other workflow\nIt is also possible to initiate this workflow from within another workflow. It is important to ensure that at least the 'from-path' and 'to-path' are passed as parameters when starting this workflow.\n\nThe workflow will respond with an error, if the request is not valid.\n\nEnsure that the other workflow triggers are deactivated before initiating the workflow." }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." }, { "id": "88e63d18-7c68-4d4f-bfe6-5780115d3ed0", "name": "Execute Workflow Trigger", "type": "n8n-nodes-base.executeWorkflowTrigger", "disabled": true, "position": [ -1320, 1440 ], "parameters": {}, "typeVersion": 1, "notes": "This executeWorkflowTrigger node performs automated tasks as part of the workflow." }, { "id": "82d7182e-3aca-4407-8faa-3704429974dc", "name": "Set folder-paths for from and to", "type": "n8n-nodes-base.set", "position": [ -280, 880 ], "parameters": { "fields": { "values": [ { "name": "from", "stringValue": "={{ $json.from }}" }, { "name": "to", "stringValue": "={{ $json.to }}" } ] }, "options": {} }, "typeVersion": 3.2, "notes": "This set node performs automated tasks as part of the workflow." }, { "id": "e9edad54-d5f2-481e-b5be-b43a15b74233", "name": "Create to folder if necessary", "type": "n8n-nodes-base.nextCloud", "onError": "continueRegularOutput", "position": [ -40, 880 ], "parameters": { "path": "={{ $json.to }}", "resource": "folder" }, "credentials": { "nextCloudApi": { "id": "kd8dB6PqsIKQhB6O", "name": "NextCloud account" } }, "typeVersion": 1, "notes": "This nextCloud node performs automated tasks as part of the workflow." }, { "id": "4283f069-ea26-499d-928c-5f0f3898cdc4", "name": "Get all folders/files in from-folder", "type": "n8n-nodes-base.nextCloud", "position": [ 240, 880 ], "parameters": { "path": "={{ $('Set folder-paths for from and to').item.json.from }}", "resource": "folder", "operation": "list" }, "credentials": { "nextCloudApi": { "id": "kd8dB6PqsIKQhB6O", "name": "NextCloud account" } }, "typeVersion": 1, "notes": "This nextCloud node performs automated tasks as part of the workflow." }, { "id": "06c77d03-d79b-4435-9f7f-eef919b7b6af", "name": "Loop over files and folders", "type": "n8n-nodes-base.splitInBatches", "position": [ 660, 880 ], "parameters": { "options": {} }, "typeVersion": 3, "notes": "This splitInBatches node performs automated tasks as part of the workflow." }, { "id": "56cc28ea-d934-4d9c-9e28-968c2e1fa4da", "name": "Consolidate all files and folders found", "type": "n8n-nodes-base.noOp", "position": [ 2000, 760 ], "parameters": {}, "typeVersion": 1, "notes": "This noOp node performs automated tasks as part of the workflow." }, { "id": "57883a8f-7989-4706-808a-595376ebaf47", "name": "Create subfolder in to-folder", "type": "n8n-nodes-base.nextCloud", "onError": "continueRegularOutput", "position": [ 1440, 900 ], "parameters": { "path": "={{$('Set new path for subfolder').item.json.to }}", "resource": "folder" }, "credentials": { "nextCloudApi": { "id": "kd8dB6PqsIKQhB6O", "name": "NextCloud account" } }, "typeVersion": 1, "notes": "This nextCloud node performs automated tasks as part of the workflow." }, { "id": "0a173b88-53c5-44b1-ae04-f68b343025ce", "name": "Get all folders/files in found subfolder", "type": "n8n-nodes-base.nextCloud", "position": [ 1680, 900 ], "parameters": { "path": "={{$('Set new path for subfolder').item.json.from }}", "resource": "folder", "operation": "list" }, "credentials": { "nextCloudApi": { "id": "kd8dB6PqsIKQhB6O", "name": "NextCloud account" } }, "typeVersion": 1, "notes": "This nextCloud node performs automated tasks as part of the workflow." }, { "id": "3c17b67c-e815-4e27-9b63-19346cb8b966", "name": "Whether there is are more files or subfolders found", "type": "n8n-nodes-base.if", "position": [ 2200, 880 ], "parameters": { "conditions": { "boolean": [ { "value1": "={{$node[\"Loop over files and folders\"].context[\"noItemsLeft\"]}}", "value2": true } ] } }, "typeVersion": 1, "notes": "This if node performs automated tasks as part of the workflow." }, { "id": "94c4e926-eb92-4b10-8d35-2b3483cc4819", "name": "Consolidate all found files", "type": "n8n-nodes-base.code", "position": [ 2580, 860 ], "parameters": { "jsCode": "let results = [],\n i = 0;\n\ndo {\n try {\n results = results.concat($(\"Consolidate all files and folders found\").all(0, i));\n } catch (error) {\n return results;\n }\n i++;\n} while (true);" }, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow." }, { "id": "b40e30ff-793c-46e6-b5a0-5498ee27a3c9", "name": "Loop Over all files", "type": "n8n-nodes-base.splitInBatches", "position": [ 3300, 860 ], "parameters": { "options": {} }, "typeVersion": 3, "notes": "This splitInBatches node performs automated tasks as part of the workflow." }, { "id": "034c66f7-c184-438d-96de-1d20f8f7adc5", "name": "Move file to destination", "type": "n8n-nodes-base.nextCloud", "position": [ 3660, 940 ], "parameters": { "path": "={{ decodeURIComponent($json.path) }}", "toPath": "={{ decodeURIComponent($json.newPath) }}", "operation": "move" }, "credentials": { "nextCloudApi": { "id": "kd8dB6PqsIKQhB6O", "name": "NextCloud account" } }, "typeVersion": 1, "notes": "This nextCloud node performs automated tasks as part of the workflow." }, { "id": "34c8521f-cb17-479f-842b-38cbb5970403", "name": "Delete from-folder", "type": "n8n-nodes-base.nextCloud", "onError": "continueRegularOutput", "position": [ 4200, 840 ], "parameters": { "path": "={{ $('Set folder-paths for from and to').item.json.from }}", "resource": "folder", "operation": "delete" }, "credentials": { "nextCloudApi": { "id": "kd8dB6PqsIKQhB6O", "name": "NextCloud account" } }, "typeVersion": 1, "notes": "This nextCloud node performs automated tasks as part of the workflow." }, { "id": "eeda26a3-f5e6-4e6d-aeca-ebe2dbc2cb9e", "name": "Set paths", "type": "n8n-nodes-base.set", "position": [ -780, 360 ], "parameters": { "fields": { "values": [ { "name": "from", "stringValue": "Old-Folder" }, { "name": "to", "stringValue": "Destination" } ] }, "options": {} }, "typeVersion": 3.2, "notes": "This set node performs automated tasks as part of the workflow." }, { "id": "ba2e352a-4911-470b-a3bb-f63e3470e228", "name": "Whether the request is valid", "type": "n8n-nodes-base.if", "position": [ -1100, 880 ], "parameters": { "conditions": { "boolean": [ { "value1": "={{ $json.hasOwnProperty('body') && $json.body.hasOwnProperty('to') && $json.body.hasOwnProperty('from')}}", "value2": true } ] } }, "typeVersion": 1, "notes": "This if node performs automated tasks as part of the workflow." }, { "id": "ed4ddbf1-becf-4944-abd4-0b4cdf6d3b85", "name": "Stop and Error: request not valid", "type": "n8n-nodes-base.stopAndError", "position": [ -760, 920 ], "parameters": { "errorMessage": "The Request is not valid!" }, "typeVersion": 1, "notes": "This stopAndError node performs automated tasks as part of the workflow." }, { "id": "2b5d67ac-983b-486d-99f1-e05995383878", "name": "Whether the request is valid1", "type": "n8n-nodes-base.if", "position": [ -1120, 1440 ], "parameters": { "conditions": { "boolean": [ { "value1": "={{ $json.hasOwnProperty('to') && $json.hasOwnProperty('from')}}", "value2": true } ] } }, "typeVersion": 1, "notes": "This if node performs automated tasks as part of the workflow." }, { "id": "b57309cf-2a69-4879-a7d4-5499f8278e3b", "name": "Stop and Error: request not valid1", "type": "n8n-nodes-base.stopAndError", "position": [ -760, 1480 ], "parameters": { "errorMessage": "The Request is not valid!" }, "typeVersion": 1, "notes": "This stopAndError node performs automated tasks as part of the workflow." }, { "id": "f109308f-0b48-4395-9f2d-c8b4e8d936d2", "name": "Sticky Note8", "type": "n8n-nodes-base.stickyNote", "position": [ -2440, 60 ], "parameters": { "width": 770.5015081009478, "height": 1247.9320267653952, "content": "# Template Description\n\n\n## Description:\nThis template facilitates the transfer of a folder, along with all its files and subfolders, within a Nextcloud instance. The Nextcloud user must have access to both the source and destination folders. While Nextcloud allows folder movement, complications may arise when dealing with external storage that has rate limits. This workflow ensures the individual transfer of each file to avoid exceeding rate limits, particularly useful for setups involving external storage with rate limitations.\n\n## How it works:\n\n- Identify all files and subfolders within the specified source folder.\n- Recursive search within subfolders for additional files.\n- Replicate the folder structure in the target folder.\n- Individually move each identified file to the corresponding location in the target folder.\n\n## Set up steps:\n\n- Set Nextcloud credentials for all Nextcloud nodes involved in the process.\n-Edit the trigger settings. Detailed instructions can be found within the respective trigger configuration.\n- Initiate the workflow to commence the folder transfer process.\n\n\n## Help\nIf you need assistance with applying this template, feel free to reach out to me. You can find additional information about me and my services here. => {{ $env.WEBHOOK_URL }}\n\nI have also produced a video where I explain the workflow and provide an example. You can find this video over here. {{ $env.WEBHOOK_URL }}\n\nCheers.\nNico Kowalczyk" }, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow." } ], "connections": { "c1e7754f-6efa-4967-9b8d-6c1bcdb55355": { "main": [ [ { "node": "error-handler-c1e7754f-6efa-4967-9b8d-6c1bcdb55355-c31c007b", "type": "main", "index": 0 } ], [ { "node": "error-handler-c1e7754f-6efa-4967-9b8d-6c1bcdb55355-f5f9a8c9", "type": "main", "index": 0 } ], [ { "node": "error-handler-c1e7754f-6efa-4967-9b8d-6c1bcdb55355-9cf40a63", "type": "main", "index": 0 } ], [ { "node": "error-handler-c1e7754f-6efa-4967-9b8d-6c1bcdb55355-84f1c7dd", "type": "main", "index": 0 } ], [ { "node": "error-handler-c1e7754f-6efa-4967-9b8d-6c1bcdb55355-5db045e5", "type": "main", "index": 0 } ], [ { "node": "error-handler-c1e7754f-6efa-4967-9b8d-6c1bcdb55355-831a9971", "type": "main", "index": 0 } ], [ { "node": "error-handler-c1e7754f-6efa-4967-9b8d-6c1bcdb55355-3204ab0b", "type": "main", "index": 0 } ], [ { "node": "error-handler-c1e7754f-6efa-4967-9b8d-6c1bcdb55355-aad8b20f", "type": "main", "index": 0 } ] ] } }, "name": "Manualtrigger Workflow", "settings": { "executionOrder": "v1", "saveManualExecutions": true, "callerPolicy": "workflowsFromSameOwner", "errorWorkflow": null, "timezone": "UTC", "executionTimeout": 3600, "maxExecutions": 1000, "retryOnFail": true, "retryCount": 3, "retryDelay": 1000 }, "description": "Automated workflow: Manualtrigger Workflow. This workflow integrates 11 different services: webhook, stickyNote, splitInBatches, code, noOp. It contains 33 nodes and follows best practices for error handling and security.", "tags": [ "automation", "n8n", "production-ready", "excellent", "optimized" ], "notes": "Excellent quality workflow: Manualtrigger Workflow. This workflow has been optimized for production use with comprehensive error handling, security, and documentation." }