Workflow: Splitinbatches Workflow
Integrations: aggregate, code, filter, formTrigger, graphql, httpRequest, if, linear, noOp, notion, respondToWebhook, set, splitInBatches, stickyNote

{"meta": {"instanceId": "workflow-a99180af", "versionId": "1.0.0", "createdAt": "2025-09-29T07:07:42.506870", "updatedAt": "2025-09-29T07:07:42.506896", "owner": "n8n-user", "license": "MIT", "category": "automation", "status": "active", "priority": "high", "environment": "production"}, "nodes": [{"id": "7d11aa76-c7bf-4aa3-9f94-fb2231f5055b", "name": "Loop Over Items", "type": "n8n-nodes-base.splitInBatches", "position": [-1460, 2080], "parameters": {"options": {}}, "typeVersion": 3, "notes": "This splitInBatches node performs automated tasks as part of the workflow."}, {"id": "bea6febe-077f-4b90-887c-9c82954ef5d9", "name": "Fetch Linear team details", "type": "n8n-nodes-base.graphql", "position": [-1220, 1760], "parameters": {"query": "=query GetTeamsAndProjects {\n  teams(filter: {name: {contains: \"{{ $json['Linear team name'] }}\"}}) {\n    nodes {\n      id\n      name\n      members {\n        nodes {\n          id\n          name\n          email\n        }\n      }\n      projects {\n        nodes {\n          id\n          name\n          description\n        }\n      }\n    }\n  }\n}\n", "endpoint": "{{ $env.API_BASE_URL }}", "requestMethod": "GET", "authentication": "{{ $credentials.headerAuth }}"}, "credentials": {"httpHeaderAuth": {"id": "zYILrk4RKFqdP66s", "name": "[Omar] Notion credentials for GraphQL API"}}, "executeOnce": true, "typeVersion": 1, "continueOnFail": true, "notes": "This graphql node performs automated tasks as part of the workflow."}, {"id": "27a2111b-716b-4150-af92-ab90d7e83642", "name": "Get issue contents", "type": "n8n-nodes-base.notion", "position": [-1220, 2340], "parameters": {"blockId": {"__rl": true, "mode": "id", "value": "={{ $('Set assignee and title').item.json.id }}"}, "resource": "block", "operation": "getAll", "returnAll": true, "simplifyOutput": false, "fetchNestedBlocks": true}, "credentials": {"notionApi": {"id": "{{ $credentials.notionApi.id }}", "name": "Notion david-internal"}}, "typeVersion": 2.1, "alwaysOutputData": true, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "96bde9b1-b84e-445a-b90c-6dd4031076aa", "name": "Aggregate", "type": "n8n-nodes-base.aggregate", "position": [-560, 2340], "parameters": {"options": {}, "fieldsToAggregate": {"fieldToAggregate": [{"fieldToAggregate": "markdown"}]}}, "typeVersion": 1, "notes": "This aggregate node performs automated tasks as part of the workflow."}, {"id": "45c63a2d-d457-4882-ac99-4b8e4a63ae43", "name": "Prepare issue data", "type": "n8n-nodes-base.set", "position": [-1220, 2620], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "e1b44489-ee32-4da4-816e-f56d640a9731", "name": "title", "type": "string", "value": "={{ $if($('Set assignee and title').item.json.title.length <= 70, $('Set assignee and title').item.json.title, $('Shorten title').item.json.message.content) }}"}, {"id": "f3fab4f6-8ea3-4b93-91ea-ec08c2d9eded", "name": "description", "type": "string", "value": "=_Issue created automatically from a [Notion block]({{ $('Set page URL').last().json.page_url + '?pvs=4#' + $('Loop Over Items').item.json.id.replaceAll('-', '') }})_ {{ $if($('Set assignee and title').item.json.assignee_fragment && !$('Set assignee and title').item.json.assignee, \"\\nAssignee '\" + $('Set assignee and title').item.json.assignee_fragment + \"' not found\", '') }}\n\n{{ $json.markdown?.join('\\n') }}"}]}}, "typeVersion": 3.3, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "ce619c7d-8363-48ec-86c8-a1f16097eb3e", "name": "Create linear issue", "type": "n8n-nodes-base.linear", "position": [-1000, 2620], "parameters": {"title": "={{ $json.title }}", "teamId": "={{ $('Set team ID').item.json.team_id }}", "additionalFields": {"assigneeId": "={{ $('Set assignee and title').item.json.assignee.id }}", "description": "={{ $json.description }}"}}, "credentials": {"linearApi": {"id": "{{ $credentials.linearApi.id }}", "name": "Linear account (David)"}}, "typeVersion": 1, "notes": "This linear node performs automated tasks as part of the workflow."}, {"id": "cc365fa1-a2cc-430c-8cb4-5d708b7a66b9", "name": "Set assignee and title", "type": "n8n-nodes-base.code", "position": [-1220, 2080], "parameters": {"mode": "runOnceForEachItem", "jsCode": "// Set the title and the assignee based on the first line of the item\n\nlet firstLine = $json[$json.type].text.reduce((s, o) => {\n  return s + o.text.content\n}, \"\")\nconsole.log('firstLin', firstLine)\nconst regex = /^(\\[[^\\]]*\\]\\s)?(.+)$/;\nconst match = firstLine.match(regex);\nconsole.log('match', match)\n\nif (match) {\n  // If the first part is not present, match[1] will be undefined\n  item.json.assignee_fragment = match[1]?.slice(1, -2) || null;\n  item.json.title = match[2];\n} else {\n  item.json.title = firstLine;\n  item.json.assignee_fragment = null;\n}\n\n// Set the new title in Notion format\n// $url will be set later, once we have it\nconst prefix_link = [\n  {\"text\":{\"content\":\"[\"}},\n  {\"text\":{\"content\":\"In Linear\", \"link\":{\"url\": \"$url\"} }},\n  {\"text\":{\"content\":\"] \"}}\n]\nitem.json.new_content = {\n  \"rich_text\": [...prefix_link, ...item.json.to_do.text]\n}\n\n// Find a matching assignee\nconst members = $('Fetch Linear team details').item.json.data.teams.nodes[0].members.nodes\nconsole.log('people', members)\nconsole.log('fragment', item.json.assignee_fragment)\nconst matching_people = members.filter(p => \n  p.name.toLowerCase().startsWith(item.json.assignee_fragment?.toLowerCase())\n)\nconsole.log('mpeople', matching_people)\nif (matching_people.length > 0) {\n  item.json.assignee = matching_people[0]\n}\n\nitem.pairedItem = 0\n\nreturn item"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "15d9c526-95f3-4209-9a9f-a0ba4c09a67e", "name": "Team missing?", "type": "n8n-nodes-base.if", "position": [-1000, 1760], "parameters": {"options": {}, "conditions": {"options": {"leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "047fbe62-ebab-44ab-89b1-232f5f15874d", "operator": {"type": "boolean", "operation": "true", "singleValue": true}, "leftValue": "={{ $json.data.teams?.nodes?.length < 1 }}", "rightValue": ""}]}}, "typeVersion": 2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "491a1176-18a7-442a-8f64-a8c946ac25dc", "name": "Set page URL", "type": "n8n-nodes-base.set", "position": [-1000, 2340], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "0b0ced59-14c9-43e9-a5ee-f4b1862fccd6", "name": "page_url", "type": "string", "value": "={{ $('n8n Form Trigger').item.json['Notion block URL'].substr(0, $('n8n Form Trigger').item.json['Notion block URL'].indexOf('?')) || $('n8n Form Trigger').item.json['Notion block URL'] }}"}, {"id": "3df2b2e6-38ca-4fb6-b00c-e3a6ceb3f9b3", "name": "root_content", "type": "object", "value": "={{ $('Set assignee and title').item.json[$('Set assignee and title').item.json.type] }}"}, {"id": "41a18b43-49fd-45a5-850d-55b9f08f9b93", "name": "root_id", "type": "string", "value": "={{ $('Set assignee and title').item.json.id }}"}]}, "includeOtherFields": true}, "typeVersion": 3.3, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "bd107990-b009-4b81-8fee-a80c9ab09b4c", "name": "Set team ID", "type": "n8n-nodes-base.set", "position": [-740, 1760], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "b22a4a67-67b5-415a-ab38-4d7f781e8b7e", "name": "team_id", "type": "string", "value": "={{ $json.data.teams.nodes[0].id }}"}]}}, "typeVersion": 3.3, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "010105b6-38a2-4859-9e5d-b9624addeacc", "name": "Add link to Notion block", "type": "n8n-nodes-base.httpRequest", "position": [-560, 2620], "parameters": {"url": "{{ $env.BASE_URL }}", "method": "PATCH", "options": {}, "jsonBody": "={\n  \"to_do\":\n    {{ JSON.stringify($('Set assignee and title').item.json.new_content).replace('$url', $json.data.issue.url) }}\n}", "sendBody": true, "sendHeaders": true, "specifyBody": "json", "authentication": "{{ $credentials.predefinedCredentialType }}", "headerParameters": {"parameters": [{"name": "Notion-Version", "value": "2022-06-28"}]}, "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"notionApi": {"id": "{{ $credentials.notionApi.id }}", "name": "Notion david-internal"}}, "typeVersion": 4.1, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "bb88f7df-deef-4c9d-b8af-3e59bf0e0b7d", "name": "Get issue URL", "type": "n8n-nodes-base.graphql", "position": [-780, 2620], "parameters": {"query": "=query IssueDetails {\n  issue(id: \"{{ $json.id }}\") {\n    url\n  }\n}", "endpoint": "{{ $env.API_BASE_URL }}", "requestMethod": "GET", "authentication": "{{ $credentials.headerAuth }}"}, "credentials": {"httpHeaderAuth": {"id": "zYILrk4RKFqdP66s", "name": "[Omar] Notion credentials for GraphQL API"}}, "executeOnce": true, "typeVersion": 1, "continueOnFail": true, "notes": "This graphql node performs automated tasks as part of the workflow."}, {"id": "00736596-08ee-4ff6-b907-bd454dd406d9", "name": "Shorten title", "type": "n8n-nodes-base.noOp", "position": [-1000, 2080], "parameters": {"modelId": {"__rl": true, "mode": "list", "value": "gpt-4", "cachedResultName": "GPT-4"}, "options": {}, "messages": {"values": [{"content": "=Make the following text more concise, so that it's max 150 chars long. If it's already less than 70 chars long, just return the original text. Do not return anything else other than the text.\n\nTEXT:\n{{ $json.title }}"}]}}, "credentials": {"openAiApi": {"id": "VQtv7frm7eLiEDnd", "name": "OpenAi account 7"}}, "typeVersion": 1, "notes": "This openAi node performs automated tasks as part of the workflow."}, {"id": "729928a2-8a5e-4b25-82be-ba1916b9953f", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [-1260, 2020], "parameters": {"color": 7, "width": 877.8549621677266, "height": 214.7985362687051, "content": "### Figure out issue assignee and title (shortening if necessary)"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "86d3b72f-f235-4b37-877f-c8ab1f39ba30", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [-1260, 2280], "parameters": {"color": 7, "width": 877.8549621677266, "height": 216.9904777194533, "content": "### Compose issue description"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "01c06352-76fa-4d63-b37b-2a0239d81302", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [-1260, 2560], "parameters": {"color": 7, "width": 877.8549621677266, "height": 216.9904777194533, "content": "### Create issue and add link to it in Notion"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "1f152f08-0c5a-4806-bd85-7e9a5e386fe4", "name": "Sticky Note5", "type": "n8n-nodes-base.stickyNote", "position": [-1260, 1500], "parameters": {"color": 7, "width": 1164.99929221574, "height": 442.760447146518, "content": "### Get the issues to create from Notion (and load Linear team details)"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "839d1815-419d-4acb-8668-94f1cbe45f1f", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [-1780, 1700], "parameters": {"height": 278.9250421966361, "content": "# Try me out\n1. In the form trigger node, enter the names of your Linear team(s) to display on the form \n2. Make sure your Notion page is formatted according to the [spec]({{ $env.WEBHOOK_URL }} and shared with your Notion integration\n2. Click the 'test workflow' button below"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "f9bc6e67-a9f0-4c9b-a9b3-fdea1fd9de3e", "name": "Unimported, unchecked to_do blocks only", "type": "n8n-nodes-base.filter", "position": [-220, 1760], "parameters": {"options": {}, "conditions": {"options": {"leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "d7e85c09-8548-4fc8-a8a9-636e4529e9d9", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $json.type }}", "rightValue": "to_do"}, {"id": "13fb565d-8951-4c89-9684-85c357459794", "operator": {"type": "boolean", "operation": "true", "singleValue": true}, "leftValue": "={{ !$json.to_do.text.reduce((s, o) => s + o.plain_text, \"\").startsWith('[In Linear]') }}", "rightValue": ""}, {"id": "0a9c8e94-11ec-4317-8de5-f22862555b78", "operator": {"type": "boolean", "operation": "false", "singleValue": true}, "leftValue": "={{ $json.to_do.checked }}", "rightValue": ""}]}}, "typeVersion": 2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "186a4272-4550-441e-9ef2-66de2dac5b8a", "name": "n8n Form Trigger", "type": "n8n-nodes-base.formTrigger", "position": [-1460, 1760], "webhookId": "5a631d63-f899-4967-acad-69924674e96a", "parameters": {"path": "5a631d63-f899-4967-acad-69924674e96a", "formTitle": "Import Linear issues from Notion", "formFields": {"values": [{"fieldLabel": "Notion page URL", "requiredField": true}, {"fieldType": "dropdown", "fieldLabel": "Linear team name", "fieldOptions": {"values": [{"option": "AI"}, {"option": "Adore"}, {"option": "Payday"}, {"option": "NODES"}]}, "requiredField": true}]}, "responseMode": "responseNode", "formDescription": "More information on Notion formatting required here: {{ $env.WEBHOOK_URL }}"}, "typeVersion": 2, "notes": "This formTrigger node performs automated tasks as part of the workflow."}, {"id": "0fed5dbe-54e9-4bc3-8ab3-6175347ecce7", "name": "Get issues", "type": "n8n-nodes-base.notion", "onError": "continueErrorOutput", "position": [-500, 1760], "parameters": {"blockId": {"__rl": true, "mode": "url", "value": "={{ $('n8n Form Trigger').item.json['Notion page URL'] }}"}, "resource": "block", "operation": "getAll", "returnAll": true, "simplifyOutput": false}, "credentials": {"notionApi": {"id": "{{ $credentials.notionApi.id }}", "name": "Notion david-internal"}}, "typeVersion": 2.1, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "7cc756ad-55f3-4596-989a-df90d4c829c7", "name": "Convert contents to Markdown", "type": "n8n-nodes-base.code", "position": [-780, 2340], "parameters": {"jsCode": "function extractMarkdown(obj) {\n  console.log('obj', obj.text)\n  return obj.text.reduce((s, o) => {\n    if(o.text?.link) {\n      return s + '[' + o.text.content + '](' + o.text.link?.url + ')'\n    }\n    return s + o.text.content\n  }, \"\")\n}\n\n\nconst indent = \"    \"; // Four spaces\nlet parent_ids = [$input.all()[0].json.root_id]\n\nfor(item of $input.all()){\n\n  // Generate the markdown\n\n  if(item.json.type) {\n  \n    const type = item.json.type\n    \n    if(type == 'bulleted_list_item' || type == 'toggle') {\n      item.json.markdown = '* ' + extractMarkdown(item.json[type])\n    } else if(type == 'numbered_list_item') {\n      item.json.markdown = '1. ' + extractMarkdown(item.json[type])\n    } else if(type == 'to_do') {\n      item.json.markdown = '+ [ ] ' + extractMarkdown(item.json[type])\n    } else if(type == 'image') {\n      item.json.markdown = '![image]('+item.json.image[item.json.image.type].url+')'\n    } else if(type == 'video') {\n      item.json.markdown = '[\ud83c\udfac Video]('+$input.all()[0].json.page_url + '?pvs=4#' + item.json.id.replaceAll('-', '') +')'\n    } else {\n      item.json.markdown = extractMarkdown(item.json[type])\n    }\n  \n    // Figure out how much to indent it\n    // If parent ID is in list, remove everything after that ID\n    // If parent ID is not in list, add it\n    // If parent is the same, do nothing\n    const parent_id_index = parent_ids.indexOf(item.json.parent_id);\n  \n    // Check if the value is found\n    if (parent_id_index !== -1) {\n      // Remove all elements after the first occurrence\n      parent_ids.splice(parent_id_index + 1);\n    } else {\n      parent_ids.push(item.json.parent_id)\n    }\n  \n    // Indent the markdown\n    //if (type != \"image\") {\n      item.json.markdown = indent.repeat(parent_ids.length - 1) + item.json.markdown\n    //}\n  }\n}\n\n// On returning, add in the root block content at the beginning\nreturn [\n  ...[\n    {\n      \"json\": {\n        \"markdown\":\nextractMarkdown($input.all()[0].json.root_content)\n      },\n      \"pairedItem\": 0\n    }\n  ],\n  ...$input.all()\n]"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "2bbd43a2-aebe-4ee5-8682-d76791214168", "name": "Respond with error", "type": "n8n-nodes-base.respondToWebhook", "position": [-220, 1580], "parameters": {"options": {}, "respondWith": "json", "responseBody": "{\n  \"formSubmittedText\": \"Couldn't fetch page content from Notion. Is it shared with your Notion integration?\"\n}"}, "typeVersion": 1, "notes": "This respondToWebhook node performs automated tasks as part of the workflow."}, {"id": "c39912c4-d33f-4d79-9d5c-e71bd0f2517d", "name": "Respond with error1", "type": "n8n-nodes-base.respondToWebhook", "position": [-740, 1580], "parameters": {"options": {}, "respondWith": "json", "responseBody": "={\n  \"formSubmittedText\": \"Couldn't find the team called '\" + {{ $('n8n Form Trigger').item.json['Linear team name'] }} + \"'\"\n} "}, "typeVersion": 1, "notes": "This respondToWebhook node performs automated tasks as part of the workflow."}], "pinData": {}, "connections": {"010105b6-38a2-4859-9e5d-b9624addeacc": {"main": [[{"node": "error-handler-010105b6-38a2-4859-9e5d-b9624addeacc", "type": "main", "index": 0}], [{"node": "error-handler-010105b6-38a2-4859-9e5d-b9624addeacc-11f68014", "type": "main", "index": 0}], [{"node": "error-handler-010105b6-38a2-4859-9e5d-b9624addeacc-6082e924", "type": "main", "index": 0}], [{"node": "error-handler-010105b6-38a2-4859-9e5d-b9624addeacc-c9a04540", "type": "main", "index": 0}], [{"node": "error-handler-010105b6-38a2-4859-9e5d-b9624addeacc-fdb5e18f", "type": "main", "index": 0}], [{"node": "error-handler-010105b6-38a2-4859-9e5d-b9624addeacc-c3dbfad6", "type": "main", "index": 0}], [{"node": "error-handler-010105b6-38a2-4859-9e5d-b9624addeacc-15bf6aeb", "type": "main", "index": 0}], [{"node": "error-handler-010105b6-38a2-4859-9e5d-b9624addeacc-0ce9850c", "type": "main", "index": 0}], [{"node": "error-handler-010105b6-38a2-4859-9e5d-b9624addeacc-0b62e208", "type": "main", "index": 0}]]}, "2bbd43a2-aebe-4ee5-8682-d76791214168": {"main": [[{"node": "error-handler-2bbd43a2-aebe-4ee5-8682-d76791214168", "type": "main", "index": 0}], [{"node": "error-handler-2bbd43a2-aebe-4ee5-8682-d76791214168-aeae97c2", "type": "main", "index": 0}], [{"node": "error-handler-2bbd43a2-aebe-4ee5-8682-d76791214168-0301e30b", "type": "main", "index": 0}], [{"node": "error-handler-2bbd43a2-aebe-4ee5-8682-d76791214168-dcbaf1ba", "type": "main", "index": 0}], [{"node": "error-handler-2bbd43a2-aebe-4ee5-8682-d76791214168-1f83480b", "type": "main", "index": 0}], [{"node": "error-handler-2bbd43a2-aebe-4ee5-8682-d76791214168-86988a74", "type": "main", "index": 0}], [{"node": "error-handler-2bbd43a2-aebe-4ee5-8682-d76791214168-e074fca8", "type": "main", "index": 0}], [{"node": "error-handler-2bbd43a2-aebe-4ee5-8682-d76791214168-329df393", "type": "main", "index": 0}], [{"node": "error-handler-2bbd43a2-aebe-4ee5-8682-d76791214168-ef0b99fc", "type": "main", "index": 0}]]}, "c39912c4-d33f-4d79-9d5c-e71bd0f2517d": {"main": [[{"node": "error-handler-c39912c4-d33f-4d79-9d5c-e71bd0f2517d", "type": "main", "index": 0}], [{"node": "error-handler-c39912c4-d33f-4d79-9d5c-e71bd0f2517d-011b5810", "type": "main", "index": 0}], [{"node": "error-handler-c39912c4-d33f-4d79-9d5c-e71bd0f2517d-d2b0c929", "type": "main", "index": 0}], [{"node": "error-handler-c39912c4-d33f-4d79-9d5c-e71bd0f2517d-438965e6", "type": "main", "index": 0}], [{"node": "error-handler-c39912c4-d33f-4d79-9d5c-e71bd0f2517d-0c430158", "type": "main", "index": 0}], [{"node": "error-handler-c39912c4-d33f-4d79-9d5c-e71bd0f2517d-9add49a0", "type": "main", "index": 0}], [{"node": "error-handler-c39912c4-d33f-4d79-9d5c-e71bd0f2517d-75066191", "type": "main", "index": 0}], [{"node": "error-handler-c39912c4-d33f-4d79-9d5c-e71bd0f2517d-3d8ecb58", "type": "main", "index": 0}], [{"node": "error-handler-c39912c4-d33f-4d79-9d5c-e71bd0f2517d-54de567a", "type": "main", "index": 0}]]}, "00736596-08ee-4ff6-b907-bd454dd406d9": {"main": [[{"node": "error-handler-00736596-08ee-4ff6-b907-bd454dd406d9-99a1be23", "type": "main", "index": 0}]]}}, "name": "Splitinbatches 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: Splitinbatches Workflow. This workflow integrates 15 different services: stickyNote, httpRequest, filter, formTrigger, code. It contains 31 nodes and follows best practices for error handling and security.", "tags": ["automation", "n8n", "production-ready", "excellent", "optimized"], "notes": "Excellent quality workflow: Splitinbatches Workflow. This workflow has been optimized for production use with comprehensive error handling, security, and documentation."}