This workflow follows the Execute Workflow Trigger → 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 →
{
"nodes": [
{
"parameters": {
"workflowInputs": {
"values": [
{
"name": "target_path"
},
{
"name": "root_folder_id"
},
{
"name": "root_path"
},
{
"name": "current_folder_id"
},
{
"name": "current_path"
}
]
}
},
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.1,
"position": [
5376,
2064
],
"id": "0e7d5f9e-d032-488a-b84e-20670364c8c8",
"name": "Trigger"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "is-recursion",
"leftValue": "={{ $json.current_folder_id }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "exists"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
5600,
2064
],
"id": "581b29d1-6b74-4e46-98a2-6834f1daf5c8",
"name": "Is Recursion?"
},
{
"parameters": {
"jsCode": "const targetPath = $('Trigger').item.json.target_path;\nconst rootPath = $('Trigger').item.json.root_path;\nconst rootFolderId = $('Trigger').item.json.root_folder_id;\n\n// Generate ancestor paths (longest first)\nconst paths = [];\nlet p = targetPath;\nwhile (p.length >= rootPath.length) {\n paths.push(p);\n if (p === rootPath) break;\n p = p.substring(0, p.lastIndexOf('/'));\n}\n\n// Pad to 4 paths (use root_path for unused slots)\nwhile (paths.length < 4) {\n paths.push(rootPath);\n}\n\nreturn [{\n json: {\n path1: paths[0],\n path2: paths[1],\n path3: paths[2],\n path4: paths[3],\n target_path: targetPath,\n root_path: rootPath,\n root_folder_id: rootFolderId\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5824,
2064
],
"id": "68c4b519-c750-4c7c-9a78-a490310756b2",
"name": "Generate Ancestor Paths"
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "16vuiDJEZz3EOMN-8z7CF_1ENDWzjfORyykFNxqhGOWE",
"mode": "list",
"cachedResultName": "PathToIDLookup",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/16vuiDJEZz3EOMN-8z7CF_1ENDWzjfORyykFNxqhGOWE/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/16vuiDJEZz3EOMN-8z7CF_1ENDWzjfORyykFNxqhGOWE/edit#gid=0"
},
"filtersUI": {
"values": [
{
"lookupColumn": "path",
"lookupValue": "={{ $json.path1 }}"
},
{
"lookupColumn": "path",
"lookupValue": "={{ $json.path2 }}"
},
{
"lookupColumn": "path",
"lookupValue": "={{ $json.path3 }}"
},
{
"lookupColumn": "path",
"lookupValue": "={{ $json.path4 }}"
}
]
},
"combineFilters": "OR",
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
6048,
2064
],
"id": "858d6ce1-c090-4995-8545-e606f129b296",
"name": "Cache OR Query",
"alwaysOutputData": true,
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Get cache results from OR query\nconst cacheResults = $input.all();\nconst meta = $('Generate Ancestor Paths').item.json;\nconst targetPath = meta.target_path;\nconst rootPath = meta.root_path;\nconst rootFolderId = meta.root_folder_id;\n\nlet currentPath = rootPath;\nlet currentFolderId = rootFolderId;\n\n// Find deepest cached path that is a prefix of the target\nlet deepestCached = null;\nfor (const item of cacheResults) {\n if (item.json.path && item.json.folder_id && targetPath.startsWith(item.json.path)) {\n if (!deepestCached || item.json.path.length > deepestCached.path.length) {\n deepestCached = { path: item.json.path, folder_id: item.json.folder_id };\n }\n }\n}\n\n// If target is cached, we're done!\nif (deepestCached && deepestCached.path === targetPath) {\n return [{ json: {\n status: 'done',\n folder_id: deepestCached.folder_id,\n folder_path: targetPath\n }}];\n}\n\n// Use deepest cached as starting point\nif (deepestCached) {\n currentPath = deepestCached.path;\n currentFolderId = deepestCached.folder_id;\n}\n\n// Calculate next segment to traverse\nconst remaining = targetPath.substring(currentPath.length);\nconst segments = remaining.split('/').filter(s => s);\nconst nextSegment = segments[0];\nconst nextPath = currentPath + '/' + nextSegment;\n\nreturn [{\n json: {\n status: 'traverse',\n target_path: targetPath,\n root_folder_id: rootFolderId,\n root_path: rootPath,\n current_folder_id: currentFolderId,\n current_path: currentPath,\n next_segment: nextSegment,\n next_path: nextPath\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
6272,
2064
],
"id": "73f44d44-16ca-420e-b798-c2aeee75eeb5",
"name": "Find Deepest Cached"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "is-done",
"leftValue": "={{ $json.status }}",
"rightValue": "done",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "done"
}
]
},
"options": {
"fallbackOutput": "extra",
"allMatchingOutputs": false
}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
6496,
2064
],
"id": "60b3c304-6657-4527-a468-31ae87335629",
"name": "Status Check"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "out-id",
"name": "folder_id",
"value": "={{ $json.folder_id }}",
"type": "string"
},
{
"id": "out-path",
"name": "folder_path",
"value": "={{ $json.folder_path }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
6720,
2064
],
"id": "96c584e5-c327-4037-91e0-9839bc2243de",
"name": "Return (Cache Hit)"
},
{
"parameters": {
"jsCode": "// MERGE POINT: receives from TWO paths\n// Path 1 (recursion): from Is Recursion? (no 'status' field)\n// Path 2 (first call): from Status Check (has 'status' field)\n\nconst input = $input.item.json;\n\nconst targetPath = input.target_path;\nconst rootPath = input.root_path;\nconst rootFolderId = input.root_folder_id;\n\nlet currentPath, currentFolderId, nextSegment, nextPath;\n\nif (input.status) {\n // First call path - has status='traverse', already calculated\n currentPath = input.current_path;\n currentFolderId = input.current_folder_id;\n nextSegment = input.next_segment;\n nextPath = input.next_path;\n} else {\n // Recursion path - no status, need to calculate next segment\n currentPath = input.current_path;\n currentFolderId = input.current_folder_id;\n\n const remaining = targetPath.substring(currentPath.length);\n const segments = remaining.split('/').filter(s => s);\n nextSegment = segments[0];\n nextPath = currentPath + '/' + nextSegment;\n}\n\nreturn [{\n json: {\n target_path: targetPath,\n root_folder_id: rootFolderId,\n root_path: rootPath,\n current_folder_id: currentFolderId,\n current_path: currentPath,\n next_segment: nextSegment,\n next_path: nextPath\n }\n}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5376,
2288
],
"id": "6ed5e135-af08-41a6-bb4a-409aff03e247",
"name": "Calculate Next Segment"
},
{
"parameters": {
"resource": "fileFolder",
"searchMethod": "query",
"queryString": "=name = '{{ $json.next_segment }}' and mimeType = 'application/vnd.google-apps.folder' and '{{ $json.current_folder_id }}' in parents and trashed = false",
"limit": 1,
"filter": {
"folderId": {
"__rl": true,
"value": "={{ $json.current_folder_id }}",
"mode": "id"
},
"whatToSearch": "folders"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
5600,
2288
],
"id": "1f08dd40-382f-4109-b1fe-61d13299fa85",
"name": "Google Drive: Find Folder",
"alwaysOutputData": true,
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "folder-found",
"leftValue": "={{ $json.id }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "exists"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
5824,
2288
],
"id": "8f942b44-ce38-48b7-9045-038ec3dc7f5c",
"name": "Folder Found?"
},
{
"parameters": {
"resource": "folder",
"name": "={{ $('Calculate Next Segment').item.json.next_segment }}",
"driveId": {
"__rl": true,
"value": "MyDrive",
"mode": "list",
"cachedResultName": "My Drive"
},
"folderId": {
"__rl": true,
"value": "={{ $('Calculate Next Segment').item.json.current_folder_id }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
6048,
2464
],
"id": "4b4d96ab-aa74-431d-abea-656470542859",
"name": "Create Folder",
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Prepare data for caching and next steps\nconst driveResult = $input.item.json;\nconst meta = $('Calculate Next Segment').item.json;\n\nreturn [{\n json: {\n found_folder_id: driveResult.id,\n found_folder_name: driveResult.name,\n next_path: meta.next_path,\n target_path: meta.target_path,\n root_folder_id: meta.root_folder_id,\n root_path: meta.root_path\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
6048,
2288
],
"id": "db3c2790-96bf-4812-bc6f-e142527874e8",
"name": "Prepare Cache Data"
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "16vuiDJEZz3EOMN-8z7CF_1ENDWzjfORyykFNxqhGOWE",
"mode": "list",
"cachedResultName": "PathToIDLookup",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/16vuiDJEZz3EOMN-8z7CF_1ENDWzjfORyykFNxqhGOWE/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/16vuiDJEZz3EOMN-8z7CF_1ENDWzjfORyykFNxqhGOWE/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"path": "={{ $json.next_path }}",
"folder_id": "={{ $json.found_folder_id }}"
},
"matchingColumns": [],
"schema": [
{
"id": "path",
"displayName": "path",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "folder_id",
"displayName": "folder_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
]
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
6256,
2288
],
"id": "98b97f9c-4996-4fbd-8f98-65ab61584a95",
"name": "Cache New Path",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "is-target",
"leftValue": "={{ $('Prepare Cache Data').item.json.next_path }}",
"rightValue": "={{ $('Prepare Cache Data').item.json.target_path }}",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
6480,
2288
],
"id": "300756cb-2d98-4ace-ae55-3da8d3bd51e4",
"name": "Reached Target?"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "out-id",
"name": "folder_id",
"value": "={{ $('Prepare Cache Data').item.json.found_folder_id }}",
"type": "string"
},
{
"id": "out-path",
"name": "folder_path",
"value": "={{ $('Prepare Cache Data').item.json.target_path }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
6704,
2272
],
"id": "2294e345-3eb2-4487-b0f0-ed276ca0b342",
"name": "Return (Found)"
},
{
"parameters": {
"workflowId": {
"__rl": true,
"value": "vFnk7s9sqVnrt4hC",
"mode": "list",
"cachedResultUrl": "/workflow/vFnk7s9sqVnrt4hC",
"cachedResultName": "gdrive-recursion"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"target_path": "={{ $('Prepare Cache Data').item.json.target_path }}",
"current_path": "={{ $('Prepare Cache Data').item.json.next_path }}",
"current_folder_id": "={{ $('Prepare Cache Data').item.json.found_folder_id }}",
"root_path": "={{ $('Prepare Cache Data').item.json.root_path }}",
"root_folder_id": "={{ $('Prepare Cache Data').item.json.root_folder_id }}"
},
"matchingColumns": [],
"schema": [
{
"id": "target_path",
"displayName": "target_path",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "root_folder_id",
"displayName": "root_folder_id",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "root_path",
"displayName": "root_path",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "current_folder_id",
"displayName": "current_folder_id",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "current_path",
"displayName": "current_path",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": true
},
"options": {
"waitForSubWorkflow": true
}
},
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
6704,
2448
],
"id": "c5eb54a7-d424-40fd-b167-99c8f9fc9db3",
"name": "Recurse (Call Self)"
},
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
5152,
1872
],
"id": "de899d9c-9048-492a-a5dc-b1960c362c34",
"name": "Manual Test Trigger"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "test-target",
"name": "target_path",
"value": "/Accounting/2025/11_November/Expense",
"type": "string"
},
{
"id": "test-root-id",
"name": "root_folder_id",
"value": "1tiI-FHH-yeiWjku8jU5dClDRFERIe4gS",
"type": "string"
},
{
"id": "test-root-path",
"name": "root_path",
"value": "/Accounting",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
5376,
1872
],
"id": "b5046fde-e201-4995-8201-a9f404f35066",
"name": "Test Data"
},
{
"parameters": {
"content": "# Folder ID Lookup (Self-Recursive)\n\n**First call:** Cache OR query \u2192 Find deepest cached \u2192 Traverse\n**Recursion:** Skip cache \u2192 Traverse directly\n\n## Flow\n1. Check if recursion (current_folder_id exists?)\n2. First call: Generate paths \u2192 OR query \u2192 Find deepest\n3. Both: Calculate next segment \u2192 Drive query \u2192 Cache \u2192 Done/Recurse",
"height": 584,
"width": 376
},
"type": "n8n-nodes-base.stickyNote",
"position": [
4944,
2016
],
"typeVersion": 1,
"id": "3d56ba52-6414-4095-8aea-ab3578125255",
"name": "Sticky Note"
}
],
"connections": {
"Trigger": {
"main": [
[
{
"node": "Is Recursion?",
"type": "main",
"index": 0
}
]
]
},
"Is Recursion?": {
"main": [
[
{
"node": "Calculate Next Segment",
"type": "main",
"index": 0
}
],
[
{
"node": "Generate Ancestor Paths",
"type": "main",
"index": 0
}
]
]
},
"Generate Ancestor Paths": {
"main": [
[
{
"node": "Cache OR Query",
"type": "main",
"index": 0
}
]
]
},
"Cache OR Query": {
"main": [
[
{
"node": "Find Deepest Cached",
"type": "main",
"index": 0
}
]
]
},
"Find Deepest Cached": {
"main": [
[
{
"node": "Status Check",
"type": "main",
"index": 0
}
]
]
},
"Status Check": {
"main": [
[
{
"node": "Return (Cache Hit)",
"type": "main",
"index": 0
}
],
[
{
"node": "Calculate Next Segment",
"type": "main",
"index": 0
}
]
]
},
"Calculate Next Segment": {
"main": [
[
{
"node": "Google Drive: Find Folder",
"type": "main",
"index": 0
}
]
]
},
"Google Drive: Find Folder": {
"main": [
[
{
"node": "Folder Found?",
"type": "main",
"index": 0
}
]
]
},
"Folder Found?": {
"main": [
[
{
"node": "Prepare Cache Data",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Folder",
"type": "main",
"index": 0
}
]
]
},
"Create Folder": {
"main": [
[
{
"node": "Prepare Cache Data",
"type": "main",
"index": 0
}
]
]
},
"Prepare Cache Data": {
"main": [
[
{
"node": "Cache New Path",
"type": "main",
"index": 0
}
]
]
},
"Cache New Path": {
"main": [
[
{
"node": "Reached Target?",
"type": "main",
"index": 0
}
]
]
},
"Reached Target?": {
"main": [
[
{
"node": "Return (Found)",
"type": "main",
"index": 0
}
],
[
{
"node": "Recurse (Call Self)",
"type": "main",
"index": 0
}
]
]
},
"Manual Test Trigger": {
"main": [
[
{
"node": "Test Data",
"type": "main",
"index": 0
}
]
]
},
"Test Data": {
"main": [
[
{
"node": "Is Recursion?",
"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.
googleDriveOAuth2ApigoogleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Gdrive-Recursion. Uses executeWorkflowTrigger, googleSheets, googleDrive. Event-driven trigger; 19 nodes.
Source: https://github.com/runfish5/micro-services/blob/main/projects/n8n/shared/gdrive-recursion.json — 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.
Intelligent URL Validation - Validates PDF URLs before attempting download, extracting filenames from URLs and generating fallback names when needed, preventing wasted processing time Binary File Hand
LINE Image Handler. Uses executeWorkflowTrigger, googleSheets, httpRequest, googleDrive. Event-driven trigger; 17 nodes.
__Create-doc. Uses executeWorkflowTrigger, googleDrive, googleSheets. Event-driven trigger; 5 nodes.
Smart-Folder2Table. Uses executeWorkflowTrigger, httpRequest, chainLlm, lmChatGroq. Event-driven trigger; 26 nodes.
PCN. Uses googleSheets, httpRequest, @n-octo-n/n8n-nodes-json-database, itemLists. Event-driven trigger; 60 nodes.