Workflow: Realtime Notion Todoist 2-way Sync Template
Integrations: aggregate, code, compareDatasets, crypto, executeWorkflow, executeWorkflowTrigger, executionData, filter, form, formTrigger, gmail, html, httpRequest, if, merge, noOp, notion, redis, respondToWebhook, scheduleTrigger, set, splitInBatches, splitOut, stickyNote, stopAndError, switch, todoist, wait, webhook

{"id": "k9abwUyVzl7OCsAl", "meta": {"instanceId": "workflow-e5bb739b", "versionId": "1.0.0", "createdAt": "2025-09-29T07:08:01.496367", "updatedAt": "2025-09-29T07:08:01.496381", "owner": "n8n-user", "license": "MIT", "category": "automation", "status": "active", "priority": "high", "environment": "production"}, "name": "Realtime Notion Todoist 2-way Sync Template", "tags": ["automation", "n8n", "production-ready", "excellent", "optimized"], "nodes": [{"id": "5e0488a3-270d-46b3-8b4b-f4ee459a3016", "name": "Get projects", "type": "n8n-nodes-base.httpRequest", "position": [-700, 260], "parameters": {"url": "{{ $env.API_BASE_URL }}", "options": {}, "authentication": "{{ $credentials.predefinedCredentialType }}", "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "typeVersion": 4.2, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "e52b0a6d-4697-4c42-91c9-6c786752877c", "name": "Get sections", "type": "n8n-nodes-base.httpRequest", "position": [180, 260], "parameters": {"url": "{{ $env.API_BASE_URL }}", "options": {}, "sendQuery": true, "authentication": "{{ $credentials.predefinedCredentialType }}", "queryParameters": {"parameters": [{"name": "project_id", "value": "={{ $json.project_id }}"}]}, "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "typeVersion": 4.2, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "8af0c04f-a46e-4fb8-ab5a-09952a3c1360", "name": "Get Notion Databases", "type": "n8n-nodes-base.notion", "position": [-1580, 260], "parameters": {"resource": "database", "operation": "getAll"}, "credentials": {"notionApi": {"id": "ObmaBA0dJss3JJPv", "name": "Notion (Test)"}}, "typeVersion": 2.2, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "09e43e00-6df6-4844-bda3-f5e8be0aed3a", "name": "Prep Dropdown", "type": "n8n-nodes-base.code", "position": [-1360, 260], "parameters": {"jsCode": "let dropDownValues = [];\n\nfor (const item of $input.all()) {\n  if (item.json.name == \"Inbox\") continue;\n  dropDownValues.push({\"option\": item.json.name});\n}\n\nreturn { \"options\": JSON.stringify(dropDownValues) };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "426872eb-228a-4b1c-b992-cf17893d29d2", "name": "Prep Dropdown1", "type": "n8n-nodes-base.code", "position": [-480, 260], "parameters": {"jsCode": "let dropDownValues = [];\n\nfor (const item of $input.all()) {\n  if (item.json.name == \"Inbox\") continue;\n  dropDownValues.push({\"option\": item.json.name});\n}\n\nreturn { \"options\": JSON.stringify(dropDownValues) };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "bebc6443-6187-4b97-82ff-45b6ce75cd7e", "name": "Generate config", "type": "n8n-nodes-base.code", "position": [400, 260], "parameters": {"jsCode": "let sections = [];\nfor (const item of $('Get sections').all()) {\n  sections.push({ id: item.json.id, name: item.json.name }); \n}\n\nreturn { json: \n{\n  \"database_id\": $('Get Notion Database ID').first().json.database_id,\n  \"project_id\": $('Get Todoist Project ID').first().json.project_id,\n  \"sections\": sections\n}  \n};"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "52692c9c-a490-4c2c-8df4-93870bf52ded", "name": "Choose Notion Database", "type": "n8n-nodes-base.form", "position": [-1140, 260], "webhookId": "971affb1-c55c-4025-9b4b-743c8f3fcfcf", "parameters": {"options": {"buttonLabel": "Continue"}, "defineForm": "json", "jsonOutput": "=[\n   {\n      \"fieldLabel\": \"Select Notion Database\",\n      \"fieldType\": \"dropdown\",\n      \"requiredField\": true,\n      \"fieldOptions\": {\n        \"values\": {{ $json.options }}\n      }\n   }\n]"}, "typeVersion": 1, "notes": "This form node performs automated tasks as part of the workflow."}, {"id": "7db73660-3f99-42b6-843d-ee4adf758441", "name": "Choose Todoist Project", "type": "n8n-nodes-base.form", "position": [-260, 260], "webhookId": "971affb1-c55c-4025-9b4b-743c8f3fcfcf", "parameters": {"options": {"buttonLabel": "Continue"}, "defineForm": "json", "jsonOutput": "=[\n   {\n      \"fieldLabel\": \"Select Todoist Project\",\n      \"fieldType\": \"dropdown\",\n      \"requiredField\": true,\n      \"fieldOptions\": {\n        \"values\": {{ $json.options }}\n      }\n   }\n]"}, "typeVersion": 1, "notes": "This form node performs automated tasks as part of the workflow."}, {"id": "3fe51a88-765f-469e-9846-191506b0a894", "name": "Verify security token", "type": "n8n-nodes-base.if", "position": [-580, 980], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "286fafb0-a200-4963-a19f-e63162c484b4", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $('OAuth redirect').first().json.query.state }}", "rightValue": "={{ $json.state }}"}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "7a040b65-5cc3-44d6-8bf6-c6bb7dfe5128", "name": "Generate security token", "type": "n8n-nodes-base.crypto", "position": [-800, 620], "parameters": {"action": "generate", "dataPropertyName": "state"}, "typeVersion": 1, "notes": "This crypto node performs automated tasks as part of the workflow."}, {"id": "7033d868-2d83-45bb-a48b-f4fd6eb14e0d", "name": "Store variables", "type": "n8n-nodes-base.code", "position": [-580, 620], "parameters": {"mode": "runOnceForEachItem", "jsCode": "const staticData = $getWorkflowStaticData('global');\nstaticData.clientID = $('Todoist Webhook Setup Helper').first().json['Client ID'];\nstaticData.clientSecret = $('Todoist Webhook Setup Helper').first().json['Client secret'];\nstaticData.state = $json.state;\n\nreturn $input.item;"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "d538d40a-599b-4255-a5e0-83d9b72f671e", "name": "Get variables", "type": "n8n-nodes-base.code", "position": [-800, 980], "parameters": {"mode": "runOnceForEachItem", "jsCode": "const staticData = $getWorkflowStaticData('global');\n$input.item.json.clientID = staticData.clientID;\n$input.item.json.clientSecret = staticData.clientSecret;\n$input.item.json.state = staticData.state;\n\nreturn $input.item;"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "7c895d16-e522-4b63-bee6-0cea3e3177dd", "name": "Redirect to Auth Page", "type": "n8n-nodes-base.form", "position": [-360, 620], "webhookId": "41b4bc95-2938-4a53-a051-7a3079001329", "parameters": {"operation": "completion", "redirectUrl": "{{ $env.BASE_URL }}", "respondWith": "redirect"}, "typeVersion": 1, "notes": "This form node performs automated tasks as part of the workflow."}, {"id": "b72c6f9f-f821-4859-b75d-192541ce0d34", "name": "OAuth redirect", "type": "n8n-nodes-base.webhook", "position": [-1020, 980], "webhookId": "7aee8b09-29e3-4e12-9b66-c6e8ab080bf7", "parameters": {"path": "7aee8b09-29e3-4e12-9b66-c6e8ab080bf7", "options": {}, "httpMethod": ["GET"], "responseMode": "responseNode", "multipleMethods": true}, "typeVersion": 2, "notes": "This webhook node performs automated tasks as part of the workflow."}, {"id": "d0adcb66-4817-44f9-ad7e-320cff86aefe", "name": "Exchange Tokens", "type": "n8n-nodes-base.httpRequest", "onError": "continueErrorOutput", "position": [-360, 880], "parameters": {"url": "{{ $env.WEBHOOK_URL }}", "method": "POST", "options": {}, "sendBody": true, "contentType": "form-urlencoded", "bodyParameters": {"parameters": [{"name": "client_id", "value": "={{ $json.clientID }}"}, {"name": "client_secret", "value": "={{ $json.clientSecret }}"}, {"name": "code", "value": "={{ $('OAuth redirect').first().json.query.code }}"}]}}, "typeVersion": 4.2, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "4fda8614-308c-4d66-ac7d-c2491ab5a86a", "name": "Respond with success", "type": "n8n-nodes-base.respondToWebhook", "position": [-140, 780], "parameters": {"options": {}, "respondWith": "text", "responseBody": "=Developer App activated successfully. The window can be closed now."}, "typeVersion": 1.1, "notes": "This respondToWebhook node performs automated tasks as part of the workflow."}, {"id": "347fb99f-861f-483a-bc39-7341fe41439f", "name": "Respond with error", "type": "n8n-nodes-base.respondToWebhook", "position": [-140, 980], "parameters": {"options": {}, "respondWith": "text", "responseBody": "Something went wrong."}, "typeVersion": 1.1, "notes": "This respondToWebhook node performs automated tasks as part of the workflow."}, {"id": "754727b6-1122-46b2-a956-79ed277c8b32", "name": "Get Notion Database ID", "type": "n8n-nodes-base.code", "position": [-920, 260], "parameters": {"jsCode": "let database_id = null;\n\nfor (const item of $('Get Notion Databases').all()) {\n  if (item.json.name == $('Choose Notion Database').first().json['Select Notion Database']) {\n    database_id = item.json.id;\n  }\n}\n\nreturn { json: { database_id: database_id } };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "565f4144-549e-4adc-b51b-2d87767c3dc3", "name": "Get Todoist Project ID", "type": "n8n-nodes-base.code", "position": [-40, 260], "parameters": {"jsCode": "let project_id = null;\n\nfor (const item of $('Get projects').all()) {\n  if (item.json.name == $('Choose Todoist Project').first().json['Select Todoist Project']) {\n    project_id = item.json.id;\n  }\n}\n\nreturn { json: { project_id: project_id } };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "6f79cd9e-493a-46b2-a3fb-853852a299be", "name": "Notion-Todoist Sync Setup Helper", "type": "n8n-nodes-base.formTrigger", "position": [-1800, 260], "webhookId": "7edf515f-bd69-4875-a5d8-eca8b4679896", "parameters": {"options": {"buttonLabel": "Begin"}, "formTitle": "Notion-Todoist Sync Setup Helper", "formFields": {"values": [{"fieldLabel": " ", "placeholder": "Click \"Begin\" to continue"}]}, "formDescription": "This tool gathers all necessary information and builds a JSON config which is needed as globals in the sync workflows."}, "typeVersion": 2.2, "notes": "This formTrigger node performs automated tasks as part of the workflow."}, {"id": "684e5eea-8148-4ddf-8972-dab890083ce2", "name": "Todoist Webhook Setup Helper", "type": "n8n-nodes-base.formTrigger", "position": [-1020, 620], "webhookId": "feb96ecb-a7c0-41d3-9368-2299cdce492e", "parameters": {"options": {}, "formTitle": "Todoist Webhook Setup Helper", "formFields": {"values": [{"fieldLabel": "Client ID", "requiredField": true}, {"fieldLabel": "Client secret", "requiredField": true}]}, "responseMode": "lastNode", "formDescription": "This tool helps activating the Todoist Webhook for a registered Developer App by connecting to the App vie OAuth."}, "typeVersion": 2.2, "notes": "This formTrigger node performs automated tasks as part of the workflow."}, {"id": "05f938b9-f2b3-4535-8fea-5c378bbf7944", "name": "Return config JSON", "type": "n8n-nodes-base.form", "position": [620, 260], "webhookId": "739f32d1-356d-429d-8698-a166485cc36f", "parameters": {"options": {}, "operation": "completion", "completionTitle": "Copy this to the Globals Nodes", "completionMessage": "={{ $json.toJsonString() }}"}, "typeVersion": 1, "notes": "This form node performs automated tasks as part of the workflow."}, {"id": "e5101689-8b9d-4896-aad6-b39ea2ec654d", "name": "Todoist", "type": "n8n-nodes-base.todoist", "position": [2980, 600], "parameters": {"filters": {"projectId": "={{ $('Globals').first().json.project_id }}"}, "operation": "getAll", "returnAll": true}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 2.1, "waitBetweenTries": 5000, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "c588831b-99c1-4c4b-9d33-4cb14687d896", "name": "Notion", "type": "n8n-nodes-base.notion", "position": [2980, 400], "parameters": {"simple": false, "options": {}, "resource": "databasePage", "operation": "getAll", "returnAll": true, "databaseId": {"__rl": true, "mode": "id", "value": "={{ $('Globals').first().json.database_id }}"}, "filterJson": "={\n  \"and\": [\n    {\n      \"and\": [\n        {\n          \"property\": \"Status\",\n          \"status\": {\n            \"does_not_equal\": \"Done\"\n          }\n        },\n        {\n          \"property\": \"Status\",\n          \"status\": {\n            \"does_not_equal\": \"Obsolete\"\n          }\n        }\n      ]\n    },\n    {\n      \"or\": [\n        {\n          \"property\": \"Focus\",\n          \"checkbox\": {\n            \"equals\": true\n          }\n        },\n        {\n          \"property\": \"Due\",\n          \"date\": {\n            \"is_not_empty\": true\n          }\n        }\n      ]\n    }\n  ]\n}", "filterType": "json"}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 5000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "72c05668-ed66-4a17-abd1-d848890d23d2", "name": "Compare Datasets", "type": "n8n-nodes-base.compareDatasets", "position": [3420, 500], "parameters": {"options": {}, "resolve": "mix", "mergeByFields": {"values": [{"field1": "id", "field2": "id"}]}}, "typeVersion": 2.3, "notes": "This compareDatasets node performs automated tasks as part of the workflow."}, {"id": "4fe3e94e-cd61-49e3-a37c-1a4a45ae990d", "name": "Exists/Completed in Notion", "type": "n8n-nodes-base.if", "position": [3880, 1220], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "455d4c70-c774-44d4-b3ba-78754073c2db", "operator": {"type": "object", "operation": "notEmpty", "singleValue": true}, "leftValue": "={{ $json }}", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "e6917957-0511-489e-aef1-268d54d1ae1b", "name": "Get Todoist Task", "type": "n8n-nodes-base.todoist", "onError": "continueErrorOutput", "maxTries": 2, "position": [4540, -280], "parameters": {"taskId": "={{ $json.id }}", "operation": "get"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 2.1, "waitBetweenTries": 2000, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "7ee99bf2-7316-4106-820b-71fb7dc62a69", "name": "If Todoist ID exists", "type": "n8n-nodes-base.if", "position": [4100, -180], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "71e9af47-a275-4dd4-a984-0b7ba2cb8733", "operator": {"type": "string", "operation": "notEmpty", "singleValue": true}, "leftValue": "={{ $json.id }}", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "c54b47a5-8c6e-4656-841b-12e64d0dc3af", "name": "Store Todoist ID", "type": "n8n-nodes-base.notion", "position": [5200, -180], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $('Notion').item.json.id }}"}, "options": {}, "resource": "databasePage", "operation": "update", "propertiesUi": {"propertyValues": [{"key": "YOUR_CREDENTIAL_HERE", "textContent": "={{ $json.id }}"}]}}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 5000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "29ccfeb0-f3c1-4141-a7ba-211ae6195e63", "name": "Mark as Incomplete in Todoist", "type": "n8n-nodes-base.todoist", "position": [4980, -480], "parameters": {"taskId": "={{ $json.id }}", "operation": "reopen"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 2.1, "waitBetweenTries": 5000, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "7322844b-0f2b-4f33-9aa6-117845bf90e5", "name": "Mark as Completed in Todoist", "type": "n8n-nodes-base.todoist", "position": [4980, 1120], "parameters": {"taskId": "={{ $('Todoist').item.json.id }}", "operation": "close"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "typeVersion": 2.1, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "44d117ae-f3a1-47ae-8e6d-c668e721add7", "name": "Delete Task in Todoist", "type": "n8n-nodes-base.todoist", "position": [4980, 1320], "parameters": {"taskId": "={{ $('Todoist').item.json.id }}", "operation": "delete"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "typeVersion": 2.1, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "57176531-6186-40d4-9306-65eea976583f", "name": "Loop Over Items", "type": "n8n-nodes-base.splitInBatches", "position": [3660, 1320], "parameters": {"options": {}}, "typeVersion": 3, "notes": "This splitInBatches node performs automated tasks as part of the workflow."}, {"id": "25bc1156-cfa2-4633-a635-f7bb2f4f25f2", "name": "Get Notion Task", "type": "n8n-nodes-base.notion", "position": [3880, 1420], "parameters": {"limit": 1, "simple": false, "filters": {"conditions": [{"key": "YOUR_CREDENTIAL_HERE", "condition": "equals", "richTextValue": "={{ $('Todoist').item.json.id }}"}]}, "options": {}, "resource": "databasePage", "operation": "getAll", "databaseId": {"__rl": true, "mode": "id", "value": "={{ $('Globals').first().json.database_id }}"}, "filterType": "manual"}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "alwaysOutputData": true, "waitBetweenTries": 5000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "01ec4ae8-22df-45ef-8593-4f16e3571c2e", "name": "Update Task in Todoist", "type": "n8n-nodes-base.httpRequest", "position": [3880, 520], "parameters": {"url": "{{ $env.BASE_URL }}", "method": "POST", "options": {}, "jsonQuery": "={{ $json.toJsonString() }}", "sendQuery": true, "specifyQuery": "json", "authentication": "{{ $credentials.predefinedCredentialType }}", "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 5000, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "4961331b-7cab-4c5e-b700-cb35e9258426", "name": "Pick Todoist Fields", "type": "n8n-nodes-base.set", "position": [3200, 600], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "18fbd156-06db-4991-bee7-14d8bde6bbf5", "name": "id", "type": "string", "value": "={{ $json.id }}"}, {"id": "a0c01dca-6cfa-4461-8c93-fe3f2191d624", "name": "content", "type": "string", "value": "={{ $json.content }}"}, {"id": "a9bae78e-1a70-4b8e-be0c-ce74e546b0de", "name": "priority", "type": "string", "value": "={{ $json.priority }}"}, {"id": "6ed4364e-6f62-464d-a841-e520b5bf3d96", "name": "due_datetime", "type": "string", "value": "={{ $if($json.due.datetime !== undefined, $json.due.datetime.toDateTime().toISO(), $json.due.date) }}"}, {"id": "23035cde-aea0-459d-9886-06cd7f20dd61", "name": "description", "type": "string", "value": "={{ $json.description }}"}, {"id": "6795fa58-4d52-4e88-8a1c-10e574ed2c3d", "name": "section_id", "type": "string", "value": "={{ $json.section_id }}"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "283370f2-93ff-4272-82bf-ad45c54e47f7", "name": "Map Notion to Todoist", "type": "n8n-nodes-base.code", "position": [3200, 400], "parameters": {"mode": "runOnceForEachItem", "jsCode": "const globals = $('Globals').first().json;\nconst properties = $json.properties;\n\nlet output = {};\n\noutput.id = properties['Todoist ID'].rich_text.length > 0 ? \n  properties['Todoist ID'].rich_text[0].text.content : \n  null;\n\noutput.content = properties['Name'].title.length > 0 ? properties['Name'].title[0].text.content : '[empty]';\n\noutput.description = \"[\u2197 Open in Notion](\" + $json.url + \")\"\n\n// Map priority\nif (properties['Priority'].select === null) {\n  output.priority = \"1\"; // P4\n} else {\n  output.priority = {\n    \"do first\": \"4\", // P1\n    \"urgent\": \"3\", // P2\n    \"important\": \"2\" // P3\n  }[properties['Priority'].select.name] ?? \"1\"; // P4\n}\n\n// Map section\nconst statusName = properties['Status'].status.name;\noutput.section_id = null;\nif (!['Done', 'Obsolete'].includes(statusName)) {\n  const sectionMap = Object.fromEntries(\n      globals.sections.map(section => [section.name, section.id])\n  );\n  if (!sectionMap.hasOwnProperty(statusName)) {\n      throw new Error(\"No Todoist section found for status '\" + statusName + \"'\");\n  }\n  output.section_id = sectionMap[statusName];\n}\n\n// Set UTC if time is set\noutput.due_datetime = null;\nif (properties['Due'].date !== null) {\n  output.due_datetime = properties.Due.date.start;\n  if (properties.Due.date.start.length > 10) {\n    output.due_datetime = new Date(properties.Due.date.start).toISOString();\n  }\n}\n\nreturn { json: output };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "ca33c90f-3204-45e1-9d66-20e7e740a4d7", "name": "Update task in Todoist before closing", "type": "n8n-nodes-base.httpRequest", "position": [4540, 1120], "parameters": {"url": "{{ $env.BASE_URL }}", "method": "POST", "options": {}, "jsonQuery": "={{ $json.toJsonString() }}", "sendQuery": true, "specifyQuery": "json", "authentication": "{{ $credentials.predefinedCredentialType }}", "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 5000, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "dd79e458-8c60-4de8-bdde-ec0009b2d4dc", "name": "Map Notion to Todoist1", "type": "n8n-nodes-base.code", "position": [4100, 1120], "parameters": {"mode": "runOnceForEachItem", "jsCode": "const globals = $('Globals').first().json;\nconst properties = $json.properties;\n\nlet output = {};\n\noutput.id = properties['Todoist ID'].rich_text.length > 0 ? \n  properties['Todoist ID'].rich_text[0].text.content : \n  null;\n\noutput.content = properties['Name'].title.length > 0 ? properties['Name'].title[0].text.content : '[empty]';\n\noutput.description = \"[\u2197 Open in Notion](\" + $json.url + \")\"\n\n// Map priority\nif (properties['Priority'].select === null) {\n  output.priority = \"1\"; // P4\n} else {\n  output.priority = {\n    \"do first\": \"4\", // P1\n    \"urgent\": \"3\", // P2\n    \"important\": \"2\" // P3\n  }[properties['Priority'].select.name] ?? \"1\"; // P4\n}\n\n// Map section\nconst statusName = properties['Status'].status.name;\noutput.section_id = null;\nif (!['Done', 'Obsolete'].includes(statusName)) {\n  const sectionMap = Object.fromEntries(\n      globals.sections.map(section => [section.name, section.id])\n  );\n  if (!sectionMap.hasOwnProperty(statusName)) {\n      throw new Error(\"No Todoist section found for status '\" + statusName + \"'\");\n  }\n  output.section_id = sectionMap[statusName];\n}\n\n// Set UTC if time is set\noutput.due_datetime = null;\nif (properties['Due'].date !== null) {\n  output.due_datetime = properties.Due.date.start;\n  if (properties.Due.date.start.length > 10) {\n    output.due_datetime = new Date(properties.Due.date.start).toISOString();\n  }\n}\n\nreturn { json: output };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "7dec20dc-c1a9-4fd7-85b0-c3425a73ff66", "name": "Handle empty dates", "type": "n8n-nodes-base.code", "position": [3660, 520], "parameters": {"mode": "runOnceForEachItem", "jsCode": "// API requires due_string instead of due_datetime when date/time is removed\nif ($json.due_datetime === null) {\n  delete $json.due_datetime;\n  $input.item.json.due_string = \"no date\";\n}\n\nreturn $input.item;"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "b1bf65ad-b9b1-49ba-a892-678966c520a3", "name": "Handle empty dates1", "type": "n8n-nodes-base.code", "position": [4320, 1120], "parameters": {"mode": "runOnceForEachItem", "jsCode": "// API requires due_string instead of due_datetime when date/time is removed\nif ($json.due_datetime === null) {\n  delete $json.due_datetime;\n  $input.item.json.due_string = \"no date\";\n}\n\nreturn $input.item;"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "8dbd2213-4a7f-44ef-9276-67672c6bb1ec", "name": "Handle empty dates2", "type": "n8n-nodes-base.code", "position": [3660, -180], "parameters": {"mode": "runOnceForEachItem", "jsCode": "// API requires due_string instead of due_datetime when date/time is removed\nif ($json.due_datetime === null) {\n  delete $json.due_datetime;\n  $input.item.json.due_string = \"no date\";\n}\n\nreturn $input.item;"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "4a538bb6-80f6-4920-b96a-0756e0dc5449", "name": "Create task in Todoist", "type": "n8n-nodes-base.httpRequest", "position": [4980, -280], "parameters": {"url": "{{ $env.BASE_URL }}", "method": "POST", "options": {}, "jsonQuery": "={{ $('If Todoist ID exists').item.json.toJsonString() }}", "sendQuery": true, "specifyQuery": "json", "authentication": "{{ $credentials.predefinedCredentialType }}", "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 5000, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "afdad4ec-e8bc-48c0-b80d-e1f4066871c2", "name": "Add project ID", "type": "n8n-nodes-base.set", "position": [3880, -180], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "38aef493-8125-46fd-82bc-93c6d7d6f66e", "name": "project_id", "type": "string", "value": "={{ $('Globals').first().json.project_id }}"}]}, "includeOtherFields": true}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "fb2f5730-5a69-4bed-983f-9597da7a0065", "name": "Lock Todoist ID", "type": "n8n-nodes-base.redis", "position": [5200, -380], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "ttl": 15, "value": "={{ $now.toISO() }}", "expire": true, "keyType": "YOUR_CREDENTIAL_HERE", "operation": "set"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "8f4e1ffd-a46c-4161-b6ed-89c0e78c47b1", "name": "Generate UUID", "type": "n8n-nodes-base.crypto", "position": [6080, -80], "parameters": {"action": "generate", "dataPropertyName": "uuid"}, "typeVersion": 1, "notes": "This crypto node performs automated tasks as part of the workflow."}, {"id": "28b4e10e-a9cb-40c0-9a2c-d45303f4b64e", "name": "Update section (Sync API)", "type": "n8n-nodes-base.httpRequest", "position": [6740, -80], "parameters": {"url": "{{ $env.API_BASE_URL }}", "method": "POST", "options": {}, "sendBody": true, "contentType": "form-urlencoded", "authentication": "{{ $credentials.predefinedCredentialType }}", "bodyParameters": {"parameters": [{"name": "commands", "value": "={{ $json.commands.toJsonString() }}"}]}, "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 5000, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "82cabb00-43bf-404a-9b3b-0a95bee0ba26", "name": "Status changed", "type": "n8n-nodes-base.filter", "position": [4100, 520], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "loose"}, "combinator": "or", "conditions": [{"id": "e3225b20-c6b6-479b-a0b5-dc9725197521", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Map Notion to Todoist').item.json.section_id }}", "rightValue": "={{ $json.section_id }}"}]}, "looseTypeValidation": true}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "d4242f35-f49f-4b3e-a99d-dacc90ea912a", "name": "Aggregate", "type": "n8n-nodes-base.aggregate", "position": [6520, -80], "parameters": {"options": {}, "aggregate": "aggregateAllItemData", "destinationFieldName": "commands"}, "typeVersion": 1, "notes": "This aggregate node performs automated tasks as part of the workflow."}, {"id": "4f6d649b-94c7-4826-950c-1f1632037d56", "name": "Prepare Batching Items", "type": "n8n-nodes-base.set", "position": [6300, -80], "parameters": {"mode": "raw", "options": {}, "jsonOutput": "={\n    \"type\": \"item_move\",\n    \"uuid\": \"{{ $json.uuid }}\",\n    \"args\": {\n      \"id\": \"{{ $('Merge').item.json.item.id }}\",\n      \"section_id\": \"{{ $json.item.section_id }}\"\n    }\n  }\n"}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "4d5993d2-13af-47e3-b17a-9c19db7d0489", "name": "Loop Over Items1", "type": "n8n-nodes-base.splitInBatches", "position": [5860, -180], "parameters": {"options": {}, "batchSize": 100}, "typeVersion": 3, "notes": "This splitInBatches node performs automated tasks as part of the workflow."}, {"id": "5235aab4-70f2-4d21-b9d3-a11ab817dd13", "name": "Status is Done", "type": "n8n-nodes-base.if", "position": [4760, 1220], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "a4a40d08-7929-46da-8369-c6d33b14e4f3", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $('Exists/Completed in Notion').item.json.properties['Status'].status.name }}", "rightValue": "Done"}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "608efb15-5e9b-49c2-96ea-ad35445c82bd", "name": "Lock Todoist ID1", "type": "n8n-nodes-base.redis", "position": [4760, 1020], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "ttl": 15, "value": "={{ $now.toISO() }}", "expire": true, "keyType": "YOUR_CREDENTIAL_HERE", "operation": "set"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "2d571e77-6162-4f5c-a147-99878d5b559a", "name": "Lock Todoist ID2", "type": "n8n-nodes-base.redis", "position": [4100, 320], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "ttl": 15, "value": "={{ $now.toISO() }}", "expire": true, "keyType": "YOUR_CREDENTIAL_HERE", "operation": "set"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "cee5f777-4763-490e-a89f-8ba715cfd5b8", "name": "Update task in Todoist", "type": "n8n-nodes-base.httpRequest", "position": [5200, -580], "parameters": {"url": "{{ $env.BASE_URL }}", "method": "POST", "options": {}, "jsonQuery": "={{ $('If Todoist ID exists').item.json.toJsonString() }}", "sendQuery": true, "specifyQuery": "json", "authentication": "{{ $credentials.predefinedCredentialType }}", "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 5000, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "0b2c57af-c3b6-4bed-8e4c-ec2f84576a47", "name": "Get mapped item", "type": "n8n-nodes-base.set", "position": [5420, -580], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "7419f1e6-2978-4e4d-9147-61dcb693260b", "name": "item", "type": "object", "value": "={{ $('Map Notion to Todoist').item.json }}"}, {"id": "7684427f-3267-4092-a5d3-0d13fe4e2ebb", "name": "action", "type": "string", "value": "reopened"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "e6d335b2-e848-4802-9809-15ea9eb3fd29", "name": "Get mapped item1", "type": "n8n-nodes-base.set", "position": [5000, 520], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "7679df9c-ccdb-425c-ad60-c8bd85379e87", "name": "item", "type": "object", "value": "={{ $('Map Notion to Todoist').item.json }}"}, {"id": "d6bc106f-ed01-49be-9c27-13429684d573", "name": "action", "type": "string", "value": "updated_status"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "ce49a060-0a2b-491f-b6f5-efa49b54e8d3", "name": "Schedule Trigger", "type": "n8n-nodes-base.scheduleTrigger", "position": [2760, 500], "parameters": {"rule": {"interval": [{"triggerAtHour": 6}]}}, "typeVersion": 1.2, "notes": "This scheduleTrigger node performs automated tasks as part of the workflow."}, {"id": "4402861c-7b7e-47c4-9e5c-91589262c556", "name": "Prepare summary data", "type": "n8n-nodes-base.set", "position": [4100, 720], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "2d823132-c41f-4685-9443-8ee143809ccb", "name": "item", "type": "object", "value": "={{ $('Map Notion to Todoist').item.json }}"}, {"id": "45b155c2-4254-45b3-bdd4-127ee356f8d4", "name": "action", "type": "string", "value": "updated"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "3a26cfc0-3e4e-4e14-80ef-2ad00236ae38", "name": "Prepare summary data1", "type": "n8n-nodes-base.set", "position": [5200, 1120], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "9614bc29-d8fa-4531-a8fc-5974f58e0f7a", "name": "item", "type": "object", "value": "={{ $('Todoist').item.json }}"}, {"id": "6c0ffc23-a595-49ce-82b9-b02c3f9766d8", "name": "action", "type": "string", "value": "completed"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "a3e4e83d-6789-4c55-9699-41da5eeac9e4", "name": "Merge", "type": "n8n-nodes-base.merge", "position": [5640, -180], "parameters": {}, "typeVersion": 3, "notes": "This merge node performs automated tasks as part of the workflow."}, {"id": "9b93f9ff-0c11-49eb-8425-7d3dc71eaa0b", "name": "Prepare summary data2", "type": "n8n-nodes-base.set", "position": [5200, 1320], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "833becab-363d-48f1-8911-12abd34f16a0", "name": "item", "type": "object", "value": "={{ $('Todoist').item.json }}"}, {"id": "306997a6-442e-44cc-ace1-c9bfe83ef28e", "name": "action", "type": "string", "value": "deleted"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "f6ea7bc7-093b-47ea-8bb0-48a487566218", "name": "Filter out status changes", "type": "n8n-nodes-base.filter", "position": [6100, 720], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "db22023d-a3ff-4891-8c14-5316be7e5334", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $json.action }}", "rightValue": "updated_status"}]}}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "63990a2a-7380-4799-9037-bd98ed779ddd", "name": "Map Todoist to Notion", "type": "n8n-nodes-base.code", "position": [6320, 720], "parameters": {"mode": "runOnceForEachItem", "jsCode": "const globals = $('Globals').first().json;\nlet eventData = $json.item;\n\nlet output = {};\n\noutput.name = eventData.content\n\n// Map priority\noutput.priority = {\n  \"4\": \"do first\", // P1\n  \"3\": \"urgent\", // P2\n  \"2\": \"important\" // P3\n}[eventData.priority] ?? \"\"; // P4\n\n// Map section\nconst sectionId = eventData.section_id;\nconst sectionMap = Object.fromEntries(\n    globals.sections.map(section => [section.id, section.name])\n);\n\noutput.status = \"Backlog\";\nif (sectionId !== null) {\n  if (!sectionMap.hasOwnProperty(sectionId)) {\n    //throw new Error(\"No status name found for section ID '\" + sectionId + \"'\");\n  }\n  output.status = sectionMap[sectionId];\n}\n\n// Since there is no Done section in Todoist, override status if completed\nif (eventData.is_completed) {\n  output.status = \"Done\";\n}\n\nif ($json.action == \"deleted\") {\n  output.status = \"Obsolete\";\n}\n\n// Format due date\noutput.due = eventData.due ? eventData.due.date : eventData.due_datetime || \"\"\n\nreturn { json: output };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "a5f3a1ed-1a97-441e-afbc-604b5011928e", "name": "Map summary fields", "type": "n8n-nodes-base.set", "position": [6540, 720], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "e0fcdd39-1bd8-44e7-b84f-0b6ba834fb46", "name": "name", "type": "string", "value": "=<a href=\"{{ $('Merge summary').item.json.item.description.extractUrl().replaceAll(')', '') }}\">{{ $json.name }}</a>"}, {"id": "f3bdfee8-1671-4e80-b8da-febe6f463756", "name": "priority", "type": "string", "value": "={{ $json.priority }}"}, {"id": "060bfe55-0dfa-406b-99cc-ad8592a209dc", "name": "status", "type": "string", "value": "={{ $json.status }}"}, {"id": "7b0ee321-0631-4cde-b1a7-b3547aff3561", "name": "due", "type": "string", "value": "={{ $if(!$json.due, '', $if($json.due.length > 10, $json.due.toDateTime().toLocaleString({ dateStyle: 'medium', timeStyle: 'short' }), $json.due.toDateTime().toLocaleString({ dateStyle: 'medium' }))) }}"}, {"id": "11184572-1126-4b21-aa72-1540969200f3", "name": "action", "type": "string", "value": "={{ $('Merge summary').item.json.action }}"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "50a844e8-5c5c-43e6-b774-993af828cb11", "name": "Convert to HTML Table", "type": "n8n-nodes-base.html", "position": [6760, 720], "parameters": {"options": {"capitalize": true}, "operation": "convertToHtmlTable"}, "typeVersion": 1.2, "notes": "This html node performs automated tasks as part of the workflow."}, {"id": "53e56e25-4931-4a1a-b70f-1eda8520c649", "name": "Generate email body", "type": "n8n-nodes-base.html", "position": [6980, 720], "parameters": {"html": "<!DOCTYPE html>\n<html>\n<head>\n    <style>\n        body {\n            font-family: Arial, sans-serif;\n            line-height: 1.6;\n            color: #333;\n            margin: 0;\n            padding: 20px;\n        }\n        .container {\n            max-width: 800px;\n            margin: 0 auto;\n            background-color: #ffffff;\n            padding: 20px;\n            border-radius: 5px;\n            box-shadow: 0 2px 5px rgba(0,0,0,0.1);\n        }\n        h1 {\n            color: #2c3e50;\n            text-align: center;\n            margin-bottom: 30px;\n            padding-bottom: 10px;\n            border-bottom: 2px solid #3498db;\n        }\n        table {\n            width: 100%;\n            border-collapse: collapse;\n            margin-top: 20px;\n        }\n        th {\n            background-color: #3498db;\n            color: white;\n            padding: 12px;\n            text-align: left;\n        }\n        td {\n            padding: 10px;\n            border-bottom: 1px solid #ddd;\n        }\n        tr:nth-child(even) {\n            background-color: #f9f9f9;\n        }\n        tr:hover {\n            background-color: #f5f5f5;\n        }\n        a {\n            color: #333;\n            text-decoration: underline;\n        }\n    </style>\n</head>\n<body>\n    <div class=\"container\">\n        <h1>Notion-Todoist Sync Report</h1>\n        <p>The following changes have been applied to Todoist:</p>\n        {{ $json.table }}\n    </div>\n</body>\n</html>\n"}, "typeVersion": 1.2, "notes": "This html node performs automated tasks as part of the workflow."}, {"id": "aa0507f8-e5b4-45b1-89bb-6ced3dd9b27e", "name": "Get mapped item2", "type": "n8n-nodes-base.set", "position": [5420, -180], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "7679df9c-ccdb-425c-ad60-c8bd85379e87", "name": "item", "type": "object", "value": "={{ $('Map Notion to Todoist').item.json }}"}, {"id": "d6bc106f-ed01-49be-9c27-13429684d573", "name": "action", "type": "string", "value": "created"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "60826717-078e-4509-9dbd-8b3896078a9c", "name": "Gmail", "type": "n8n-nodes-base.gmail", "position": [7200, 720], "webhookId": "5af8a60e-a295-40c1-8f03-0a920ba1de17", "parameters": {"message": "={{ $json.html }}", "options": {"appendAttribution": false}, "subject": "Notion-Todoist Sync Report"}, "credentials": {"gmailOAuth2": {"id": "TWM2BLjDHQtGAFQn", "name": "Gmail (octionicsolutions)"}}, "typeVersion": 2.1, "notes": "This gmail node performs automated tasks as part of the workflow."}, {"id": "29ea508e-eba9-40a3-9840-d70d4ec51ddb", "name": "Merge summary", "type": "n8n-nodes-base.merge", "position": [5880, 680], "parameters": {"numberInputs": 5}, "typeVersion": 3, "notes": "This merge node performs automated tasks as part of the workflow."}, {"id": "9773c5d4-a910-4b26-858d-2abf30c9602f", "name": "Sticky Note8", "type": "n8n-nodes-base.stickyNote", "position": [3140, 260], "parameters": {"color": 7, "width": 220, "height": 300, "content": "## Keep in sync\nif you update the mapping, make sure to change it in the other node as well!"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "e4224f99-4063-49a2-a5cd-c2515b117bc5", "name": "Sticky Note9", "type": "n8n-nodes-base.stickyNote", "position": [4040, 980], "parameters": {"color": 7, "width": 220, "height": 300, "content": "## Keep in sync\nif you update the mapping, make sure to change it in the other node as well!"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "3d5662ad-81a4-4a02-abe3-38d59a08bf9f", "name": "Sticky Note10", "type": "n8n-nodes-base.stickyNote", "position": [2920, 20], "parameters": {"width": 220, "height": 300, "content": "## Set Globals\nUse Sync Setup Helper Workflow to generate the JSON and paste it in every Globals Nodes"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "e78779d9-32f9-467f-ae2c-ad0cec69f506", "name": "Globals", "type": "n8n-nodes-base.set", "position": [2980, 160], "parameters": {"mode": "raw", "options": {}, "jsonOutput": "{\n  \"database_id\": \"165beaa8f4448063be17d105a9898961\",\n  \"project_id\": \"2327925123\",\n  \"sections\": [\n    {\"id\": \"177308158\", \"name\": \"Backlog\"},\n    {\"id\": \"175778849\", \"name\": \"In progress\"},\n    {\"id\": \"175778857\", \"name\": \"On hold\"}\n  ]\n}"}, "executeOnce": false, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "f01855a1-e9bf-469a-ad68-bef8d0286e3a", "name": "Sticky Note14", "type": "n8n-nodes-base.stickyNote", "position": [5800, -260], "parameters": {"color": 7, "width": 1100, "height": 380, "content": "Update section for each task in Todoist using the Sync API in full sync mode. Use batching and split into multiple batches, if needed, since there is a request limit of 100 full syncs within a 15 minute period."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "2ec59ee5-5ffb-4b57-9a2d-0f7d10a580ba", "name": "Sticky Note15", "type": "n8n-nodes-base.stickyNote", "position": [2640, -740], "parameters": {"color": 5, "width": 4760, "height": 2420, "content": "# Unidirectional sync from Notion to Todoist"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "a6f87f39-6885-4fff-99f0-1f29f0db25b7", "name": "Sticky Note16", "type": "n8n-nodes-base.stickyNote", "position": [2920, 320], "parameters": {"color": 7, "width": 220, "height": 440, "content": "Get open/incomplete tasks from both Notion and Todoist"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "fcffe10e-4ab3-4172-8be3-d060a4070922", "name": "Sticky Note17", "type": "n8n-nodes-base.stickyNote", "position": [6040, 660], "parameters": {"color": 7, "width": 1100, "height": 220, "content": "Generate an email report containing all changes made by this workflow, since changes in Todoist are being overwritten."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "af70e38e-eca7-4eba-9125-69a8ff222ac0", "name": "Execute Workflow Trigger", "type": "n8n-nodes-base.executeWorkflowTrigger", "disabled": true, "position": [2800, 3540], "parameters": {}, "typeVersion": 1, "notes": "This executeWorkflowTrigger node performs automated tasks as part of the workflow."}, {"id": "8e09225d-5d7b-468e-a8f6-c0836469799a", "name": "Differences exist1", "type": "n8n-nodes-base.filter", "position": [6980, 2720], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "or", "conditions": [{"id": "db8871d6-7f3f-468e-9a6e-3975c9f003f5", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Map Todoist to Notion1').item.json.name }}", "rightValue": "={{ $json.name }}"}, {"id": "df493beb-52a8-47f7-90c6-fb5786b1deed", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Map Todoist to Notion1').item.json.priority }}", "rightValue": "={{ $json.property_priority }}"}, {"id": "8942df51-6aa3-46d6-91f3-c6949bfd2361", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Map Todoist to Notion1').item.json.status }}", "rightValue": "={{ $json.property_status }}"}, {"id": "5484e1f8-4fbc-4c63-829a-4b45b9090702", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $if($('Map Todoist to Notion1').item.json.due !== null, $('Map Todoist to Notion1').item.json.due.toDateTime().toISO(), null) }}", "rightValue": "={{ $if($json.property_due !== null, $json.property_due.start.toDateTime().toISO(), null) }}"}]}}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "d30a9839-6ff4-49d3-8413-4c4786c47a0a", "name": "Requires completion change1", "type": "n8n-nodes-base.filter", "position": [6980, 3100], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "b9e134c4-66a7-4a83-8372-282c19fdc98e", "operator": {"type": "boolean", "operation": "notEquals"}, "leftValue": "={{ $('Get Todoist Task1').item.json.is_completed }}", "rightValue": "={{ $if($json.property_status == \"Done\", true, false) }}"}]}}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "38edc95d-ae92-46bb-9cc5-338588d720af", "name": "Create task in Notion", "type": "n8n-nodes-base.notion", "position": [7420, 2220], "parameters": {"title": "={{ $('Map Todoist to Notion1').item.json.name }}", "options": {}, "resource": "databasePage", "databaseId": {"__rl": true, "mode": "id", "value": "={{ $('Globals1').first().json.database_id }}"}, "propertiesUi": {"propertyValues": [{"key": "YOUR_CREDENTIAL_HERE", "textContent": "={{ $('Map Todoist to Notion1').item.json.todoist_id }}"}, {"key": "YOUR_CREDENTIAL_HERE", "selectValue": "={{ $('Map Todoist to Notion1').item.json.priority }}"}, {"key": "YOUR_CREDENTIAL_HERE", "statusValue": "={{ $('Map Todoist to Notion1').item.json.status }}"}, {"key": "YOUR_CREDENTIAL_HERE", "date": "={{ $('Map Todoist to Notion1').item.json.due || '' }}", "includeTime": "={{ $('Map Todoist to Notion1').item.json.due !== null && $('Map Todoist to Notion1').item.json.due.length > 10 }}"}, {"key": "YOUR_CREDENTIAL_HERE", "checkboxValue": true}]}}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 3000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "fd60280f-aef7-43f6-835c-0086adad3952", "name": "Update task in Notion", "type": "n8n-nodes-base.notion", "position": [7640, 2720], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $json.id }}"}, "options": {}, "resource": "databasePage", "operation": "update", "propertiesUi": {"propertyValues": [{"key": "YOUR_CREDENTIAL_HERE", "title": "={{ $('Map Todoist to Notion1').item.json.name }}"}, {"key": "YOUR_CREDENTIAL_HERE", "selectValue": "={{ $('Map Todoist to Notion1').item.json.priority }}"}, {"key": "YOUR_CREDENTIAL_HERE", "statusValue": "={{ $('Map Todoist to Notion1').item.json.status }}"}, {"key": "YOUR_CREDENTIAL_HERE", "date": "={{ $ifEmpty($('Map Todoist to Notion1').item.json.due, \"\") }}", "includeTime": "={{ $('Map Todoist to Notion1').item.json.due !== null && $('Map Todoist to Notion1').item.json.due.length > 10 }}"}]}}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 3000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "5aecc1ce-f83b-43a2-b2a3-44b00b59f26b", "name": "Mark as Done in Notion", "type": "n8n-nodes-base.notion", "position": [7640, 3380], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $json.id }}"}, "options": {}, "resource": "databasePage", "operation": "update", "propertiesUi": {"propertyValues": [{"key": "YOUR_CREDENTIAL_HERE", "statusValue": "Done"}]}}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 3000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "fb2c809e-50ad-4627-ad7e-4919cf30b845", "name": "Mark as Obsolete in Notion", "type": "n8n-nodes-base.notion", "position": [7640, 3700], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $json.id }}"}, "options": {}, "resource": "databasePage", "operation": "update", "propertiesUi": {"propertyValues": [{"key": "YOUR_CREDENTIAL_HERE", "statusValue": "Obsolete"}]}}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 3000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "6cb3caa1-45f5-4675-ae06-5c15163ccac1", "name": "Mark as In Progress in Notion", "type": "n8n-nodes-base.notion", "position": [7640, 3180], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $json.id }}"}, "options": {}, "resource": "databasePage", "operation": "update", "propertiesUi": {"propertyValues": [{"key": "YOUR_CREDENTIAL_HERE", "statusValue": "In progress"}]}}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 3000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "7aa800dd-620a-40b3-86a3-9fc07aeb51fb", "name": "Only continue if not locked", "type": "n8n-nodes-base.filter", "position": [3860, 2900], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "6b3359cb-c8fc-4663-b2a9-e367ad4fcba5", "operator": {"type": "string", "operation": "empty", "singleValue": true}, "leftValue": "={{ $json.item }}", "rightValue": "={{ null }}"}]}}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "ddf2093b-3528-4141-9694-0bc164851919", "name": "Lock Notion ID", "type": "n8n-nodes-base.redis", "position": [8300, 3040], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "ttl": 15, "value": "={{ $now.toISO() }}", "expire": true, "keyType": "YOUR_CREDENTIAL_HERE", "operation": "set"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "05aac18c-e591-430e-9bc9-93a581cb0e89", "name": "Check if Todoist ID is locked", "type": "n8n-nodes-base.redis", "position": [3640, 2900], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "keyType": "YOUR_CREDENTIAL_HERE", "options": {}, "operation": "get", "propertyName": "item"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "4df2d7a9-ca8e-4b64-a797-1cf9ec4786cc", "name": "Update Description in Todoist", "type": "n8n-nodes-base.todoist", "position": [7860, 2220], "parameters": {"taskId": "={{ $('Map Todoist to Notion1').item.json.todoist_id }}", "operation": "update", "updateFields": {"description": "={{ \"[\u2197 Open in Notion](\" + $('Create task in Notion').item.json.url + \")\" }}"}}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "typeVersion": 2.1, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "a2b07adf-6e01-4bbc-a852-8b59fb20046f", "name": "Description has changed in Todoist", "type": "n8n-nodes-base.filter", "position": [6980, 2920], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "loose"}, "combinator": "or", "conditions": [{"id": "db8871d6-7f3f-468e-9a6e-3975c9f003f5", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Get Todoist Task1').item.json.description }}", "rightValue": "={{ \"[\u2197 Open in Notion](\" + $json.url + \")\" }}"}]}, "looseTypeValidation": true}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "5fe846b7-9869-4d1f-a6f0-6cd14e3127d4", "name": "Update Description in Todoist1", "type": "n8n-nodes-base.todoist", "position": [7200, 2920], "parameters": {"taskId": "={{ $('Map Todoist to Notion1').item.json.todoist_id }}", "operation": "update", "updateFields": {"description": "={{ \"[\u2197 Open in Notion](\" + $json.url + \")\" }}"}}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "typeVersion": 2.1, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "879b47ad-5d32-4a40-ad61-690762e202b0", "name": "Generate UUID1", "type": "n8n-nodes-base.crypto", "position": [7420, 2440], "parameters": {"action": "generate", "dataPropertyName": "uuid"}, "typeVersion": 1, "notes": "This crypto node performs automated tasks as part of the workflow."}, {"id": "106f7e35-0cc2-4054-817c-6c3d50407e88", "name": "Update section (Sync API)1", "type": "n8n-nodes-base.httpRequest", "position": [7640, 2440], "parameters": {"url": "{{ $env.API_BASE_URL }}", "method": "POST", "options": {}, "sendBody": true, "contentType": "form-urlencoded", "authentication": "{{ $credentials.predefinedCredentialType }}", "bodyParameters": {"parameters": [{"name": "commands", "value": "=[ { \"type\": \"item_move\", \"uuid\": \"{{ $json.uuid }}\", \"args\": { \"id\": \"{{ $('Map Todoist to Notion1').item.json.todoist_id }}\", \"section_id\": \"{{ $json.section_id }}\" } }]"}]}, "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "typeVersion": 4.2, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "20d2cdc2-92ca-497d-a606-445ecf52e908", "name": "Get Backlog Section ID", "type": "n8n-nodes-base.code", "position": [7200, 2440], "parameters": {"mode": "runOnceForEachItem", "jsCode": "const globals = $('Globals1').first().json;\n\nlet output = {};\n\nconst statusName = $('Map Todoist to Notion1').item.json.status;\nconst sectionMap = Object.fromEntries(\n    globals.sections.map(section => [section.name, section.id])\n);\nif (!sectionMap.hasOwnProperty(statusName)) {\n    throw new Error(\"No Todoist section found for status '\" + statusName + \"'\");\n}\noutput.section_id = sectionMap[statusName];\n\nreturn { json: output };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "72819157-59db-4a4e-ad13-1bf73c583be7", "name": "Notion Task not found", "type": "n8n-nodes-base.if", "position": [6280, 2760], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "27ce7ba1-43e5-4534-a759-1bafb979c190", "operator": {"type": "object", "operation": "empty", "singleValue": true}, "leftValue": "={{ $json }}", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "3b5d3da8-e8b6-46d0-8706-88a9d1a5e95a", "name": "Check if creating flag exists1", "type": "n8n-nodes-base.redis", "position": [6540, 2440], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "keyType": "YOUR_CREDENTIAL_HERE", "options": {}, "operation": "get", "propertyName": "item"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "1dc24cdd-63e7-4307-a945-6cc07babd535", "name": "Only continue if flag does not exist1", "type": "n8n-nodes-base.filter", "position": [6760, 2440], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "6b3359cb-c8fc-4663-b2a9-e367ad4fcba5", "operator": {"type": "string", "operation": "empty", "singleValue": true}, "leftValue": "={{ $json.item }}", "rightValue": "={{ null }}"}]}}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "2e64fc80-428b-4f14-a7a1-db158bc58b06", "name": "Set creating flag1", "type": "n8n-nodes-base.redis", "position": [6980, 2440], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "ttl": 10, "value": "={{ $now.toISO() }}", "expire": true, "keyType": "YOUR_CREDENTIAL_HERE", "operation": "set"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "4a2cb838-40d6-4e16-bb41-c04548d3394c", "name": "Neither focussed nor planned1", "type": "n8n-nodes-base.filter", "position": [7420, 3020], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "loose"}, "combinator": "and", "conditions": [{"id": "95c47aa4-249c-4235-89c2-44c16c5b65bb", "operator": {"type": "boolean", "operation": "false", "singleValue": true}, "leftValue": "={{ $json.property_focus }}", "rightValue": ""}, {"id": "a611c1d2-d01a-4f5a-8ad8-cfaa65b11202", "operator": {"type": "object", "operation": "empty", "singleValue": true}, "leftValue": "={{ $json.property_due }}", "rightValue": ""}]}, "looseTypeValidation": true}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "56df5752-6beb-4ebe-8b6c-1b5752633db3", "name": "Mark as Focussed in Notion", "type": "n8n-nodes-base.notion", "position": [7640, 2920], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $json.id }}"}, "options": {}, "resource": "databasePage", "operation": "update", "propertiesUi": {"propertyValues": [{"key": "YOUR_CREDENTIAL_HERE", "checkboxValue": true}]}}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 3000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "e031cb0d-41bf-4273-a198-5485cdaa3758", "name": "Todoist trigger reference", "type": "n8n-nodes-base.noOp", "position": [3200, 2900], "parameters": {}, "typeVersion": 1, "notes": "This noOp node performs automated tasks as part of the workflow."}, {"id": "539bac50-20c8-4212-aaa5-a99b7f2c3cdb", "name": "Get Notion Task2", "type": "n8n-nodes-base.notion", "position": [6760, 3700], "parameters": {"limit": 1, "filters": {"conditions": [{"key": "YOUR_CREDENTIAL_HERE", "condition": "equals", "richTextValue": "={{ $('Todoist trigger reference').item.json.body.event_data.id }}"}]}, "options": {}, "resource": "databasePage", "operation": "getAll", "databaseId": {"__rl": true, "mode": "id", "value": "={{ $('Globals1').first().json.database_id }}"}, "filterType": "manual"}, "credentials": {"notionApi": {"id": "ObmaBA0dJss3JJPv", "name": "Notion (Test)"}}, "typeVersion": 2.2, "alwaysOutputData": true, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "1585da72-5578-456b-a092-21e66b033350", "name": "Notion Task found1", "type": "n8n-nodes-base.filter", "position": [6980, 3700], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "d644079f-b140-42b3-85c5-f1283a6d21d8", "operator": {"type": "object", "operation": "notEmpty", "singleValue": true}, "leftValue": "={{ $json }}", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "c5df6b20-a7ed-4e57-9814-091f76ee677b", "name": "Switch by Event", "type": "n8n-nodes-base.switch", "position": [6760, 2920], "parameters": {"rules": {"values": [{"outputKey": "YOUR_CREDENTIAL_HERE", "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "4ffd3f5d-dc24-47cb-a4df-1a6f6a6dc2b9", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $('Todoist trigger reference').item.json.body.event_name }}", "rightValue": "item:updated"}]}, "renameOutput": true}, {"outputKey": "YOUR_CREDENTIAL_HERE", "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "d5290195-6595-4c91-af86-6df724bb0980", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $('Todoist trigger reference').item.json.body.event_name }}", "rightValue": "item:completed"}]}, "renameOutput": true}, {"outputKey": "YOUR_CREDENTIAL_HERE", "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "e047ade5-ce68-4bc8-b799-7130bd0eb917", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $('Todoist trigger reference').item.json.body.event_name }}", "rightValue": "item:uncompleted"}]}, "renameOutput": true}]}, "options": {}}, "typeVersion": 3.2, "notes": "This switch node performs automated tasks as part of the workflow."}, {"id": "74859244-46a3-45a2-8e90-923c6bdc0d40", "name": "If event is not :deleted", "type": "n8n-nodes-base.if", "position": [4080, 2900], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "4fb65ecf-dbb9-4bd1-a5a8-614441aa2963", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Todoist trigger reference').item.json.body.event_name }}", "rightValue": "item:deleted"}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "f3404606-51e8-4b53-b332-20f9cf26a969", "name": "Execution Data", "type": "n8n-nodes-base.executionData", "position": [3420, 2900], "parameters": {"dataToSave": {"values": [{"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $('Globals1').first().json.database_id }}"}, {"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $('Globals1').first().json.project_id }}"}, {"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $json.body.event_data.id }}"}, {"key": "YOUR_CREDENTIAL_HERE", "value": "Todoist"}]}}, "typeVersion": 1, "notes": "This executionData node performs automated tasks as part of the workflow."}, {"id": "643b67db-43e2-47fb-8f7f-1c5ad7b780fc", "name": "Execution Data1", "type": "n8n-nodes-base.executionData", "position": [5840, 2760], "parameters": {"dataToSave": {"values": [{"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $json.name }}"}]}}, "typeVersion": 1, "notes": "This executionData node performs automated tasks as part of the workflow."}, {"id": "ad4ff5a0-ab39-48f9-82fd-9f920b8160d5", "name": "Execution Data2", "type": "n8n-nodes-base.executionData", "position": [7640, 2220], "parameters": {"dataToSave": {"values": [{"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $json.id }}"}]}}, "typeVersion": 1, "notes": "This executionData node performs automated tasks as part of the workflow."}, {"id": "ee82b160-35ce-4943-b8f7-6297382f0656", "name": "Execution Data3", "type": "n8n-nodes-base.executionData", "position": [6540, 2920], "parameters": {"dataToSave": {"values": [{"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $json.id }}"}]}}, "typeVersion": 1, "notes": "This executionData node performs automated tasks as part of the workflow."}, {"id": "591f2447-1a81-436c-b073-838cb3011f51", "name": "Execution Data4", "type": "n8n-nodes-base.executionData", "position": [7200, 3700], "parameters": {"dataToSave": {"values": [{"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $json.id }}"}, {"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $json.name }}"}]}}, "typeVersion": 1, "notes": "This executionData node performs automated tasks as part of the workflow."}, {"id": "558f1a95-56ad-43c0-b924-b8a6ad50cf36", "name": "Globals1", "type": "n8n-nodes-base.set", "position": [3420, 2640], "parameters": {"mode": "raw", "options": {}, "jsonOutput": "{\n  \"database_id\": \"165beaa8-f444-8063-be17-d105a9898961\",\n  \"project_id\": \"2327925123\",\n  \"sections\": [\n    {\"id\": \"177308158\", \"name\": \"Backlog\"},\n    {\"id\": \"175778849\", \"name\": \"In progress\"},\n    {\"id\": \"175778857\", \"name\": \"On hold\"}\n  ]\n}"}, "executeOnce": true, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "3f39ff17-8700-475e-9ae7-ef3dbef299af", "name": "Sticky Note12", "type": "n8n-nodes-base.stickyNote", "position": [3360, 2500], "parameters": {"width": 220, "height": 300, "content": "## Set Globals\nUse Sync Setup Helper Workflow to generate the JSON and paste it in every Globals Nodes"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "44393b98-739a-4478-a485-67b8bb59e901", "name": "Map Todoist to Notion1", "type": "n8n-nodes-base.code", "position": [5620, 2760], "parameters": {"mode": "runOnceForEachItem", "jsCode": "const globals = $('Globals1').first().json;\nlet eventData = $json;\n\nlet output = {};\n\noutput.todoist_id = eventData.id;\n\noutput.name = eventData.content\n\n// Map priority\noutput.priority = {\n  \"4\": \"do first\", // P1\n  \"3\": \"urgent\", // P2\n  \"2\": \"important\" // P3\n}[eventData.priority] ?? \"\"; // P4\n\n// Map section\nconst sectionId = eventData.section_id;\nconst sectionMap = Object.fromEntries(\n    globals.sections.map(section => [section.id, section.name])\n);\n\noutput.status = \"Backlog\";\nif (sectionId !== null) {\n  if (!sectionMap.hasOwnProperty(sectionId)) {\n    throw new Error(\"No status name found for section ID '\" + sectionId + \"'\");\n  }\n  output.status = sectionMap[sectionId];\n}\n\n// Since there is no Done section in Todoist, override status if completed\nif (eventData.is_completed) {\n  output.status = \"Done\";\n}\n\n// Map date and time\noutput.due = null;\nif (eventData.due !== null) {\n  output.due = eventData.due.date\n  if (eventData.due.datetime !== undefined) {\n    output.due = eventData.due.datetime\n  }\n}\n\nreturn { json: output };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "9c9bf784-1318-4891-a6d8-3303aa09bd82", "name": "Get Todoist Task1", "type": "n8n-nodes-base.todoist", "onError": "continueErrorOutput", "maxTries": 3, "position": [4740, 2760], "parameters": {"taskId": "={{ $('Todoist trigger reference').item.json.body.event_data.id }}", "operation": "get"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 2.1, "waitBetweenTries": 3000, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "0d64962d-079a-4991-9442-8cf1f18b4b4f", "name": "Get Notion Task1", "type": "n8n-nodes-base.notion", "position": [6060, 2760], "parameters": {"limit": 1, "filters": {"conditions": [{"key": "YOUR_CREDENTIAL_HERE", "condition": "equals", "richTextValue": "={{ $('Map Todoist to Notion1').item.json.todoist_id }}"}]}, "options": {}, "resource": "databasePage", "operation": "getAll", "databaseId": {"__rl": true, "mode": "id", "value": "={{ $('Globals1').first().json.database_id }}"}, "filterType": "manual"}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "typeVersion": 2.2, "alwaysOutputData": true, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "0380f6c3-a3e8-4dba-8e45-f98a53dfa60f", "name": "Sticky Note23", "type": "n8n-nodes-base.stickyNote", "position": [5780, 2660], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Store more information, which was not available earlier"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "a36cf7d4-d467-428b-b6cc-2ff7c9ccd1ad", "name": "Sticky Note26", "type": "n8n-nodes-base.stickyNote", "position": [7360, 2140], "parameters": {"color": 7, "width": 220, "height": 240, "content": "Set focussed flag, so it remains in Todoist even after updating in Notion"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "79a16ed2-2cc9-4dc0-9efd-996ec3409b73", "name": "Sticky Note27", "type": "n8n-nodes-base.stickyNote", "position": [6460, 1880], "parameters": {"color": 7, "width": 1800, "height": 760, "content": "**Create Notion page** if it does not exist"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "124820ca-86d1-4b99-8437-2fcdc399cef7", "name": "Sticky Note28", "type": "n8n-nodes-base.stickyNote", "position": [6460, 2660], "parameters": {"color": 7, "width": 1340, "height": 900, "content": "**Update Notion page** if it needs to be changed"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "597e44ec-8c2c-4e2e-9d8c-e9fd8495dcd3", "name": "Sticky Note29", "type": "n8n-nodes-base.stickyNote", "position": [3580, 2800], "parameters": {"color": 7, "width": 440, "height": 280, "content": "Checking for a cached flag in Redis, to prevent overrides and endless loops."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "88302c02-82d5-4e5d-9716-49e01d4d1b12", "name": "Sticky Note31", "type": "n8n-nodes-base.stickyNote", "position": [6000, 2660], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Retrieve Notion page, so it can be compared against"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "31f3c572-4f38-4133-85ff-699ab6359d0e", "name": "Sticky Note32", "type": "n8n-nodes-base.stickyNote", "position": [6480, 2820], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Store more information, which was not available earlier"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "f2f6d6b4-3c6e-46ef-906b-7293d16a0ce2", "name": "Sticky Note33", "type": "n8n-nodes-base.stickyNote", "position": [3360, 2800], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Store relevant information in highlighted execution data, in case of debugging"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "3529a085-c1dc-4467-927f-cebbbc7ec128", "name": "Sticky Note35", "type": "n8n-nodes-base.stickyNote", "position": [2640, 1780], "parameters": {"color": 5, "width": 5880, "height": 2160, "content": "## Trigger based synchronization from Todoist to Notion (part of bi-directional sync)"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "7bd78d2f-8452-48d0-8b85-c2ec9023336f", "name": "Sticky Note36", "type": "n8n-nodes-base.stickyNote", "position": [3140, 2800], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Following nodes reference to this node, so the trigger can easily be replaced"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "e87ad29c-5eb8-4abb-86a2-d2dd937ab81d", "name": "Sticky Note37", "type": "n8n-nodes-base.stickyNote", "position": [6480, 2380], "parameters": {"color": 7, "width": 660, "height": 240, "content": "This prevents the creation of duplicates in Notion, by storing a special flag in Redis containing the Todoist ID"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "e496f9f0-28be-4ae2-8a69-47805e858724", "name": "Requires completion change", "type": "n8n-nodes-base.filter", "position": [1220, 2560], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "b9e134c4-66a7-4a83-8372-282c19fdc98e", "operator": {"type": "boolean", "operation": "notEquals"}, "leftValue": "={{ $('Map Notion to Todoist2').item.json.is_completed }}", "rightValue": "={{ $json.is_completed }}"}]}}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "6f0f1698-638a-49ec-af54-8a11ddfb4a3a", "name": "Has been completed?", "type": "n8n-nodes-base.if", "position": [1440, 2560], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "a8595a9c-fc03-4bb1-b1fc-ecde223d19aa", "operator": {"type": "boolean", "operation": "true", "singleValue": true}, "leftValue": "={{ $('Map Notion to Todoist2').item.json.is_completed }}", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "dab457b1-f419-4916-bc93-35029e8a8136", "name": "Differences exist", "type": "n8n-nodes-base.filter", "position": [1220, 2260], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "loose"}, "combinator": "or", "conditions": [{"id": "3a400a40-a25b-4e88-81ba-1aa9ada9d3aa", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Map Notion to Todoist2').item.json.content }}", "rightValue": "={{ $json.content }}"}, {"id": "8a649292-a21e-4d3f-9e3c-0047b21a7487", "operator": {"type": "number", "operation": "notEquals"}, "leftValue": "={{ $('Map Notion to Todoist2').item.json.priority }}", "rightValue": "={{ $json.priority }}"}, {"id": "65f02b94-18de-4883-b5b1-f58fe5459828", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Map Notion to Todoist2').item.json.due_datetime || null }}", "rightValue": "={{ $json.due === null ? null : $json.due.date}}"}, {"id": "e3225b20-c6b6-479b-a0b5-dc9725197521", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Map Notion to Todoist2').item.json.section_id }}", "rightValue": "={{ $json.section_id }}"}, {"id": "0b24cd35-b8b0-4d21-943c-fad905c4542a", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Map Notion to Todoist2').item.json.description }}", "rightValue": "={{ $json.description }}"}]}, "looseTypeValidation": true}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "57489d22-0081-48f5-883d-d646cde35908", "name": "Only continue if not locked1", "type": "n8n-nodes-base.filter", "position": [-1680, 3040], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "6b3359cb-c8fc-4663-b2a9-e367ad4fcba5", "operator": {"type": "string", "operation": "empty", "singleValue": true}, "leftValue": "={{ $json.item }}", "rightValue": "={{ null }}"}]}}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "6bbb506e-407b-4357-935e-692cdccac165", "name": "Check if Notion ID is locked", "type": "n8n-nodes-base.redis", "position": [-1900, 3040], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "keyType": "YOUR_CREDENTIAL_HERE", "options": {}, "operation": "get", "propertyName": "item"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "abd91f47-f21c-4805-ae8a-d52a6b576304", "name": "Get Notion task", "type": "n8n-nodes-base.notion", "maxTries": 2, "position": [-1460, 3040], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $('Notion trigger reference').item.json.body.data.id }}"}, "simple": false, "resource": "databasePage", "operation": "get"}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 3000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "62a7503b-d605-484b-95c0-864ea698f31b", "name": "Check if creating flag exists", "type": "n8n-nodes-base.redis", "position": [1440, 3400], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "keyType": "YOUR_CREDENTIAL_HERE", "options": {}, "operation": "get", "propertyName": "item"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "e7981df5-7d8e-4f2d-b25f-9b74b177c95e", "name": "Only continue if flag does not exist", "type": "n8n-nodes-base.filter", "position": [1660, 3400], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "6b3359cb-c8fc-4663-b2a9-e367ad4fcba5", "operator": {"type": "string", "operation": "empty", "singleValue": true}, "leftValue": "={{ $json.item }}", "rightValue": "={{ null }}"}]}}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "22869aac-c36b-4aad-ab3b-00971cfe7c4e", "name": "Set creating flag", "type": "n8n-nodes-base.redis", "position": [1880, 3400], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "ttl": 10, "value": "={{ $now.toISO() }}", "expire": true, "keyType": "YOUR_CREDENTIAL_HERE", "operation": "set"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "86e4a245-d8e3-4a2a-adab-57f908fadaca", "name": "Todoist ID exists?", "type": "n8n-nodes-base.if", "position": [-360, 3240], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "8b074088-25e0-4975-9356-bd2d9c17c0d3", "operator": {"type": "string", "operation": "notEmpty", "singleValue": true}, "leftValue": "={{ $json.id }}", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "0f49f736-eeec-4159-bd12-056ef4270fb5", "name": "Get todoist ID", "type": "n8n-nodes-base.set", "position": [1880, 2560], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "be85c68d-e9a8-4a88-ae12-54ec9cc685e5", "name": "id", "type": "string", "value": "={{ $('Get Todoist Task2').item.json.id }}"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "0f1eea07-0750-46a8-801b-76254461437f", "name": "Todoist ID exists", "type": "n8n-nodes-base.filter", "position": [1220, 2000], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "loose"}, "combinator": "and", "conditions": [{"id": "d20ece89-96d3-4324-894f-860711138952", "operator": {"type": "string", "operation": "notEmpty", "singleValue": true}, "leftValue": "={{ $json.id }}", "rightValue": ""}]}, "looseTypeValidation": true}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "5fe0975a-9b10-4046-8037-54e20ebd8804", "name": "Get todoist ID1", "type": "n8n-nodes-base.set", "position": [1880, 2000], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "be85c68d-e9a8-4a88-ae12-54ec9cc685e5", "name": "id", "type": "string", "value": "={{ $('Todoist ID exists').item.json.id }}"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "7025479a-b14e-471e-bc7d-211241b37d8f", "name": "Status changed but not empty", "type": "n8n-nodes-base.filter", "position": [1220, 2900], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "loose"}, "combinator": "and", "conditions": [{"id": "e3225b20-c6b6-479b-a0b5-dc9725197521", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Map Notion to Todoist2').item.json.section_id }}", "rightValue": "={{ $json.section_id }}"}, {"id": "2ea2a63b-9968-4f95-af48-fc71fa210ffd", "operator": {"type": "string", "operation": "notEmpty", "singleValue": true}, "leftValue": "={{ $('Map Notion to Todoist2').item.json.section_id }}", "rightValue": ""}]}, "looseTypeValidation": true}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "e8b84dd0-9f5e-4670-a2ee-50e75448b611", "name": "Status is Obsolete?", "type": "n8n-nodes-base.if", "position": [-800, 3040], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "9118fe29-c592-430e-8877-57cb81e6bf75", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $('Get Notion task').item.json.properties['Status'].status.name }}", "rightValue": "Obsolete"}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "aba681f0-a079-442f-9538-fb81abe8a9b7", "name": "Status is not Done", "type": "n8n-nodes-base.filter", "position": [1220, 3240], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "loose"}, "combinator": "and", "conditions": [{"id": "e3225b20-c6b6-479b-a0b5-dc9725197521", "operator": {"type": "string", "operation": "notEquals"}, "leftValue": "={{ $('Get Notion task').item.json.properties['Status'].status.name }}", "rightValue": "Done"}]}, "looseTypeValidation": true}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "8262fbbd-3fc4-478c-8ffc-3d8d6ece743c", "name": "Neither focussed nor planned", "type": "n8n-nodes-base.if", "position": [-580, 3140], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "990a9ac6-c358-4aae-8c4e-4c49f9830ef5", "operator": {"type": "object", "operation": "empty", "singleValue": true}, "leftValue": "={{ $('Get Notion task').item.json.properties.Due.date }}", "rightValue": ""}, {"id": "93c8c7eb-1979-4eaf-90a6-be659290cded", "operator": {"type": "boolean", "operation": "false", "singleValue": true}, "leftValue": "={{ $('Get Notion task').item.json.properties.Focus.checkbox }}", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "d0ab88eb-deed-4a2f-85f9-7aa381659c55", "name": "Notion trigger reference", "type": "n8n-nodes-base.noOp", "position": [-2340, 3040], "parameters": {}, "typeVersion": 1, "notes": "This noOp node performs automated tasks as part of the workflow."}, {"id": "1895a6d2-71dc-4f58-a746-9b59c71bedcb", "name": "Notion Webhook", "type": "n8n-nodes-base.webhook", "position": [-3220, 3040], "webhookId": "7e1a4d8a-9cdc-4991-817c-1429ccfbece2", "parameters": {"path": "7e1a4d8a-9cdc-4991-817c-1429ccfbece2", "options": {}, "httpMethod": "POST"}, "typeVersion": 2, "notes": "This webhook node performs automated tasks as part of the workflow."}, {"id": "35178847-629c-4672-858a-9b7de7344ef7", "name": "Store Todoist ID1", "type": "n8n-nodes-base.notion", "position": [2320, 3500], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $('Notion trigger reference').item.json.body.data.id }}"}, "options": {}, "resource": "databasePage", "operation": "update", "propertiesUi": {"propertyValues": [{"key": "YOUR_CREDENTIAL_HERE", "textContent": "={{ $json.id }}"}]}}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "retryOnFail": true, "typeVersion": 2.2, "waitBetweenTries": 3000, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "1310034f-b035-43d0-9448-122bf0018e63", "name": "Mark as Completed in Todoist1", "type": "n8n-nodes-base.todoist", "position": [1660, 2460], "parameters": {"taskId": "={{ $('Get Todoist Task2').item.json.id }}", "operation": "close"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 2.1, "waitBetweenTries": 3000, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "baa2d8ad-3eee-46ec-bd20-cea4615f5cec", "name": "Update task in Todoist1", "type": "n8n-nodes-base.httpRequest", "position": [1660, 2260], "parameters": {"url": "{{ $env.BASE_URL }}", "method": "POST", "options": {}, "jsonQuery": "={{ $('Map Notion to Todoist2').item.json.toJsonString() }}", "sendQuery": true, "specifyQuery": "json", "authentication": "{{ $credentials.predefinedCredentialType }}", "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 3000, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "ad9d9069-a09b-41d0-a4f5-e83ac28f04cf", "name": "Globals2", "type": "n8n-nodes-base.set", "position": [-2120, 2780], "parameters": {"mode": "raw", "options": {}, "jsonOutput": "{\n  \"database_id\": \"165beaa8-f444-8063-be17-d105a9898961\",\n  \"project_id\": \"2327925123\",\n  \"sections\": [\n    {\"id\": \"177308158\", \"name\": \"Backlog\"},\n    {\"id\": \"175778849\", \"name\": \"In progress\"},\n    {\"id\": \"175778857\", \"name\": \"On hold\"}\n  ]\n}"}, "executeOnce": true, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "165db83b-47fd-4aa6-9d04-c7ce07e8f8bf", "name": "Mark as Incomplete in Todoist1", "type": "n8n-nodes-base.todoist", "position": [1660, 2660], "parameters": {"taskId": "={{ $json.id }}", "operation": "reopen"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 2.1, "waitBetweenTries": 3000, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "a5bca570-f213-4d40-ac5f-7f4bbd532432", "name": "Generate UUID2", "type": "n8n-nodes-base.crypto", "position": [1440, 2900], "parameters": {"action": "generate", "dataPropertyName": "uuid"}, "typeVersion": 1, "notes": "This crypto node performs automated tasks as part of the workflow."}, {"id": "00cea352-9026-433b-b351-28720e0b6a40", "name": "Update section (Sync API)2", "type": "n8n-nodes-base.httpRequest", "position": [1660, 2900], "parameters": {"url": "{{ $env.API_BASE_URL }}", "method": "POST", "options": {}, "sendBody": true, "contentType": "form-urlencoded", "authentication": "{{ $credentials.predefinedCredentialType }}", "bodyParameters": {"parameters": [{"name": "commands", "value": "=[ { \"type\": \"item_move\", \"uuid\": \"{{ $json.uuid }}\", \"args\": { \"id\": \"{{ $('Get Todoist Task2').item.json.id }}\", \"section_id\": \"{{ $('Map Notion to Todoist2').item.json.section_id }}\" } }]"}]}, "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "typeVersion": 4.2, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "1cd9bf69-0e3c-4ace-8e79-894fea1c0045", "name": "Lock Todoist ID4", "type": "n8n-nodes-base.redis", "position": [2320, 2580], "parameters": {"key": "YOUR_CREDENTIAL_HERE", "ttl": 15, "value": "={{ $now.toISO() }}", "expire": true, "keyType": "YOUR_CREDENTIAL_HERE", "operation": "set"}, "credentials": {"redis": {"id": "t7IN9qSccRezBIzV", "name": "Redis account"}}, "typeVersion": 1, "notes": "This redis node performs automated tasks as part of the workflow."}, {"id": "dc21db22-41a0-4a3b-aeba-dc3c81fdf251", "name": "Delete Task in Todoist2", "type": "n8n-nodes-base.todoist", "onError": "continueRegularOutput", "position": [1660, 2000], "parameters": {"taskId": "={{ $json.id }}", "operation": "delete"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 2.1, "waitBetweenTries": 3000, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "e90c2ef5-61f9-4e16-879f-14c9d7974f88", "name": "Map Notion to Todoist2", "type": "n8n-nodes-base.code", "position": [-1240, 3040], "parameters": {"mode": "runOnceForEachItem", "jsCode": "const globals = $('Globals2').first().json;\nconst properties = $('Get Notion task').item.json.properties;\n\nlet output = {};\n\noutput.project_id = globals.project_id;\n\noutput.id = properties['Todoist ID'].rich_text.length > 0 ? \n  properties['Todoist ID'].rich_text[0].text.content : \n  null;\n\noutput.content = properties['Name'].title.length > 0 ? properties['Name'].title[0].text.content : '[empty]';\n\noutput.description = \"[\u2197 Open in Notion](\" + $('Get Notion task').item.json.url + \")\"\n\n// Map priority\nif (properties['Priority'].select === null) {\n  output.priority = \"1\"; // P4\n} else {\n  output.priority = {\n    \"do first\": \"4\", // P1\n    \"urgent\": \"3\", // P2\n    \"important\": \"2\" // P3\n  }[properties['Priority'].select.name] ?? \"1\"; // P4\n}\n\n// Map section\nconst statusName = properties['Status'].status.name;\noutput.section_id = null;\nif (!['Done', 'Obsolete'].includes(statusName)) {\n  const sectionMap = Object.fromEntries(\n      globals.sections.map(section => [section.name, section.id])\n  );\n  if (!sectionMap.hasOwnProperty(statusName)) {\n      throw new Error(\"No Todoist section found for status '\" + statusName + \"'\");\n  }\n  output.section_id = sectionMap[statusName];\n}\n\n// Map completed by status \"Done\"\noutput.is_completed = false;\nif (properties['Status'].status.name == \"Done\") {\n  output.is_completed = true;\n}\n\n// Set UTC if time is set\nif (properties['Due'].date !== null) {\n  output.due_datetime = properties.Due.date.start\n  if (properties.Due.date.start.length > 10) {\n    output.due_datetime = new Date(properties.Due.date.start).toISOString();\n  }\n} else {\n  // API requires due_string instead of due_datetime when date/time is removed\n  output.due_string = \"no date\";\n}\n\nreturn { json: output };"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "e5e12b26-6c45-4c63-9c7d-05ae88a457aa", "name": "Get Todoist Task2", "type": "n8n-nodes-base.todoist", "onError": "continueErrorOutput", "position": [300, 3140], "parameters": {"taskId": "={{ $json.id }}", "operation": "get"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "typeVersion": 2.1, "notes": "This todoist node performs automated tasks as part of the workflow."}, {"id": "05441894-9904-44b3-94f7-bb600bcbe7ba", "name": "Create task in Todoist1", "type": "n8n-nodes-base.httpRequest", "position": [2100, 3400], "parameters": {"url": "{{ $env.BASE_URL }}", "method": "POST", "options": {}, "jsonQuery": "={{ $('Map Notion to Todoist2').item.json.toJsonString() }}", "sendQuery": true, "specifyQuery": "json", "authentication": "{{ $credentials.predefinedCredentialType }}", "nodeCredentialType": "YOUR_CREDENTIAL_HERE"}, "credentials": {"todoistApi": {"id": "Dp3VoWGH5IhrK22k", "name": "Todoist (mario.haarmann)"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 3000, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "354b6df0-b51e-476c-bb54-baaa1a6ca788", "name": "Execution Data5", "type": "n8n-nodes-base.executionData", "position": [-2120, 3040], "parameters": {"dataToSave": {"values": [{"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $('Globals2').first().json.database_id }}"}, {"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $('Globals2').first().json.project_id }}"}, {"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $json.body.data.id }}"}, {"key": "YOUR_CREDENTIAL_HERE", "value": "Notion"}]}}, "typeVersion": 1, "notes": "This executionData node performs automated tasks as part of the workflow."}, {"id": "04fe1427-30ba-45ae-904a-05c7b4c2269f", "name": "Execution Data6", "type": "n8n-nodes-base.executionData", "position": [-1020, 3040], "parameters": {"dataToSave": {"values": [{"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $json.id }}"}, {"key": "YOUR_CREDENTIAL_HERE", "value": "={{ $json.content }}"}]}}, "typeVersion": 1, "notes": "This executionData node performs automated tasks as part of the workflow."}, {"id": "2b8262d7-298b-4a70-a420-50d8d066cb40", "name": "Sticky Note22", "type": "n8n-nodes-base.stickyNote", "position": [1380, 2840], "parameters": {"color": 7, "width": 440, "height": 220, "content": "Update section for in Todoist using the Sync API in full sync mode (**maximum of 100 requests within a 15 minute period!**)"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "10571edb-ed79-43d3-bcb9-a64273c3eeb7", "name": "Sticky Note24", "type": "n8n-nodes-base.stickyNote", "position": [1140, 1880], "parameters": {"color": 7, "width": 900, "height": 300, "content": "**Delete the task** if it does not fit into the filter anymore"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "85124819-6f06-44e8-a5dd-0c31c1aec5e3", "name": "Sticky Note25", "type": "n8n-nodes-base.stickyNote", "position": [1140, 2200], "parameters": {"color": 7, "width": 900, "height": 880, "content": "**Update the task** if it needs to be changed"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "6a9d1e64-39d1-4361-aa60-2368ba36a2cd", "name": "Sticky Note38", "type": "n8n-nodes-base.stickyNote", "position": [-1520, 2940], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Notion tries to deliver webhooks again. To ensure the data is up to date, the payload must be retrieved again."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "4787a4de-f5d2-4983-a658-916c497ce683", "name": "Sticky Note39", "type": "n8n-nodes-base.stickyNote", "position": [-2180, 2640], "parameters": {"width": 220, "height": 300, "content": "## Set Globals\nUse Sync Setup Helper Workflow to generate the JSON and paste it in every Globals Nodes"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "5afdea5e-e6ed-463a-99bb-35bda66c25e6", "name": "Sticky Note41", "type": "n8n-nodes-base.stickyNote", "position": [1600, 1920], "parameters": {"color": 7, "width": 220, "height": 240, "content": "Continues on error, so it is not necessary to retrieve task beforehand"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "6353737c-ea2b-4907-94d1-d5b4c743ca29", "name": "Sticky Note42", "type": "n8n-nodes-base.stickyNote", "position": [-2400, 2940], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Following nodes reference to this node, so the trigger can easily be replaced"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "caa80f7d-1c1a-4bbf-a92d-4df2a65e6e2f", "name": "Sticky Note43", "type": "n8n-nodes-base.stickyNote", "position": [-2180, 2940], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Store relevant information in highlighted execution data, in case of debugging"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "58332f57-5bee-4559-922b-f2ec025af90d", "name": "Sticky Note44", "type": "n8n-nodes-base.stickyNote", "position": [-1080, 2940], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Store more information, which was not available earlier"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "785526ba-39e6-42aa-83f0-731a81e821ec", "name": "Sticky Note45", "type": "n8n-nodes-base.stickyNote", "position": [-860, 2940], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Instead of deleting pages in Notion, it is a safer practice to mark them as obsolete instead."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "933c2fd0-c378-4984-b34a-6a1f89d61299", "name": "Sticky Note46", "type": "n8n-nodes-base.stickyNote", "position": [1380, 3320], "parameters": {"color": 7, "width": 660, "height": 260, "content": "In rare cases Notion fires multiple webhooks within a short period of time, when creating a new task. This prevents the creation of duplicates in Todoist."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "14b97b13-0689-481b-8a21-61940e742b2a", "name": "Sticky Note47", "type": "n8n-nodes-base.stickyNote", "position": [-1300, 2940], "parameters": {"color": 7, "width": 220, "height": 280, "content": "The mapping is more advanced and also includes fall-back values for edge-cases"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "ce6ed70d-4fa0-4291-9ac8-13cd23e899cc", "name": "Sticky Note48", "type": "n8n-nodes-base.stickyNote", "position": [-1960, 2940], "parameters": {"color": 7, "width": 440, "height": 280, "content": "Checking for a cached flag in Redis, to prevent overrides and endless loops."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "21378455-34e5-42bf-857d-543a7a972c33", "name": "Sticky Note5", "type": "n8n-nodes-base.stickyNote", "position": [-3340, 1780], "parameters": {"color": 5, "width": 5880, "height": 1960, "content": "# Trigger based synchronization from Notion to Todoist (part of bi-directional sync)"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "0688c57e-9de6-43b5-8b75-b214760a8445", "name": "Sticky Note20", "type": "n8n-nodes-base.stickyNote", "position": [-3280, 2820], "parameters": {"width": 220, "height": 400, "content": "## Configure Webhook\nGrab the webhook URL and use it in the Notion automation. If using the NotionWebhookEmulator instead, replace this node with the nodes shown below."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "d3a90492-d62f-4b01-bf2b-1b82b74c04cc", "name": "Sticky Note7", "type": "n8n-nodes-base.stickyNote", "position": [-640, 3040], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Only sync focussed & planned tasks for efficiency reasons"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "d0b03cb0-8c69-4a6a-854a-afdfc690219f", "name": "Sticky Note19", "type": "n8n-nodes-base.stickyNote", "position": [-420, 3120], "parameters": {"color": 7, "width": 220, "height": 280, "content": "The mapping between the Notion and Todoist tasks is stored in Notion"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "c34e228b-eee5-43f4-9876-8ffcd3f64488", "name": "Sticky Note11", "type": "n8n-nodes-base.stickyNote", "position": [2260, 2460], "parameters": {"color": 7, "width": 220, "height": 280, "content": "The Todoist ID is being cached in Redis for 15 seconds, so the other Diff sync does not get triggered."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "c78c9264-683f-4ee5-9cd2-086627ce4fd1", "name": "Sticky Note13", "type": "n8n-nodes-base.stickyNote", "position": [1140, 3100], "parameters": {"color": 7, "width": 1340, "height": 580, "content": "**Create task** if it does not exist (anymore)"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "7940c709-6107-4efa-83dc-639b88286a84", "name": "Sticky Note6", "type": "n8n-nodes-base.stickyNote", "position": [1160, 3160], "parameters": {"color": 7, "width": 220, "height": 240, "content": "Edge case: the task has been moved from Obsolete to Done"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "2d6b7423-0818-4a2d-b672-0d09f7eaab59", "name": "Sticky Note49", "type": "n8n-nodes-base.stickyNote", "position": [2700, 3180], "parameters": {"color": 7, "width": 1020, "height": 700, "content": "## Todoist Webhook Router Example (optional)\nSince Todoist sends the updates of all projects to a single webhook, the requests need to be routed accordingly.\nTo do so, create a separate workflow, each containing the flow above, except replace the webhook and switch nodes with an \"Execute Workflow Trigger\".\nThen activate the nodes below and configure it, so it calls the sub-workflows accordingly."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "b1b38cc8-3dda-43de-9fd3-8341b5fcd798", "name": "Sticky Note51", "type": "n8n-nodes-base.stickyNote", "position": [4020, 2800], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Using the event type from Todoist is the only option to actually recognize, if a task was deleted"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "5eb3caf9-ae18-47ce-80c1-52b1029e627d", "name": "Sticky Note52", "type": "n8n-nodes-base.stickyNote", "position": [5560, 2660], "parameters": {"color": 7, "width": 220, "height": 280, "content": "The mapping is more advanced and also includes fall-back values for edge-cases"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "d8935c51-34a4-4d69-890e-23a7194ba5ea", "name": "Sticky Note53", "type": "n8n-nodes-base.stickyNote", "position": [6220, 2660], "parameters": {"color": 7, "width": 220, "height": 280, "content": "Previous node always returns output data, since it uses a filter to retrieve a matching item"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "d7f254d6-026a-48bb-b3db-1104181db443", "name": "Sticky Note54", "type": "n8n-nodes-base.stickyNote", "position": [7140, 2380], "parameters": {"color": 7, "width": 220, "height": 240, "content": "Reverse mapping the Section/Status"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "68536590-3bd2-443a-804a-e433910b0ccc", "name": "Sticky Note55", "type": "n8n-nodes-base.stickyNote", "position": [7360, 2380], "parameters": {"color": 7, "width": 440, "height": 240, "content": "Update section for in Todoist using the Sync API in full sync mode (**maximum of 100 requests within a 15 minute period!**)"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "18013962-c950-4976-8639-b00b3c2c43ed", "name": "Sticky Note56", "type": "n8n-nodes-base.stickyNote", "position": [7580, 2140], "parameters": {"color": 7, "width": 220, "height": 240, "content": "Store more information, which was not available earlier"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "02b2cddc-565e-46b1-aaec-b3fc3db0611c", "name": "Sticky Note57", "type": "n8n-nodes-base.stickyNote", "position": [8240, 2920], "parameters": {"color": 7, "width": 220, "height": 280, "content": "The Notion ID is being cached in Redis for 15 seconds, so the other Diff sync does not get triggered."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "9d1db117-f41a-4529-a2e8-503d65cff0ad", "name": "Sticky Note58", "type": "n8n-nodes-base.stickyNote", "position": [6460, 3580], "parameters": {"color": 7, "width": 1340, "height": 320, "content": "**Delete Notion page** if Todoist task has been deleted"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "9de565e0-3edb-4ea0-bace-8c17b10d5aac", "name": "Sticky Note59", "type": "n8n-nodes-base.stickyNote", "position": [7140, 3620], "parameters": {"color": 7, "width": 220, "height": 260, "content": "Store more information, which was not available earlier"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "b8aeecf7-28fb-433e-b045-147e6080cc30", "name": "Todoist Webhook", "type": "n8n-nodes-base.webhook", "position": [2760, 2900], "webhookId": "81107d16-d839-4507-a627-f607f1d53d4e", "parameters": {"path": "47b150f3-12a3-4fe9-97cc-5d139daa3b00", "options": {}, "httpMethod": "POST"}, "typeVersion": 2, "notes": "This webhook node performs automated tasks as part of the workflow."}, {"id": "d48d3e29-d1b1-4751-adad-692cddaf49e7", "name": "Sticky Note60", "type": "n8n-nodes-base.stickyNote", "position": [3600, -640], "parameters": {"color": 7, "width": 1980, "height": 860, "content": "Handle tasks **only found in Notion**. This can still mean, that the task exists in Todoist, but does not match the filter, since it has been completed. In that case the Todoist task is being retrieved. If it exist, the Todoist Task will be reopened, otherwise it will be created.\nThe Todoist ID is being cached in Redis for 15 seconds, so the Diff sync does not get triggered."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "bfbf04c7-8e27-4afd-998c-1720eb7f05bd", "name": "Sticky Note61", "type": "n8n-nodes-base.stickyNote", "position": [3600, 240], "parameters": {"color": 7, "width": 1760, "height": 640, "content": "Handle tasks which show **differences** between both systems. If there are differences, the Todoist task will be overwritten by all properties from Notion. Also the status is being updated in the following step accordingly.\nThe Todoist ID is being cached in Redis for 15 seconds, so the Diff sync does not get triggered."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "8edde5d8-649a-42a3-bf81-83c36ac1ff03", "name": "Sticky Note62", "type": "n8n-nodes-base.stickyNote", "position": [3600, 900], "parameters": {"color": 7, "width": 1760, "height": 720, "content": "Handle tasks **only found in Todoist**. This can still mean, that the task exists in Notion, but does not match the filter, since it has been completed. In that case the Notion page is being retrieved. If it does not exist, because it was deleted, the Todoist Task will be deleted as well.\nThe Todoist ID is being cached in Redis for 15 seconds, so the Diff sync does not get triggered."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "284199c4-40ba-4590-9eb8-1af4dedfedf4", "name": "Sticky Note63", "type": "n8n-nodes-base.stickyNote", "position": [2700, 360], "parameters": {"width": 220, "height": 280, "content": "## Set Schedule\nSet the preferred period. Usually once a day, likely outside working hours should be a solid option."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "2cad2f03-b24d-4146-883d-78bdd11ac127", "name": "Sticky Note64", "type": "n8n-nodes-base.stickyNote", "position": [3140, 560], "parameters": {"color": 7, "width": 220, "height": 200, "content": "Preparing data for comparing"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "453dd0b4-1163-46dd-849b-39179f8ddb7c", "name": "Sticky Note65", "type": "n8n-nodes-base.stickyNote", "position": [7140, 560], "parameters": {"width": 220, "height": 320, "content": "## Set mail address\nProvide the email address where the report should be sent to. This node can also be replaced by an SMTP or similar node."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "f77d8d57-0a5c-4b33-be07-9806c55330c3", "name": "Execute Workflow", "type": "n8n-nodes-base.executeWorkflow", "disabled": true, "position": [3520, 3440], "parameters": {"options": {}, "workflowId": {"__rl": true, "mode": "id", "value": ""}}, "typeVersion": 1.1, "notes": "This executeWorkflow node performs automated tasks as part of the workflow."}, {"id": "4bd15a48-39d1-41c3-8b15-7de6abf3d35d", "name": "Extract IDs", "type": "n8n-nodes-base.set", "position": [-2560, 2940], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "7f0c61dc-87ae-4fbe-9e40-d9d27a0d61a8", "name": "body.data.id", "type": "string", "value": "={{ $json.data.id }}"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "6e61887e-8022-44ee-b9ab-94058b08a07b", "name": "Split out Notion changes", "type": "n8n-nodes-base.splitOut", "position": [-2780, 2940], "parameters": {"options": {}, "fieldToSplitOut": "body"}, "typeVersion": 1, "notes": "This splitOut node performs automated tasks as part of the workflow."}, {"id": "5ed59854-c972-42bd-9b41-8e6c1d616c8f", "name": "Body is array?", "type": "n8n-nodes-base.if", "position": [-3000, 3040], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "0479a702-9ec0-4203-bcaa-e4bdbd872f4e", "operator": {"type": "object", "operation": "exists", "singleValue": true}, "leftValue": "={{ $json.body[0] }}", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "a6003e6f-84a7-4c5e-99d7-47b31dee10d1", "name": "Sticky Note66", "type": "n8n-nodes-base.stickyNote", "position": [-3060, 2880], "parameters": {"color": 7, "width": 660, "height": 340, "content": "If consuming data from the Notion Webhook Emulator instead of a regular Notion Webhook, the data is structured differently and can contain multiple items. This section makes the workflow compatible with both approaches."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "07740ae3-3472-4125-bae9-d1c54e9584f1", "name": "Switch by project", "type": "n8n-nodes-base.switch", "position": [2980, 2900], "parameters": {"rules": {"values": [{"outputKey": "YOUR_CREDENTIAL_HERE", "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "loose"}, "combinator": "and", "conditions": [{"operator": {"type": "string", "operation": "equals"}, "leftValue": "={{ $json.body.event_data.project_id }}", "rightValue": "2346249328"}]}, "renameOutput": true}]}, "options": {}, "looseTypeValidation": true}, "typeVersion": 3.2, "notes": "This switch node performs automated tasks as part of the workflow."}, {"id": "a76190bf-f467-4bbe-82f3-581c2ab27e2a", "name": "Sticky Note21", "type": "n8n-nodes-base.stickyNote", "position": [2700, 2680], "parameters": {"width": 220, "height": 400, "content": "## Register Webhook in Todoist App\nGrab the URL from here and add it to the Todosit developer app as described in the setup instructions"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "689992a3-c8bc-4d2e-856b-2c512afdb2da", "name": "Sticky Note67", "type": "n8n-nodes-base.stickyNote", "position": [2920, 2680], "parameters": {"width": 220, "height": 400, "content": "## Set project ID and name\nThe ID can be copied from JSON in Globals Node.\nIf syncing multiple Todoist projects, use the router described below."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "f2243125-18c3-4484-9a06-85c638b1895f", "name": "Webhook", "type": "n8n-nodes-base.webhook", "disabled": true, "position": [3080, 3540], "webhookId": "1d758ef5-a9b9-4313-b81e-1c7dfe230a33", "parameters": {"path": "1d758ef5-a9b9-4313-b81e-1c7dfe230a33", "options": {}}, "typeVersion": 2, "notes": "This webhook node performs automated tasks as part of the workflow."}, {"id": "f875a8c7-2223-4296-bd55-8f42789d11b7", "name": "Switch", "type": "n8n-nodes-base.switch", "disabled": true, "position": [3300, 3540], "parameters": {"rules": {"values": [{"outputKey": "YOUR_CREDENTIAL_HERE", "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"operator": {"type": "string", "operation": "equals"}, "leftValue": "={{ $json.body.event_data.project_id }}", "rightValue": ""}]}, "renameOutput": true}, {"outputKey": "YOUR_CREDENTIAL_HERE", "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "759db81d-4d1a-463f-9773-1db73b6a7829", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $json.body.event_data.project_id }}", "rightValue": ""}]}, "renameOutput": true}]}, "options": {}}, "typeVersion": 3.2, "notes": "This switch node performs automated tasks as part of the workflow."}, {"id": "e603a21c-238f-45a9-8d7d-d8ccabe25217", "name": "Execute Workflow1", "type": "n8n-nodes-base.executeWorkflow", "disabled": true, "position": [3520, 3640], "parameters": {"options": {}, "workflowId": {"__rl": true, "mode": "id", "value": ""}}, "typeVersion": 1.1, "notes": "This executeWorkflow node performs automated tasks as part of the workflow."}, {"id": "91fe0191-0c57-404b-95e8-c3257426ccde", "name": "Sticky Note50", "type": "n8n-nodes-base.stickyNote", "position": [2740, 3440], "parameters": {"width": 220, "height": 280, "content": "### Replace trigger\nUse this trigger in the sub-workflows instead"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "98bbef00-86b5-46b2-8242-45f8eec47de4", "name": "Sticky Note68", "type": "n8n-nodes-base.stickyNote", "position": [3020, 3420], "parameters": {"width": 220, "height": 300, "content": "### Register Webhook\nConfigure this webhook in the Todoist App as mentioned above"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "650d3da2-4dee-44ab-afcd-680fb0a0d74a", "name": "Sticky Note69", "type": "n8n-nodes-base.stickyNote", "position": [3240, 3380], "parameters": {"width": 220, "height": 340, "content": "### Set project IDs and names\nCopy each project ID from the JSON of the different Globals Nodes and name the branches by the projects."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "b9c01e3c-bb9c-4bfd-aa22-b1b4a7824e42", "name": "Sticky Note70", "type": "n8n-nodes-base.stickyNote", "position": [3460, 3320], "parameters": {"width": 220, "height": 500, "content": "### Select sub-workflows\nChoose the sub-workflows based on the branch of the Switch Node"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "007599a2-8c60-423c-80a8-7599ba709db8", "name": "Sticky Note72", "type": "n8n-nodes-base.stickyNote", "position": [-1920, -580], "parameters": {"width": 980, "height": 560, "content": "# 0. Start here\n### Notion\n- A database must already exist (get a basic template [here]({{ $env.WEBHOOK_URL }} with the following properties (case matters!):\n  - Text: \"Name\"\n  - Status: \"Status\", containing at least the options \"Backlog\", \"In progress\", \"Done\", \"Obsolete\"\n  - Select: \"Priority\", containing the options \"do first\", \"urgent\", \"important\"\n  - Date: \"Due\"\n  - Checkbox: \"Focus\"\n  - Text: \"Todoist ID\"\n### Todoist\n- A project must already exist with the same sections like defined as Status in Notion (except Done and Obsolete)\n### Redis\n- Create a [Free Redis Cloud]({{ $env.WEBHOOK_URL }} instance or self-host\n### Setup credentials in n8n\n- Notion: {{ $env.WEBHOOK_URL }}\n- Todoist: {{ $env.WEBHOOK_URL }}\n- Redis: {{ $env.WEBHOOK_URL }}\n### Clone workflow\n- Clone this workflow into your n8n environment and map the credentials (beware, Todoist credentials are also used in HTTP request nodes)\n- Check for every yellow sticky notes. They show instructions which need to be done once"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "a30814bb-205c-488b-aa0b-bf315f3e32b2", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [-1920, 0], "parameters": {"width": 2780, "height": 460, "content": "# 1. Generate config JSON for Globals Nodes\nThe sync workflows have a Node named \"Globals\", which contain mapping information for both Notion and Todoist. This flow helps to generate the config, using a multi-form.\n### How to use\n- Check if the credentials in the Notion and HTTP Request Nodes are set correctly\n- Activate this workflow\n- Open production URL of the form trigger and complete the form\n- Copy the resulting JSON code into all 3 \"Globals\" nodes"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "1f273075-0648-44b1-a8b7-7e226671293a", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [-1920, 480], "parameters": {"width": 800, "height": 680, "content": "# 2. Setup Todoist Webhook\n- In the Todoist App open Settings > Integrations > Developer > Build Integrations\n- Click \"Create a new app\"\n  - App name: \"Notion-Todoist Sync\" / Name of your n8n instance\n  - App service URL: (leave empty)\n- Click \"Create app\"\n- Update settings of the app:\n  - OAuth redirect URL: Grab the production URL of the \"OAuth redirect\" Node (from step 3 on the right)\n- Click on \"Save settings\", leave tab open\n\n- Activate this workflow\n- Open the Production URL of the \"Todoist Webhook Setup Helper\" Node (from step 3 on the right)\n- Copy the Client ID and secret from the App settings into the form and submit it\n- When prompted for permissions, click on \"Agree\"\n\n- Go back to App settings tab\n- Scroll further down to \"Webhook\"\n- Update settings of the app:\n  - Webhook callback URL: Grab the production URL of the \"Todoist Webhook\" Node (Todoist to Notion Diff sync)\n  - Select these options:\n    - item:added\n    - item:updated\n    - item:completed\n    - item:uncompleted\n    - item:deleted\n- Click \"Save webhook configuration\"\n- Click \"Activate webhook\"\n- Settings can now be closed"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "4915c92e-aca2-409a-909f-7b2efa440bed", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [60, 480], "parameters": {"width": 800, "height": 680, "content": "# 4. Setup Notion Webhook\nYou can choose from two options and also choose different options for different workspaces, e.g. optionA for a business related workspace and option B for a private workspace. Both options work similarly, option A is a bit easier to setup, yet it is more expensive.\n\n## Option A: Notion Webhook via Automation (paid plan required)\n- Create a new [automation]({{ $env.WEBHOOK_URL }} on the database which should be synced\n- Rename the automation to \"Notion-Todoist Sync\" or similar\n- Add trigger: \"Any property edited\"\n- Add action: \"Send webhook\"\n  - URL: Grab the production URL from the \"Notion Webhook\" Node of this workflow (Notion to Todoist Diff sync)\n  - Content: Select all existing properties\n- Click \"Save\"\n\n## Option B: Notion Webhook Emulator (no subscription required)\n- Follow the instructions here: {{ $env.WEBHOOK_URL }}\n- Use the Webhook URL from the \"Notion Webhook\" Node of this workflow to push the updates to"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "de16a551-7864-4758-9da0-8f25856ca33d", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [-1100, 480], "parameters": {"width": 1140, "height": 680, "content": "# 3. Activate Todoist Webhook\nWebhooks are a bit different in Todoist then usual. A Developer App needs to be created and once be connected to via OAuth, to activate the webhook functionality.\nThis flow only works when using the **production URL** of it's form trigger."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "921e8fea-c860-4943-b9f8-9dee42a36688", "name": "Due date empty", "type": "n8n-nodes-base.filter", "position": [7420, 2820], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "loose"}, "combinator": "and", "conditions": [{"id": "9e948541-8c3b-4b99-99ab-0966319500ac", "operator": {"type": "string", "operation": "empty", "singleValue": true}, "leftValue": "={{ $if($('Map Todoist to Notion1').item.json.due !== null, $('Map Todoist to Notion1').item.json.due.toDateTime().toISO(), null) }}", "rightValue": "null"}]}, "looseTypeValidation": true}, "typeVersion": 2.2, "notes": "This filter node performs automated tasks as part of the workflow."}, {"id": "6ae7b8f4-32b5-4a08-9061-07399fc87631", "name": "Has not been completed?", "type": "n8n-nodes-base.if", "position": [7200, 3100], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "a8595a9c-fc03-4bb1-b1fc-ecde223d19aa", "operator": {"type": "boolean", "operation": "false", "singleValue": true}, "leftValue": "={{ $('Get Todoist Task1').item.json.is_completed }}", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "dae00353-4b24-4ab1-a2c3-86ffd6860ae3", "name": "End here", "type": "n8n-nodes-base.noOp", "position": [5180, 2860], "parameters": {}, "typeVersion": 1, "notes": "This noOp node performs automated tasks as part of the workflow."}, {"id": "11685bda-e714-4183-87b5-47dd1dd45123", "name": "Retry limit reached", "type": "n8n-nodes-base.stopAndError", "position": [4320, 20], "parameters": {"errorMessage": "Retry limit reached"}, "typeVersion": 1, "notes": "This stopAndError node performs automated tasks as part of the workflow."}, {"id": "05386c19-1529-48e6-b158-1eaca54ac19f", "name": "Set tries", "type": "n8n-nodes-base.set", "position": [4320, -280], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "cd93a7f6-4c06-4e8a-8d0d-e812c5ec4bc5", "name": "tries", "type": "number", "value": "={{ $json.tries || 0 }}"}]}, "includeOtherFields": true}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "21d1ab05-c6fe-44c8-831c-095d24e7251f", "name": "Update tries", "type": "n8n-nodes-base.set", "position": [5200, 20], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "df3c9b29-afa6-4e08-868d-5b7e8202eefa", "name": "tries", "type": "number", "value": "={{ $('Set tries').item.json.tries + 1 }}"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "c67ba192-2207-47a3-a3c5-91d059bc4e91", "name": "Wait", "type": "n8n-nodes-base.wait", "position": [4980, 20], "webhookId": "9d3b561f-4afd-478c-8f6e-60641d4f1d0b", "parameters": {}, "typeVersion": 1.1, "notes": "This wait node performs automated tasks as part of the workflow."}, {"id": "ca20fd60-2b56-4590-ae22-a43c6dd05973", "name": "Catch known error", "type": "n8n-nodes-base.if", "position": [4760, -180], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "6a379b06-0b04-4ae4-9bf9-394bd40744b7", "operator": {"type": "string", "operation": "contains"}, "leftValue": "={{ $json.error }}", "rightValue": "could not be found"}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "feb6c1ec-2696-49df-94ed-e257361b92a0", "name": "If tries left", "type": "n8n-nodes-base.if", "position": [4100, 20], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "b18f784a-4386-4ced-a9e1-ce5a21ad036e", "operator": {"type": "number", "operation": "lt"}, "leftValue": "={{ $json.tries }}", "rightValue": 3}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "a7f467fb-4ef2-4251-aa0d-47b069fe1cd1", "name": "Sticky Note18", "type": "n8n-nodes-base.stickyNote", "position": [-200, 3060], "parameters": {"color": 7, "width": 1320, "height": 480, "content": "Retrieve Todoist task, which can be completed or deleted at this point.\nUsing advanced retry logic, since the \"Get many tasks\" endpoint does not return completed tasks."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "6b1cc5de-e44e-4a8a-ba28-b05273abba18", "name": "Retry limit reached1", "type": "n8n-nodes-base.stopAndError", "position": [80, 3340], "parameters": {"errorMessage": "Retry limit reached"}, "typeVersion": 1, "notes": "This stopAndError node performs automated tasks as part of the workflow."}, {"id": "5e38207a-bf7c-4d0d-9e19-ba387a5c68a9", "name": "Set tries1", "type": "n8n-nodes-base.set", "position": [80, 3140], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "cd93a7f6-4c06-4e8a-8d0d-e812c5ec4bc5", "name": "tries", "type": "number", "value": "={{ $json.tries || 0 }}"}]}, "includeOtherFields": true}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "1ba45fd2-c7e3-40de-a341-27a342892765", "name": "Update tries1", "type": "n8n-nodes-base.set", "position": [960, 3340], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "df3c9b29-afa6-4e08-868d-5b7e8202eefa", "name": "tries", "type": "number", "value": "={{ $('Set tries1').item.json.tries + 1 }}"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "a9696b6e-f134-46ba-9b25-862ae73df812", "name": "Wait1", "type": "n8n-nodes-base.wait", "position": [740, 3340], "webhookId": "9d3b561f-4afd-478c-8f6e-60641d4f1d0b", "parameters": {}, "typeVersion": 1.1, "notes": "This wait node performs automated tasks as part of the workflow."}, {"id": "84e88ff7-bf50-4911-8f30-7027d68abe2a", "name": "Catch known error1", "type": "n8n-nodes-base.if", "position": [520, 3240], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "6a379b06-0b04-4ae4-9bf9-394bd40744b7", "operator": {"type": "string", "operation": "contains"}, "leftValue": "={{ $json.error }}", "rightValue": "could not be found"}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "0482d2b4-4327-4a71-b473-9d12e07e24a7", "name": "If tries left1", "type": "n8n-nodes-base.if", "position": [-140, 3340], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "b18f784a-4386-4ced-a9e1-ce5a21ad036e", "operator": {"type": "number", "operation": "lt"}, "leftValue": "={{ $json.tries }}", "rightValue": 3}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "92ff51e9-b9c7-47b4-a0ed-a5a0fddd7ef7", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [4240, 2660], "parameters": {"color": 7, "width": 1320, "height": 600, "content": "Todoist webhooks can arrive late. To ensure the data is up to date, the payload must be retrieved again. Triggers on deletion can overlap, thus try 3 times, then just stop.\nUsing advanced retry logic, since the \"Get many tasks\" endpoint does not return completed tasks.\nNo more retries needed, if Task does not exist."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "a611b837-4c77-4092-a0ee-db5ed7eee17c", "name": "Set tries2", "type": "n8n-nodes-base.set", "position": [4520, 2760], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "cd93a7f6-4c06-4e8a-8d0d-e812c5ec4bc5", "name": "tries", "type": "number", "value": "={{ $json.tries || 0 }}"}]}, "includeOtherFields": true}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "ac1787c5-a25e-4916-a537-7e9a0854fdc9", "name": "Update tries2", "type": "n8n-nodes-base.set", "position": [5400, 3060], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "df3c9b29-afa6-4e08-868d-5b7e8202eefa", "name": "tries", "type": "number", "value": "={{ $('Set tries2').item.json.tries + 1 }}"}]}}, "typeVersion": 3.4, "notes": "This set node performs automated tasks as part of the workflow."}, {"id": "614e1fa0-aac4-425f-87e0-85ba084319cb", "name": "Wait2", "type": "n8n-nodes-base.wait", "position": [5180, 3060], "webhookId": "9d3b561f-4afd-478c-8f6e-60641d4f1d0b", "parameters": {}, "typeVersion": 1.1, "notes": "This wait node performs automated tasks as part of the workflow."}, {"id": "5c205ac1-0c4c-4441-b49f-6e0fd12ead4a", "name": "Catch known error2", "type": "n8n-nodes-base.if", "position": [4960, 2860], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "6a379b06-0b04-4ae4-9bf9-394bd40744b7", "operator": {"type": "string", "operation": "contains"}, "leftValue": "={{ $json.error }}", "rightValue": "could not be found"}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "e00655f6-fe5d-4d16-ac8f-303687aa17a9", "name": "If tries left2", "type": "n8n-nodes-base.if", "position": [4300, 3060], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "b18f784a-4386-4ced-a9e1-ce5a21ad036e", "operator": {"type": "number", "operation": "lt"}, "leftValue": "={{ $json.tries }}", "rightValue": 3}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "a78d5103-9c36-4660-8823-6dbc99a90b3b", "name": "Retry limit reached2", "type": "n8n-nodes-base.stopAndError", "position": [4520, 3060], "parameters": {"errorMessage": "Retry limit reached"}, "typeVersion": 1, "notes": "This stopAndError node performs automated tasks as part of the workflow."}, {"id": "a760a6a7-f93c-4674-a21e-d10867c523de", "name": "Sticky Note30", "type": "n8n-nodes-base.stickyNote", "position": [7800, 2140], "parameters": {"color": 7, "width": 220, "height": 240, "content": "Overwrite description with Notion URL"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "5a7190f7-78e7-4362-8129-72e461abb099", "name": "Turn Markdown into Notion Blocks", "type": "n8n-nodes-base.code", "position": [7640, 1960], "parameters": {"mode": "runOnceForEachItem", "jsCode": "// Get the markdown from the input\nconst markdown = $('Get Todoist Task').item.json.description || '';\n\n// Split the markdown into lines and remove empty lines\nconst lines = markdown.split('\\n').filter(line => line.trim());\n\n// Initialize result array\nconst result = [];\n\n// Helper function to split long text into chunks\nfunction splitIntoChunks(text, maxLength) {\n    const chunks = [];\n    let remaining = text;\n    \n    while (remaining.length > maxLength) {\n        // Find the last space before maxLength\n        let splitIndex = remaining.lastIndexOf(' ', maxLength);\n        // If no space found, force split at maxLength\n        if (splitIndex === -1) splitIndex = maxLength;\n        \n        chunks.push(remaining.substring(0, splitIndex).trim());\n        remaining = remaining.substring(splitIndex).trim();\n    }\n    \n    if (remaining.length > 0) {\n        chunks.push(remaining);\n    }\n    \n    return chunks;\n}\n\nfor (const line of lines) {\n    const trimmedLine = line.trim();\n    \n    // Skip if empty line\n    if (!trimmedLine) continue;\n    \n    // Check for different types of markdown elements\n    if (trimmedLine.startsWith('# ')) {\n        result.push({\n            type: 'heading_1',\n            content: trimmedLine.substring(2).trim()\n        });\n    }\n    else if (trimmedLine.startsWith('## ')) {\n        result.push({\n            type: 'heading_2',\n            content: trimmedLine.substring(3).trim()\n        });\n    }\n    else if (trimmedLine.startsWith('### ')) {\n        result.push({\n            type: 'heading_3',\n            content: trimmedLine.substring(4).trim()\n        });\n    }\n    else if (trimmedLine.match(/^\\d+\\./)) {\n        result.push({\n            type: 'numbered_list_item',\n            content: trimmedLine.replace(/^\\d+\\.\\s*/, '').trim()\n        });\n    }\n    else if (trimmedLine.startsWith('- ')) {\n        result.push({\n            type: 'bulleted_list_item',\n            content: trimmedLine.substring(2).trim()\n        });\n    }\n    else {\n        // For paragraphs, check if they need to be split\n        if (trimmedLine.length > 2000) {\n            const chunks = splitIntoChunks(trimmedLine, 2000);\n            for (const chunk of chunks) {\n                result.push({\n                    type: 'paragraph',\n                    content: chunk\n                });\n            }\n        } else {\n            result.push({\n                type: 'paragraph',\n                content: trimmedLine\n            });\n        }\n    }\n}\n\n// Return the result in the format n8n expects\nreturn { blocks: result };\n"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "edb96637-26f5-49e7-ad33-a9be019f874c", "name": "Handle each block separately", "type": "n8n-nodes-base.splitOut", "position": [7860, 1960], "parameters": {"options": {}, "fieldToSplitOut": "blocks"}, "typeVersion": 1, "notes": "This splitOut node performs automated tasks as part of the workflow."}, {"id": "8c4722b7-87ae-42e3-a97e-16f4bb112b74", "name": "Append Notion Block", "type": "n8n-nodes-base.notion", "position": [8080, 1960], "parameters": {"blockId": {"__rl": true, "mode": "id", "value": "={{ $('Create task in Notion').item.json.id }}"}, "blockUi": {"blockValues": [{"type": "={{ $json.type }}", "textContent": "={{ $json.content }}"}]}, "resource": "block"}, "credentials": {"notionApi": {"id": "5AJPpSLgA5nVypUf", "name": "Notion (octionic)"}}, "typeVersion": 2.2, "notes": "This notion node performs automated tasks as part of the workflow."}, {"id": "8ba90a10-f79d-4bac-af19-f61e7bbad979", "name": "Sticky Note34", "type": "n8n-nodes-base.stickyNote", "position": [7580, 1900], "parameters": {"color": 7, "width": 660, "height": 240, "content": "Copy task description initially to Notion, before it get's replaced in Todoist by the Notion URL"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "888f3904-cd9a-4b7b-ab7e-af2eda62aa68", "name": "Sticky Note40", "type": "n8n-nodes-base.stickyNote", "position": [60, 1180], "parameters": {"width": 800, "height": 380, "content": "# 5. Finishing Touches\n\n### Cleanup\nNow technically everything is ready, but it is missing better usability and maintainability. So start by moving (cut & paste) the 3 big flows (outlined in blue) to separate n8n workflows. You can name them as follows:\n- Notion-Todoist Full Sync (<workspace-name>)\n- Notion-Todoist Diff Sync (<workspace-name>)\n- Todoist-Notion Diff Sync (<workspace-name>)\n \nMake sure to **activate every workflow** which requires it.\nAlso save this workflow to avoid duplicates.\n\n### First run\n- Execute the **Notion-Todoist Full Sync** workflow manually first\n- Now that the initial sync is done and you can start using the realtime sync"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}], "active": false, "pinData": {}, "settings": {"executionOrder": "v1", "saveManualExecutions": true, "callerPolicy": "workflowsFromSameOwner", "errorWorkflow": null, "timezone": "UTC", "executionTimeout": 3600, "maxExecutions": 1000, "retryOnFail": true, "retryCount": 3, "retryDelay": 1000}, "versionId": "a3febb48-a786-4168-8e4f-61821e15858e", "connections": {"5e0488a3-270d-46b3-8b4b-f4ee459a3016": {"main": [[{"node": "error-handler-5e0488a3-270d-46b3-8b4b-f4ee459a3016-017f6cf9", "type": "main", "index": 0}], [{"node": "error-handler-5e0488a3-270d-46b3-8b4b-f4ee459a3016-fca9a795", "type": "main", "index": 0}], [{"node": "error-handler-5e0488a3-270d-46b3-8b4b-f4ee459a3016-0b94b233", "type": "main", "index": 0}], [{"node": "error-handler-5e0488a3-270d-46b3-8b4b-f4ee459a3016-855a78c5", "type": "main", "index": 0}], [{"node": "error-handler-5e0488a3-270d-46b3-8b4b-f4ee459a3016-9c4a90f3", "type": "main", "index": 0}], [{"node": "error-handler-5e0488a3-270d-46b3-8b4b-f4ee459a3016-e5e7f05c", "type": "main", "index": 0}], [{"node": "error-handler-5e0488a3-270d-46b3-8b4b-f4ee459a3016-0c6b63db", "type": "main", "index": 0}], [{"node": "error-handler-5e0488a3-270d-46b3-8b4b-f4ee459a3016-88cd3512", "type": "main", "index": 0}]]}, "e52b0a6d-4697-4c42-91c9-6c786752877c": {"main": [[{"node": "error-handler-e52b0a6d-4697-4c42-91c9-6c786752877c-d80ba345", "type": "main", "index": 0}], [{"node": "error-handler-e52b0a6d-4697-4c42-91c9-6c786752877c-6b185364", "type": "main", "index": 0}], [{"node": "error-handler-e52b0a6d-4697-4c42-91c9-6c786752877c-cc3c534a", "type": "main", "index": 0}], [{"node": "error-handler-e52b0a6d-4697-4c42-91c9-6c786752877c-7d9ff5b5", "type": "main", "index": 0}], [{"node": "error-handler-e52b0a6d-4697-4c42-91c9-6c786752877c-99d4766f", "type": "main", "index": 0}], [{"node": "error-handler-e52b0a6d-4697-4c42-91c9-6c786752877c-80298ef2", "type": "main", "index": 0}], [{"node": "error-handler-e52b0a6d-4697-4c42-91c9-6c786752877c-6dfd0ba3", "type": "main", "index": 0}], [{"node": "error-handler-e52b0a6d-4697-4c42-91c9-6c786752877c-a312e7dd", "type": "main", "index": 0}]]}, "b72c6f9f-f821-4859-b75d-192541ce0d34": {"main": [[{"node": "error-handler-b72c6f9f-f821-4859-b75d-192541ce0d34-3a15c61f", "type": "main", "index": 0}], [{"node": "error-handler-b72c6f9f-f821-4859-b75d-192541ce0d34-6a8f27c5", "type": "main", "index": 0}], [{"node": "error-handler-b72c6f9f-f821-4859-b75d-192541ce0d34-6368f322", "type": "main", "index": 0}], [{"node": "error-handler-b72c6f9f-f821-4859-b75d-192541ce0d34-559a101a", "type": "main", "index": 0}], [{"node": "error-handler-b72c6f9f-f821-4859-b75d-192541ce0d34-bde6f4ac", "type": "main", "index": 0}], [{"node": "error-handler-b72c6f9f-f821-4859-b75d-192541ce0d34-fec5728f", "type": "main", "index": 0}], [{"node": "error-handler-b72c6f9f-f821-4859-b75d-192541ce0d34-c3548583", "type": "main", "index": 0}], [{"node": "error-handler-b72c6f9f-f821-4859-b75d-192541ce0d34-ea116986", "type": "main", "index": 0}]]}, "d0adcb66-4817-44f9-ad7e-320cff86aefe": {"main": [[{"node": "error-handler-d0adcb66-4817-44f9-ad7e-320cff86aefe-78e3f447", "type": "main", "index": 0}], [{"node": "error-handler-d0adcb66-4817-44f9-ad7e-320cff86aefe-a431519f", "type": "main", "index": 0}], [{"node": "error-handler-d0adcb66-4817-44f9-ad7e-320cff86aefe-17b0cde4", "type": "main", "index": 0}], [{"node": "error-handler-d0adcb66-4817-44f9-ad7e-320cff86aefe-e1c82903", "type": "main", "index": 0}], [{"node": "error-handler-d0adcb66-4817-44f9-ad7e-320cff86aefe-ecb5b9db", "type": "main", "index": 0}], [{"node": "error-handler-d0adcb66-4817-44f9-ad7e-320cff86aefe-de856e39", "type": "main", "index": 0}], [{"node": "error-handler-d0adcb66-4817-44f9-ad7e-320cff86aefe-97b119b4", "type": "main", "index": 0}], [{"node": "error-handler-d0adcb66-4817-44f9-ad7e-320cff86aefe-ffe561cd", "type": "main", "index": 0}]]}, "4fda8614-308c-4d66-ac7d-c2491ab5a86a": {"main": [[{"node": "error-handler-4fda8614-308c-4d66-ac7d-c2491ab5a86a-948ef9d9", "type": "main", "index": 0}], [{"node": "error-handler-4fda8614-308c-4d66-ac7d-c2491ab5a86a-77d30073", "type": "main", "index": 0}], [{"node": "error-handler-4fda8614-308c-4d66-ac7d-c2491ab5a86a-1699498c", "type": "main", "index": 0}], [{"node": "error-handler-4fda8614-308c-4d66-ac7d-c2491ab5a86a-f2f8c596", "type": "main", "index": 0}], [{"node": "error-handler-4fda8614-308c-4d66-ac7d-c2491ab5a86a-6a7646d5", "type": "main", "index": 0}], [{"node": "error-handler-4fda8614-308c-4d66-ac7d-c2491ab5a86a-8648f76c", "type": "main", "index": 0}], [{"node": "error-handler-4fda8614-308c-4d66-ac7d-c2491ab5a86a-47edd77f", "type": "main", "index": 0}], [{"node": "error-handler-4fda8614-308c-4d66-ac7d-c2491ab5a86a-22d59075", "type": "main", "index": 0}]]}, "347fb99f-861f-483a-bc39-7341fe41439f": {"main": [[{"node": "error-handler-347fb99f-861f-483a-bc39-7341fe41439f-f7a2c8ba", "type": "main", "index": 0}], [{"node": "error-handler-347fb99f-861f-483a-bc39-7341fe41439f-5b5d574b", "type": "main", "index": 0}], [{"node": "error-handler-347fb99f-861f-483a-bc39-7341fe41439f-31f1482a", "type": "main", "index": 0}], [{"node": "error-handler-347fb99f-861f-483a-bc39-7341fe41439f-b4c3af8f", "type": "main", "index": 0}], [{"node": "error-handler-347fb99f-861f-483a-bc39-7341fe41439f-63f15e01", "type": "main", "index": 0}], [{"node": "error-handler-347fb99f-861f-483a-bc39-7341fe41439f-42eea218", "type": "main", "index": 0}], [{"node": "error-handler-347fb99f-861f-483a-bc39-7341fe41439f-6c7f8dde", "type": "main", "index": 0}], [{"node": "error-handler-347fb99f-861f-483a-bc39-7341fe41439f-bb252a46", "type": "main", "index": 0}]]}, "01ec4ae8-22df-45ef-8593-4f16e3571c2e": {"main": [[{"node": "error-handler-01ec4ae8-22df-45ef-8593-4f16e3571c2e-0748ebb9", "type": "main", "index": 0}], [{"node": "error-handler-01ec4ae8-22df-45ef-8593-4f16e3571c2e-7dbdc1b6", "type": "main", "index": 0}], [{"node": "error-handler-01ec4ae8-22df-45ef-8593-4f16e3571c2e-0cb534bf", "type": "main", "index": 0}], [{"node": "error-handler-01ec4ae8-22df-45ef-8593-4f16e3571c2e-68d1196b", "type": "main", "index": 0}], [{"node": "error-handler-01ec4ae8-22df-45ef-8593-4f16e3571c2e-469f05c7", "type": "main", "index": 0}], [{"node": "error-handler-01ec4ae8-22df-45ef-8593-4f16e3571c2e-9ced7107", "type": "main", "index": 0}], [{"node": "error-handler-01ec4ae8-22df-45ef-8593-4f16e3571c2e-d83bce1f", "type": "main", "index": 0}], [{"node": "error-handler-01ec4ae8-22df-45ef-8593-4f16e3571c2e-cf5fc4fd", "type": "main", "index": 0}]]}, "ca33c90f-3204-45e1-9d66-20e7e740a4d7": {"main": [[{"node": "error-handler-ca33c90f-3204-45e1-9d66-20e7e740a4d7-dceb6800", "type": "main", "index": 0}], [{"node": "error-handler-ca33c90f-3204-45e1-9d66-20e7e740a4d7-35904545", "type": "main", "index": 0}], [{"node": "error-handler-ca33c90f-3204-45e1-9d66-20e7e740a4d7-f2215f2b", "type": "main", "index": 0}], [{"node": "error-handler-ca33c90f-3204-45e1-9d66-20e7e740a4d7-39cd8567", "type": "main", "index": 0}], [{"node": "error-handler-ca33c90f-3204-45e1-9d66-20e7e740a4d7-1ee32aef", "type": "main", "index": 0}], [{"node": "error-handler-ca33c90f-3204-45e1-9d66-20e7e740a4d7-0cfa4bd2", "type": "main", "index": 0}], [{"node": "error-handler-ca33c90f-3204-45e1-9d66-20e7e740a4d7-f5234f19", "type": "main", "index": 0}], [{"node": "error-handler-ca33c90f-3204-45e1-9d66-20e7e740a4d7-4052f671", "type": "main", "index": 0}]]}, "4a538bb6-80f6-4920-b96a-0756e0dc5449": {"main": [[{"node": "error-handler-4a538bb6-80f6-4920-b96a-0756e0dc5449-720c51a3", "type": "main", "index": 0}], [{"node": "error-handler-4a538bb6-80f6-4920-b96a-0756e0dc5449-06d20c3d", "type": "main", "index": 0}], [{"node": "error-handler-4a538bb6-80f6-4920-b96a-0756e0dc5449-92094e34", "type": "main", "index": 0}], [{"node": "error-handler-4a538bb6-80f6-4920-b96a-0756e0dc5449-8b3a952e", "type": "main", "index": 0}], [{"node": "error-handler-4a538bb6-80f6-4920-b96a-0756e0dc5449-c64708cd", "type": "main", "index": 0}], [{"node": "error-handler-4a538bb6-80f6-4920-b96a-0756e0dc5449-294e905a", "type": "main", "index": 0}], [{"node": "error-handler-4a538bb6-80f6-4920-b96a-0756e0dc5449-4a86f7fa", "type": "main", "index": 0}], [{"node": "error-handler-4a538bb6-80f6-4920-b96a-0756e0dc5449-8fb74834", "type": "main", "index": 0}]]}, "28b4e10e-a9cb-40c0-9a2c-d45303f4b64e": {"main": [[{"node": "error-handler-28b4e10e-a9cb-40c0-9a2c-d45303f4b64e-92ea1cc2", "type": "main", "index": 0}], [{"node": "error-handler-28b4e10e-a9cb-40c0-9a2c-d45303f4b64e-88acbae3", "type": "main", "index": 0}], [{"node": "error-handler-28b4e10e-a9cb-40c0-9a2c-d45303f4b64e-ace2b8a0", "type": "main", "index": 0}], [{"node": "error-handler-28b4e10e-a9cb-40c0-9a2c-d45303f4b64e-ffc51962", "type": "main", "index": 0}], [{"node": "error-handler-28b4e10e-a9cb-40c0-9a2c-d45303f4b64e-214df4ec", "type": "main", "index": 0}], [{"node": "error-handler-28b4e10e-a9cb-40c0-9a2c-d45303f4b64e-c3b213be", "type": "main", "index": 0}], [{"node": "error-handler-28b4e10e-a9cb-40c0-9a2c-d45303f4b64e-2700ac6d", "type": "main", "index": 0}], [{"node": "error-handler-28b4e10e-a9cb-40c0-9a2c-d45303f4b64e-a348dbbd", "type": "main", "index": 0}]]}, "cee5f777-4763-490e-a89f-8ba715cfd5b8": {"main": [[{"node": "error-handler-cee5f777-4763-490e-a89f-8ba715cfd5b8-e81abfc3", "type": "main", "index": 0}], [{"node": "error-handler-cee5f777-4763-490e-a89f-8ba715cfd5b8-20b4a3d0", "type": "main", "index": 0}], [{"node": "error-handler-cee5f777-4763-490e-a89f-8ba715cfd5b8-0c9251c6", "type": "main", "index": 0}], [{"node": "error-handler-cee5f777-4763-490e-a89f-8ba715cfd5b8-c524330d", "type": "main", "index": 0}], [{"node": "error-handler-cee5f777-4763-490e-a89f-8ba715cfd5b8-175a2f3f", "type": "main", "index": 0}], [{"node": "error-handler-cee5f777-4763-490e-a89f-8ba715cfd5b8-2800a330", "type": "main", "index": 0}], [{"node": "error-handler-cee5f777-4763-490e-a89f-8ba715cfd5b8-374bed1f", "type": "main", "index": 0}], [{"node": "error-handler-cee5f777-4763-490e-a89f-8ba715cfd5b8-3695e9ab", "type": "main", "index": 0}]]}, "106f7e35-0cc2-4054-817c-6c3d50407e88": {"main": [[{"node": "error-handler-106f7e35-0cc2-4054-817c-6c3d50407e88-69abe5da", "type": "main", "index": 0}], [{"node": "error-handler-106f7e35-0cc2-4054-817c-6c3d50407e88-a4d5d232", "type": "main", "index": 0}], [{"node": "error-handler-106f7e35-0cc2-4054-817c-6c3d50407e88-c6d6eb43", "type": "main", "index": 0}], [{"node": "error-handler-106f7e35-0cc2-4054-817c-6c3d50407e88-116b008f", "type": "main", "index": 0}], [{"node": "error-handler-106f7e35-0cc2-4054-817c-6c3d50407e88-ff75326f", "type": "main", "index": 0}], [{"node": "error-handler-106f7e35-0cc2-4054-817c-6c3d50407e88-8168c9cf", "type": "main", "index": 0}], [{"node": "error-handler-106f7e35-0cc2-4054-817c-6c3d50407e88-286b2d8c", "type": "main", "index": 0}], [{"node": "error-handler-106f7e35-0cc2-4054-817c-6c3d50407e88-bc82e5ea", "type": "main", "index": 0}]]}, "1895a6d2-71dc-4f58-a746-9b59c71bedcb": {"main": [[{"node": "error-handler-1895a6d2-71dc-4f58-a746-9b59c71bedcb-349dac5a", "type": "main", "index": 0}], [{"node": "error-handler-1895a6d2-71dc-4f58-a746-9b59c71bedcb-092c8e0c", "type": "main", "index": 0}], [{"node": "error-handler-1895a6d2-71dc-4f58-a746-9b59c71bedcb-37b1e771", "type": "main", "index": 0}], [{"node": "error-handler-1895a6d2-71dc-4f58-a746-9b59c71bedcb-a76d2010", "type": "main", "index": 0}], [{"node": "error-handler-1895a6d2-71dc-4f58-a746-9b59c71bedcb-a11e2255", "type": "main", "index": 0}], [{"node": "error-handler-1895a6d2-71dc-4f58-a746-9b59c71bedcb-cdbdc9f8", "type": "main", "index": 0}], [{"node": "error-handler-1895a6d2-71dc-4f58-a746-9b59c71bedcb-8c1db354", "type": "main", "index": 0}], [{"node": "error-handler-1895a6d2-71dc-4f58-a746-9b59c71bedcb-81b8440c", "type": "main", "index": 0}]]}, "baa2d8ad-3eee-46ec-bd20-cea4615f5cec": {"main": [[{"node": "error-handler-baa2d8ad-3eee-46ec-bd20-cea4615f5cec-63810387", "type": "main", "index": 0}], [{"node": "error-handler-baa2d8ad-3eee-46ec-bd20-cea4615f5cec-fb4de95b", "type": "main", "index": 0}], [{"node": "error-handler-baa2d8ad-3eee-46ec-bd20-cea4615f5cec-3da3cb43", "type": "main", "index": 0}], [{"node": "error-handler-baa2d8ad-3eee-46ec-bd20-cea4615f5cec-e626e306", "type": "main", "index": 0}], [{"node": "error-handler-baa2d8ad-3eee-46ec-bd20-cea4615f5cec-1f5df045", "type": "main", "index": 0}], [{"node": "error-handler-baa2d8ad-3eee-46ec-bd20-cea4615f5cec-d3d4042a", "type": "main", "index": 0}], [{"node": "error-handler-baa2d8ad-3eee-46ec-bd20-cea4615f5cec-0068eb40", "type": "main", "index": 0}], [{"node": "error-handler-baa2d8ad-3eee-46ec-bd20-cea4615f5cec-2c416622", "type": "main", "index": 0}]]}, "00cea352-9026-433b-b351-28720e0b6a40": {"main": [[{"node": "error-handler-00cea352-9026-433b-b351-28720e0b6a40-19797b30", "type": "main", "index": 0}], [{"node": "error-handler-00cea352-9026-433b-b351-28720e0b6a40-cc652ed6", "type": "main", "index": 0}], [{"node": "error-handler-00cea352-9026-433b-b351-28720e0b6a40-15d1b721", "type": "main", "index": 0}], [{"node": "error-handler-00cea352-9026-433b-b351-28720e0b6a40-220e4f76", "type": "main", "index": 0}], [{"node": "error-handler-00cea352-9026-433b-b351-28720e0b6a40-45264c8e", "type": "main", "index": 0}], [{"node": "error-handler-00cea352-9026-433b-b351-28720e0b6a40-72ad83e9", "type": "main", "index": 0}], [{"node": "error-handler-00cea352-9026-433b-b351-28720e0b6a40-e538ba71", "type": "main", "index": 0}], [{"node": "error-handler-00cea352-9026-433b-b351-28720e0b6a40-94040e75", "type": "main", "index": 0}]]}, "05441894-9904-44b3-94f7-bb600bcbe7ba": {"main": [[{"node": "error-handler-05441894-9904-44b3-94f7-bb600bcbe7ba-a346db67", "type": "main", "index": 0}], [{"node": "error-handler-05441894-9904-44b3-94f7-bb600bcbe7ba-5d444d90", "type": "main", "index": 0}], [{"node": "error-handler-05441894-9904-44b3-94f7-bb600bcbe7ba-e5756f4a", "type": "main", "index": 0}], [{"node": "error-handler-05441894-9904-44b3-94f7-bb600bcbe7ba-020ce299", "type": "main", "index": 0}], [{"node": "error-handler-05441894-9904-44b3-94f7-bb600bcbe7ba-1ab52860", "type": "main", "index": 0}], [{"node": "error-handler-05441894-9904-44b3-94f7-bb600bcbe7ba-a5dcf11c", "type": "main", "index": 0}], [{"node": "error-handler-05441894-9904-44b3-94f7-bb600bcbe7ba-bba47e16", "type": "main", "index": 0}], [{"node": "error-handler-05441894-9904-44b3-94f7-bb600bcbe7ba-e574e281", "type": "main", "index": 0}]]}, "b8aeecf7-28fb-433e-b045-147e6080cc30": {"main": [[{"node": "error-handler-b8aeecf7-28fb-433e-b045-147e6080cc30-9d63c251", "type": "main", "index": 0}], [{"node": "error-handler-b8aeecf7-28fb-433e-b045-147e6080cc30-05675fd6", "type": "main", "index": 0}], [{"node": "error-handler-b8aeecf7-28fb-433e-b045-147e6080cc30-291c1c69", "type": "main", "index": 0}], [{"node": "error-handler-b8aeecf7-28fb-433e-b045-147e6080cc30-40905dba", "type": "main", "index": 0}], [{"node": "error-handler-b8aeecf7-28fb-433e-b045-147e6080cc30-6bef4904", "type": "main", "index": 0}], [{"node": "error-handler-b8aeecf7-28fb-433e-b045-147e6080cc30-51620de2", "type": "main", "index": 0}], [{"node": "error-handler-b8aeecf7-28fb-433e-b045-147e6080cc30-bfe35077", "type": "main", "index": 0}], [{"node": "error-handler-b8aeecf7-28fb-433e-b045-147e6080cc30-c1874d50", "type": "main", "index": 0}]]}, "f2243125-18c3-4484-9a06-85c638b1895f": {"main": [[{"node": "error-handler-f2243125-18c3-4484-9a06-85c638b1895f-1c292a6a", "type": "main", "index": 0}], [{"node": "error-handler-f2243125-18c3-4484-9a06-85c638b1895f-1f3d1af6", "type": "main", "index": 0}], [{"node": "error-handler-f2243125-18c3-4484-9a06-85c638b1895f-0a16ca55", "type": "main", "index": 0}], [{"node": "error-handler-f2243125-18c3-4484-9a06-85c638b1895f-7cb357c4", "type": "main", "index": 0}], [{"node": "error-handler-f2243125-18c3-4484-9a06-85c638b1895f-53b98cbd", "type": "main", "index": 0}], [{"node": "error-handler-f2243125-18c3-4484-9a06-85c638b1895f-f0c5204a", "type": "main", "index": 0}], [{"node": "error-handler-f2243125-18c3-4484-9a06-85c638b1895f-f65488f0", "type": "main", "index": 0}], [{"node": "error-handler-f2243125-18c3-4484-9a06-85c638b1895f-a57cea23", "type": "main", "index": 0}]]}}, "description": "Automated workflow: Realtime Notion Todoist 2-way Sync Template. This workflow integrates 29 different services: stickyNote, scheduleTrigger, merge, switch, redis. It contains 264 nodes and follows best practices for error handling and security.", "notes": "Excellent quality workflow: Realtime Notion Todoist 2-way Sync Template. This workflow has been optimized for production use with comprehensive error handling, security, and documentation."}