This workflow corresponds to n8n.io template #11716 — we link there as the canonical source.
This workflow follows the Form → Form Trigger recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"nodes": [
{
"id": "6d2c347e-0f99-46ca-814d-41591d67b339",
"name": "Has been completed?2",
"type": "n8n-nodes-base.if",
"position": [
3760,
16256
],
"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 Todoist').item.json.checked }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "03358d46-ee0c-4ab9-a71b-63e4fd448986",
"name": "Only continue if not locked3",
"type": "n8n-nodes-base.filter",
"position": [
976,
16752
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "6b3359cb-c8fc-4663-b2a9-e367ad4fcba5",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $json.item }}",
"rightValue": "=Executed"
},
{
"id": "95985927-a020-4851-a5ca-9312949da3e4",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $json.item }}",
"rightValue": "SmallExecuted"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "edb0e1aa-dd1c-4489-be9d-76c7132ad269",
"name": "Check if Notion ID is locked2",
"type": "n8n-nodes-base.redis",
"position": [
752,
16752
],
"parameters": {
"key": "=lock_{{ $json.body.entity.id }}",
"keyType": "string",
"options": {},
"operation": "get",
"propertyName": "item"
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "436f3883-48f7-4d05-9894-ca039a2df11c",
"name": "Get Notion task2",
"type": "n8n-nodes-base.notion",
"maxTries": 2,
"position": [
1200,
16752
],
"parameters": {
"pageId": {
"__rl": true,
"mode": "id",
"value": "={{$('Notion Trigger')\n.item.json.body.entity.id }}"
},
"simple": false,
"resource": "databasePage",
"operation": "get"
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 2.2,
"waitBetweenTries": 3000
},
{
"id": "e45cc5b7-8502-4a36-8ccf-7ca803ad0a12",
"name": "Check if creating flag exists3",
"type": "n8n-nodes-base.redis",
"position": [
3920,
16880
],
"parameters": {
"key": "=creating_for_{{ $('Notion Trigger').item.json.body.data.parent.id }}",
"keyType": "string",
"options": {},
"operation": "get",
"propertyName": "item"
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "a32c470d-fcc9-4cc1-920e-b4f421cbe0cd",
"name": "Only continue if flag does not exist3",
"type": "n8n-nodes-base.filter",
"position": [
4128,
16880
],
"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
},
{
"id": "aa1b4d65-7ce7-4608-9743-f7acd5c5a91d",
"name": "Set creating flag3",
"type": "n8n-nodes-base.redis",
"position": [
4320,
16880
],
"parameters": {
"key": "=creating_for_{{ $('Notion Trigger').item.json.body.data.parent.id }}",
"ttl": 10,
"value": "={{ $now.toISO() }}",
"expire": true,
"keyType": "string",
"operation": "set"
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "4a804851-f3e0-4b3c-8b1a-e601f62e074e",
"name": "Todoist ID exists?2",
"type": "n8n-nodes-base.if",
"position": [
2096,
16752
],
"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
},
{
"id": "443d12b9-e6f1-42b3-8eaa-b45a066e0902",
"name": "Get todoist ID4",
"type": "n8n-nodes-base.set",
"position": [
4288,
16256
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "be85c68d-e9a8-4a88-ae12-54ec9cc685e5",
"name": "id",
"type": "string",
"value": "={{ $('Get Todoist Task3').item.json.id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "22f4d0cc-d538-4957-9890-c4da4aad1c66",
"name": "Todoist ID exists2",
"type": "n8n-nodes-base.filter",
"position": [
3648,
15744
],
"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
},
{
"id": "9f49048e-5122-4c59-b309-5300cec6fd90",
"name": "Get todoist ID5",
"type": "n8n-nodes-base.set",
"position": [
4288,
15744
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "be85c68d-e9a8-4a88-ae12-54ec9cc685e5",
"name": "id",
"type": "string",
"value": "={{ $('Todoist ID exists2').item.json.id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "775827ef-151d-4987-9617-7d7726e6865e",
"name": "Status is not Done2",
"type": "n8n-nodes-base.filter",
"position": [
3664,
16720
],
"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 task2').item.json.properties['Status'].status.name }}",
"rightValue": "Done"
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "b54b3195-c9eb-4822-b396-abebff98fa16",
"name": "Mark as Completed in Todoist2",
"type": "n8n-nodes-base.todoist",
"position": [
3904,
16160
],
"parameters": {
"taskId": "={{ $('Get Todoist Task3').item.json.id }}",
"operation": "close"
},
"credentials": {
"todoistApi": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 2.1,
"waitBetweenTries": 3000
},
{
"id": "bc05aaf9-0d16-4d54-9b40-2bdd00aa7558",
"name": "Update task in Todoist2",
"type": "n8n-nodes-base.httpRequest",
"position": [
3904,
15984
],
"parameters": {
"url": "=https://api.todoist.com/api/v1/tasks/{{ $json.id }}",
"method": "POST",
"options": {},
"jsonQuery": "={{ $('Map Notion to Todoist').item.json.toJsonString() }}",
"sendQuery": true,
"specifyQuery": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "todoistApi"
},
"credentials": {
"todoistApi": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2,
"waitBetweenTries": 3000
},
{
"id": "33e1d27d-1da7-4484-a118-a2f8080ee642",
"name": "Mark as Incomplete in Todoist2",
"type": "n8n-nodes-base.todoist",
"position": [
3904,
16320
],
"parameters": {
"taskId": "={{ $json.id }}",
"operation": "reopen"
},
"credentials": {
"todoistApi": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 2.1,
"waitBetweenTries": 3000
},
{
"id": "69aa7795-12ce-4282-99f3-794eeb95712a",
"name": "Lock Todoist ID5",
"type": "n8n-nodes-base.redis",
"position": [
5488,
16128
],
"parameters": {
"key": "=lock_{{ $json.id }}",
"ttl": 80,
"value": "=Executed",
"expire": true,
"keyType": "string",
"operation": "set"
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "ce543935-618f-48df-b766-9d15c5d9c196",
"name": "Delete Task in Todoist3",
"type": "n8n-nodes-base.todoist",
"onError": "continueRegularOutput",
"position": [
3904,
15744
],
"parameters": {
"taskId": "={{ $json.id }}",
"operation": "delete"
},
"credentials": {
"todoistApi": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 2.1,
"waitBetweenTries": 3000
},
{
"id": "31f15117-77e5-4d7f-a6c6-c49fe2c3ee7e",
"name": "Create task in Todoist2",
"type": "n8n-nodes-base.httpRequest",
"position": [
4544,
16880
],
"parameters": {
"url": "=https://api.todoist.com/api/v1/tasks",
"method": "POST",
"options": {},
"jsonQuery": "={{ $('Map Notion to Todoist').item.json.toJsonString() }}",
"sendQuery": true,
"specifyQuery": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "todoistApi"
},
"credentials": {
"todoistApi": {
"name": "<your credential>"
}
},
"retryOnFail": true,
"typeVersion": 4.2,
"waitBetweenTries": 3000
},
{
"id": "c9aaa540-b1b3-4884-9f81-447be03c6f1f",
"name": "Execution Data10",
"type": "n8n-nodes-base.executionData",
"position": [
1648,
16752
],
"parameters": {
"dataToSave": {
"values": [
{
"key": "todoist_id",
"value": "={{ $json.id }}"
},
{
"key": "task_name",
"value": "={{ $json.content }}"
}
]
}
},
"typeVersion": 1
},
{
"id": "b7aec9bb-e88c-493a-b780-a5a42aad43b4",
"name": "Sticky Note77",
"type": "n8n-nodes-base.stickyNote",
"position": [
3584,
15616
],
"parameters": {
"color": 7,
"width": 852,
"height": 300,
"content": "**Delete the task** if it does not fit into the filter anymore"
},
"typeVersion": 1
},
{
"id": "d2cdbd35-b726-47c9-a708-e82f375fdf6d",
"name": "Sticky Note78",
"type": "n8n-nodes-base.stickyNote",
"position": [
3584,
15936
],
"parameters": {
"color": 7,
"width": 852,
"height": 624,
"content": "**Update the task** if it needs to be changed "
},
"typeVersion": 1
},
{
"id": "5d28c45b-a753-4daf-9bb0-96232606e8c3",
"name": "Sticky Note79",
"type": "n8n-nodes-base.stickyNote",
"position": [
1136,
16656
],
"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
},
{
"id": "cb5d267d-23e3-455a-99f7-5043fd0ee799",
"name": "Sticky Note81",
"type": "n8n-nodes-base.stickyNote",
"position": [
3776,
15664
],
"parameters": {
"color": 7,
"width": 220,
"height": 240,
"content": "Continues on error, so it is not necessary to retrieve task beforehand"
},
"typeVersion": 1
},
{
"id": "604e77ba-9183-4bff-8800-510a93244ec8",
"name": "Sticky Note82",
"type": "n8n-nodes-base.stickyNote",
"position": [
16,
16656
],
"parameters": {
"color": 7,
"width": 220,
"height": 280,
"content": "Following nodes reference to this node, so the trigger can easily be replaced"
},
"typeVersion": 1
},
{
"id": "349a1b02-bb45-439d-bb18-8e0eaee45d78",
"name": "Sticky Note83",
"type": "n8n-nodes-base.stickyNote",
"position": [
464,
16656
],
"parameters": {
"color": 7,
"width": 220,
"height": 280,
"content": "Store relevant information in highlighted execution data, in case of debugging"
},
"typeVersion": 1
},
{
"id": "f7edc270-27f6-49f6-bff2-3145b1169f71",
"name": "Sticky Note84",
"type": "n8n-nodes-base.stickyNote",
"position": [
1584,
16656
],
"parameters": {
"color": 7,
"width": 220,
"height": 280,
"content": "Store more information, which was not available earlier"
},
"typeVersion": 1
},
{
"id": "49c45513-ab8a-4bb9-8655-2d5827d395d7",
"name": "Sticky Note85",
"type": "n8n-nodes-base.stickyNote",
"position": [
1808,
16656
],
"parameters": {
"color": 7,
"width": 220,
"height": 280,
"content": ""
},
"typeVersion": 1
},
{
"id": "a47983a7-680a-4789-8f4d-3a5c8ab096f7",
"name": "Sticky Note86",
"type": "n8n-nodes-base.stickyNote",
"position": [
3856,
16800
],
"parameters": {
"color": 7,
"width": 612,
"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
},
{
"id": "c0650569-87bd-4c1b-a08a-cc4356b7e8b5",
"name": "Sticky Note87",
"type": "n8n-nodes-base.stickyNote",
"position": [
1360,
16656
],
"parameters": {
"color": 7,
"width": 220,
"height": 280,
"content": "The mapping is more advanced and also includes fall-back values for edge-cases"
},
"typeVersion": 1
},
{
"id": "2ab9d46c-0e93-457b-bfb8-a8499a235536",
"name": "Sticky Note89",
"type": "n8n-nodes-base.stickyNote",
"position": [
2032,
16656
],
"parameters": {
"color": 7,
"width": 220,
"height": 280,
"content": "The mapping between the Notion and Todoist tasks is stored in Notion"
},
"typeVersion": 1
},
{
"id": "cd45982f-d32f-4ea1-aaf8-19ae0076e8fa",
"name": "Sticky Note16",
"type": "n8n-nodes-base.stickyNote",
"position": [
5424,
16000
],
"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
},
{
"id": "4c2ad6e3-4384-4965-84cc-3a9514309c6e",
"name": "Sticky Note17",
"type": "n8n-nodes-base.stickyNote",
"position": [
3584,
16576
],
"parameters": {
"color": 7,
"width": 1308,
"height": 516,
"content": "**Create task** if it does not exist (anymore)"
},
"typeVersion": 1
},
{
"id": "99d3ad62-c2bc-4b2d-9ab9-182fe88d2be4",
"name": "Sticky Note91",
"type": "n8n-nodes-base.stickyNote",
"position": [
2256,
16656
],
"parameters": {
"color": 7,
"width": 1320,
"height": 432,
"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
},
{
"id": "f8cb5557-0afd-44d1-aafa-6dd7dd2f1272",
"name": "Retry limit reached3",
"type": "n8n-nodes-base.stopAndError",
"position": [
2752,
16896
],
"parameters": {
"errorMessage": "Retry limit reached"
},
"typeVersion": 1
},
{
"id": "5ffc5e8f-9248-47a8-8b21-4c36e503d029",
"name": "Wait3",
"type": "n8n-nodes-base.wait",
"position": [
3184,
16896
],
"parameters": {},
"typeVersion": 1.1
},
{
"id": "88ac8aec-58de-4d1c-b698-568ade2b2616",
"name": "Catch known error3",
"type": "n8n-nodes-base.if",
"position": [
2960,
16768
],
"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
},
{
"id": "58dbc51f-95dc-42a9-ba11-6a646953ce1b",
"name": "If tries left3",
"type": "n8n-nodes-base.if",
"position": [
2560,
16896
],
"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
},
{
"id": "1c205719-92a8-4c4c-8d5d-10521f2080a6",
"name": "Is Deleted?1",
"type": "n8n-nodes-base.if",
"position": [
1872,
16752
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "9118fe29-c592-430e-8877-57cb81e6bf75",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $('Get Notion task2').item.json.archived }}",
"rightValue": "Obsolete"
},
{
"id": "b274d333-06f9-4e9f-8955-92e9476bc2e7",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Get Notion task2').item.json.properties.Section.select.name }}",
"rightValue": "Obsolete"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "7d973e36-0d2c-4347-b780-ad70c1a4eee0",
"name": "Sticky Note92",
"type": "n8n-nodes-base.stickyNote",
"position": [
688,
16656
],
"parameters": {
"color": 7,
"width": 440,
"height": 280,
"content": "Checking for a cached flag in Redis, to prevent overrides and endless loops."
},
"typeVersion": 1
},
{
"id": "ba5efa65-fbb7-440a-b78d-cc846dfaeb50",
"name": "Sticky Note93",
"type": "n8n-nodes-base.stickyNote",
"position": [
3600,
16640
],
"parameters": {
"color": 7,
"width": 220,
"height": 240,
"content": "Edge case: the task has been moved from Obsolete to Done"
},
"typeVersion": 1
},
{
"id": "c3b7de05-17d1-4db6-9933-32bfc2b1eda1",
"name": "Sticky Note88",
"type": "n8n-nodes-base.stickyNote",
"position": [
-208,
16592
],
"parameters": {
"width": 220,
"height": 344,
"content": "## Notion 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
},
{
"id": "2dd58deb-8bd0-4d53-b004-ad49b695d056",
"name": "Sticky Note90",
"type": "n8n-nodes-base.stickyNote",
"position": [
240,
16592
],
"parameters": {
"width": 220,
"height": 344,
"content": "## Set Globals\nUse Sync Setup Helper Workflow to generate the JSON and paste it in every Globals Nodes"
},
"typeVersion": 1
},
{
"id": "fbaaf1c5-3f61-40d7-8d96-ea258cacdb6a",
"name": "Notion Trigger",
"type": "n8n-nodes-base.noOp",
"position": [
64,
16752
],
"parameters": {},
"typeVersion": 1
},
{
"id": "a0b7210b-de27-4970-a6ae-3d53d1ea03ff",
"name": "Lock Todoist ID6",
"type": "n8n-nodes-base.redis",
"position": [
5488,
16448
],
"parameters": {
"key": "=lock_{{ $json.id }}",
"ttl": 80,
"value": "=SmallExecuted",
"expire": true,
"keyType": "string",
"operation": "set"
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "9b5e889d-0516-42f1-a6a0-126763f77354",
"name": "Sticky Note80",
"type": "n8n-nodes-base.stickyNote",
"position": [
5424,
16320
],
"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
},
{
"id": "c84ded26-3d53-49e9-a469-6bac0ae15a07",
"name": "Map Notion to Todoist",
"type": "n8n-nodes-base.code",
"position": [
1424,
16752
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const globals = $('GlobalsN').item.json;\nlet eventData = $json;\nlet output = {};\n\n//Task id\noutput.id = eventData.properties['TodoistID'].rich_text.length > 0 ? eventData.properties['TodoistID'].rich_text[0].text.content : \"\";\n//Task id\n\n\n//Task Name\noutput.content =\neventData.properties['Task Name'].title.length > 0 ? eventData.properties['Task Name'].title[0].text.content :\n'[empty]';\n//Task Name\n\n//Priority\nconst priorityMap = {\n \"1\": \"4\",\n \"2\": \"3\",\n \"3\": \"2\",\n \"4\": \"1\"\n};\nconst priorityKey = String(eventData.priority);\noutput.priority = priorityMap.hasOwnProperty(priorityKey) ? priorityMap[priorityKey] : \"1\";\n//Priority\n\n//Section\nfor (let i = 0; i < globals.sections.length; i++) { \n if (eventData.properties.Section.select.name == (\"\\n\"+ globals.sections[i].Section_name + \"\\n\\n\\n\") || (globals.sections[i].Section_name) ){\n output.section_id = globals.sections[i].Section_id;\n break;\n }\n}\n//Section\n\n//Completed\nif(eventData.properties.Status.checkbox == false){\n output.checked = false;\n}else if(eventData.properties.Status.checkbox == true){\n output.checked=true;\n}\n//Completed\n\n\n//Date\nif(eventData.properties.Due.date != null){\n const startDate = eventData.properties.Due.date.start;\n output.due_datetime = startDate;\n//Recurrence\n if(eventData.properties.Recurrence.rich_text.length !== 0){\n if(eventData.properties.Due.date.start.includes('T')){\n const time = startDate.split('T')[1].substring(0, 5);\n const hours = time.substring(0,2);\n const minutes = time.substring(2,5);\n const ampm = +hours >= 12 ? 'pm' : 'am';\n const hour12 = +hours % 12 === 0 ? 12 : hours % 12;\n output.due_string= eventData.properties.Recurrence.rich_text[0].text.content+ ` at ${hour12}${minutes} ${ampm}`; \n }else{\n output.due_string = eventData.properties.Recurrence.rich_text[0].text.content;\n }\n }\n//Recurrence\n//End Date\n if(eventData.properties.Due.date.end !== null){\n output.due_string = eventData.properties.Recurrence.rich_text[0].text.content+ \" until \" + eventData.properties.Due.date.end.split('T')[0];\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\n\n\n//Reminder\nif (output.due_datetime) {\n output.reminders = [\n { type: \"push\", minutes_before: 0 }\n ];\n}\n//Reminder\n\nreturn { json: output };"
},
"typeVersion": 2
},
{
"id": "22698f2d-bea4-457c-b92c-607efa97f30d",
"name": "GlobalsN",
"type": "n8n-nodes-base.set",
"position": [
304,
16752
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\"database_id\":\"27fcb946-5a19-80fe-baec-f102fdb6ad20\",\"project_id\":\"6c35QVGpP7p9mh9f\",\"sections\":[{\"Section_id\":\"6fJJ7f4Qv3WPqCqf\",\"Section_name\":\"Section1\"},{\"Section_id\":\"6fJJ7g5p34JVvXMf\",\"Section_name\":\"Section2\"},{\"Section_id\":\"6fJJ7h2hRwfRfH47\",\"Section_name\":\"Section3\"}]}"
},
"executeOnce": true,
"typeVersion": 3.4
},
{
"id": "e0b218ce-b531-4a1e-8b62-9eae4f47a6b7",
"name": "Notion Webhook",
"type": "n8n-nodes-base.webhook",
"disabled": true,
"position": [
-144,
16752
],
"parameters": {
"path": "notion-webhook2",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 2
},
{
"id": "9084a0c6-cff4-4177-8b25-7dd221dcac5a",
"name": "Update tries2",
"type": "n8n-nodes-base.set",
"position": [
3376,
16896
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "df3c9b29-afa6-4e08-868d-5b7e8202eefa",
"name": "tries",
"type": "number",
"value": "={{ $('Set tries2').item.json.tries + 1 }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "e9449def-7b96-49da-80d2-b63ec651784c",
"name": "Set tries2",
"type": "n8n-nodes-base.set",
"position": [
2432,
16736
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cd93a7f6-4c06-4e8a-8d0d-e812c5ec4bc5",
"name": "tries",
"type": "number",
"value": "={{ $json.tries || 0 }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "ef147fb7-a567-464c-a05e-3e0e45414222",
"name": "Store Todoist ID",
"type": "n8n-nodes-base.httpRequest",
"position": [
4752,
16960
],
"parameters": {
"url": "=https://api.notion.com/v1/pages/{{ $('Notion Trigger').item.json.body.entity.id }}",
"method": "PATCH",
"options": {},
"sendBody": true,
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "=properties.TodoistID.rich_text[0].text.content",
"value": "={{ $json.id }}"
}
]
},
"nodeCredentialType": "notionApi"
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "40908513-6458-45b5-8f47-405e6e915771",
"name": "Switch",
"type": "n8n-nodes-base.switch",
"position": [
3648,
16144
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c7b5055a-f459-433d-be42-357930db43fc",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Notion Trigger').item.json.body.type }}",
"rightValue": "page.properties_updated"
}
]
}
}
]
},
"options": {}
},
"typeVersion": 3.3
},
{
"id": "e4bd5848-ad4c-431f-9220-6870441d256f",
"name": "Get Todoist Task3",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
2736,
16736
],
"parameters": {
"url": "=https://api.todoist.com/api/v1/tasks/{{ $json.id }}",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "todoistApi"
},
"credentials": {
"todoistApi": {
"name": "<your credential>"
}
},
"retryOnFail": false,
"typeVersion": 4.2
},
{
"id": "37929dcd-48a9-48b6-94a6-50366f5f203a",
"name": "Filter",
"type": "n8n-nodes-base.filter",
"position": [
4096,
16080
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "742e02b1-70a6-476d-9500-c2f36ef29acf",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $('Map Notion to Todoist').item.json.due_string }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "1aec0326-8386-47d8-8f6b-bf74bdeff741",
"name": "Update Notion",
"type": "n8n-nodes-base.httpRequest",
"position": [
4288,
16080
],
"parameters": {
"url": "=https://api.notion.com/v1/pages/{{ $('Notion Trigger').item.json.body.entity.id }}",
"method": "PATCH",
"options": {},
"jsonBody": "{\n \"properties\": {\n \"Status\": {\n \"checkbox\": false\n }\n }\n}\n",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "notionApi"
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "cbb737af-7c08-4573-8fa5-f5b0f29cbcc7",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3168,
16160
],
"parameters": {
"width": 2752,
"height": 320,
"content": "# 1. Generate config JSON for Globals Nodes"
},
"typeVersion": 1
},
{
"id": "6e0619d7-8e33-44d1-8937-aae13bf84cd0",
"name": "Verify security token1",
"type": "n8n-nodes-base.if",
"position": [
-2640,
16944
],
"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 redirect1').first().json.query.state }}",
"rightValue": "={{ $json.state }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "b6f610ec-09a8-41ad-98f8-e55c770a3380",
"name": "Get variables1",
"type": "n8n-nodes-base.code",
"position": [
-2864,
16944
],
"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
},
{
"id": "e31415ea-7178-4749-959e-9818f74d1ae4",
"name": "Exchange Tokens1",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
-2416,
16880
],
"parameters": {
"url": "https://todoist.com/oauth/access_token",
"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 redirect1').first().json.query.code }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "72db1e28-786f-41b9-8e80-283c370a7cf1",
"name": "Respond with success1",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-2192,
16832
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "=Developer App activated successfully. The window can be closed now."
},
"typeVersion": 1.1
},
{
"id": "ea1c1a3c-ce9a-45ed-9aa9-7e862d3715ae",
"name": "Respond with error1",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-2192,
17040
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "Something went wrong."
},
"typeVersion": 1.1
},
{
"id": "3a7e36ea-9ef9-43ef-bd6b-dc44fc86a679",
"name": "OAuth redirect1",
"type": "n8n-nodes-base.webhook",
"position": [
-3088,
16944
],
"parameters": {
"path": "Todoist-outh",
"options": {},
"httpMethod": [
"GET"
],
"responseMode": "responseNode",
"multipleMethods": true
},
"typeVersion": 2
},
{
"id": "fe59fc24-94e4-423f-9773-bab2afefdaa2",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3168,
16480
],
"parameters": {
"width": 1152,
"height": 720,
"content": "\n# 2. Activate Todoist Webhook"
},
"typeVersion": 1
},
{
"id": "06079fb9-79af-495a-a3b7-b04025cae0c0",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3168,
15600
],
"parameters": {
"width": 1008,
"height": 560,
"content": "# 0. Start here\n### Notion\n- A database must already exist (get a basic template [here](https://steadfast-banjo-d1f.notion.site/17682b476c848086b002de766879aa71)) 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](https://redis.io/try-free/) instance or self-host\n### Setup credentials in n8n\n- Notion: https://docs.n8n.io/integrations/builtin/credentials/notion/\n- Todoist: https://docs.n8n.io/integrations/builtin/credentials/todoist/\n- Redis: https://docs.n8n.io/integrations/builtin/credentials/redis/\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
},
{
"id": "2b70e5db-7e05-4bb9-a4a2-2c2bb9d8b7c1",
"name": "Get projects1",
"type": "n8n-nodes-base.httpRequest",
"position": [
-2032,
16272
],
"parameters": {
"url": "https://api.todoist.com/api/v1/projects",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "todoistApi"
},
"credentials": {
"todoistApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "f6530df3-ceba-41e9-a104-6da568400a95",
"name": "Get sections1",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1152,
16272
],
"parameters": {
"url": "https://api.todoist.com/api/v1/sections",
"options": {},
"sendQuery": true,
"authentication": "predefinedCredentialType",
"queryParameters": {
"parameters": [
{
"name": "project_id",
"value": "={{ $json.project_id }}"
}
]
},
"nodeCredentialType": "todoistApi"
},
"credentials": {
"todoistApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "399ae7e3-1c2b-43e2-86a2-b9c8ed86df7f",
"name": "Get Notion Databases1",
"type": "n8n-nodes-base.notion",
"position": [
-2896,
16272
],
"parameters": {
"resource": "database",
"operation": "getAll"
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "6a3c3cf0-dcc4-496a-945c-cd920086471b",
"name": "Prep Dropdown2",
"type": "n8n-nodes-base.code",
"position": [
-2672,
16272
],
"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
},
{
"id": "ed95d163-1af6-4024-b06a-da62e8d67d29",
"name": "Prep Dropdown3",
"type": "n8n-nodes-base.code",
"position": [
-1824,
16272
],
"parameters": {
"jsCode": "let dropDownValues = [];\nconst results = $input.first().json.results;\nfor (const project of results) {\n dropDownValues.push({ option: project.name});\n}\n\nreturn { options: JSON.stringify(dropDownValues)};\n\n"
},
"typeVersion": 2
},
{
"id": "7fa03e90-2c15-47ff-89dc-bd9577fc1b63",
"name": "Generate config1",
"type": "n8n-nodes-base.code",
"position": [
-736,
16272
],
"parameters": {
"jsCode": "let sections = [];\nconst results = $('Get sections1').first().json.results;\nfor (const item of results) {\n sections.push({ Section_id: item.id, Section_name: item.name }); \n}\n\nreturn { json: \n{\n \"database_id\": $('Get Notion Database ID1').first().json.database_id,\n \"project_id\": $('Get Todoist Project ID1').first().json.project_id,\n \"sections\": sections,\n \"timezone\": $input.first().json['Select your Timezone']\n} \n};"
},
"typeVersion": 2
},
{
"id": "338b22c9-2a20-421d-8bb1-748fe5c42f44",
"name": "Choose Notion Database1",
"type": "n8n-nodes-base.form",
"position": [
-2448,
16272
],
"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
},
{
"id": "9978b743-370b-4c3e-a082-c7e6a860d53b",
"name": "Choose Todoist Project1",
"type": "n8n-nodes-base.form",
"position": [
-1600,
16272
],
"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
},
{
"id": "e6679b9f-afb9-4435-bae7-4ef8bc4fcae1",
"name": "Get Notion Database ID1",
"type": "n8n-nodes-base.code",
"position": [
-2240,
16272
],
"parameters": {
"jsCode": "let database_id = null;\n\nfor (const item of $('Get Notion Databases1').all()) {\n if (item.json.name == $('Choose Notion Database1').first().json['Select Notion Database']) {\n database_id = item.json.id;\n }\n}\n\nreturn { json: { database_id: database_id } };"
},
"typeVersion": 2
},
{
"id": "17d80134-7345-4866-b1ab-b4e89206fa1a",
"name": "Get Todoist Project ID1",
"type": "n8n-nodes-base.code",
"position": [
-1376,
16272
],
"parameters": {
"jsCode": "let project_id = null;\nconst results = $('Get projects1').first().json.results;\nfor (const item of results) {\n if (item.name == $('Choose Todoist Project1').first().json['Select Todoist Project']) {\n project_id = item.id;\n }\n}\n\nreturn { json: { project_id: project_id } };\n\n"
},
"typeVersion": 2
},
{
"id": "460fe318-2928-41fb-982b-f61fdf3bc504",
"name": "On form submission1",
"type": "n8n-nodes-base.formTrigger",
"position": [
-3120,
16272
],
"parameters": {
"options": {},
"formTitle": "Setup Helper",
"formDescription": "This is helper for setup."
},
"typeVersion": 2.3
},
{
"id": "c9814fc9-5ada-42af-a466-003e3ec89549",
"name": "Form1",
"type": "n8n-nodes-base.form",
"position": [
-528,
16272
],
"parameters": {
"options": {},
"operation": "completion",
"completionTitle": "Copy this to the Globals Nodes",
"completionMessage": "={{ $json.toJsonString() }}"
},
"typeVersion": 2.3
},
{
"id": "18a67ea2-9dd5-4878-8d8c-f6879312e4e3",
"name": "Generate security token1",
"type": "n8n-nodes-base.crypto",
"position": [
-2864,
16608
],
"parameters": {
"action": "generate",
"dataPropertyName": "state"
},
"typeVersion": 1
},
{
"id": "29576f4d-c051-47d3-889d-32eacd4fb152",
"name": "Store variables1",
"type": "n8n-nodes-base.code",
"position": [
-2640,
16608
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const staticData = $getWorkflowStaticData('global');\nstaticData.clientID = $('Todoist Webhook Setup Helper1').first().json['Client ID'];\nstaticData.clientSecret = $('Todoist Webhook Setup Helper1').first().json['Client secret'];\nstaticData.state = $json.state;\n\nreturn $input.item;"
},
"typeVersion": 2
},
{
"id": "5074b2e3-e9b5-45e2-9143-36d9c870e724",
"name": "Redirect to Auth Page1",
"type": "n8n-nodes-base.form",
"position": [
-2416,
16608
],
"parameters": {
"options": {},
"operation": "completion",
"redirectUrl": "=https://todoist.com/oauth/authorize?client_id={{ $json['Client ID'] }}&scope=task:add,data:read_write,data:delete&state={{ $json.state }}",
"respondWith": "redirect"
},
"typeVersion": 1
},
{
"id": "b199da1c-cfd0-41da-8a1f-fd2cc7e92b09",
"name": "Todoist Webhook Setup Helper1",
"type": "n8n-nodes-base.formTrigger",
"position": [
-3088,
16608
],
"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
},
{
"id": "9897d60f-ef22-4fd1-b4b4-e4db80d351ca",
"name": "Execution Data",
"type": "n8n-nodes-base.executionData",
"position": [
528,
16752
],
"parameters": {
"dataToSave": {
"values": [
{
"key": "database_id",
"value": "={{ $('GlobalsN').first().json.database_id }}"
},
{
"key": "project_id",
"value": "={{ $('GlobalsN').first().json.project_id }}"
},
{
"key": "notion_id",
"value": "={{ $('Notion Trigger').item.json.body.data.parent.id }}"
},
{
"key": "source",
"value": "Notion"
}
]
}
},
"typeVersion": 1
},
{
"id": "8d44d5ea-88cd-4380-a627-cf442204785b",
"name": "Differences exist",
"type": "n8n-nodes-base.filter",
"position": [
3760,
15984
],
"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 Todoist').item.json.content }}",
"rightValue": "={{ $json.content }}"
},
{
"id": "8a649292-a21e-4d3f-9e3c-0047b21a7487",
"operator": {
"type": "number",
"operation": "notEquals"
},
"leftValue": "={{ $('Map Notion to Todoist').item.json.priority }}",
"rightValue": "={{ $json.priority }}"
},
{
"id": "65f02b94-18de-4883-b5b1-f58fe5459828",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $('Map Notion to Todoist').item.json.due_datetime.split('.')[0] || null }}",
"rightValue": "={{ $json.due === null ? null : $json.due.date}}"
},
{
"id": "e3225b20-c6b6-479b-a0b5-dc9725197521",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $('Map Notion to Todoist').item.json.section_id }}",
"rightValue": "={{ $json.section_id }}"
},
{
"id": "3a5dba8d-cd7c-4ca1-a450-6503f679d2f7",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $('Map Notion to Todoist').item.json.due_string || null }}",
"rightValue": "={{ $json.due.string }}"
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "a8bbeb7e-9046-45cd-a42f-0ab4436e418e",
"name": "Choose Timezone1",
"type": "n8n-nodes-base.form",
"position": [
-944,
16272
],
"parameters": {
"options": {},
"defineForm": "json",
"jsonOutput": "[\n{\n \"fieldLabel\": \"Select your Timezone\",\n \"fieldType\": \"text\",\n \"requiredField\": true,\n \"placeholder\": \"America/New_York\"\n },\n{\n \"fieldType\": \"html\",\n \"fieldLabel\": \"Timezone Help\",\n \"html\": \"<p>Find your timezone at: <a href='https://www.iana.org/time-zones' target='_blank'>IANA Time Zones</a></p>\"\n }\n]"
},
"typeVersion": 1
},
{
"id": "cc3654e1-d9fa-43f9-91a2-9e33e2c3a554",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"position": [
2240,
16544
],
"parameters": {
"color": 7,
"width": 1344,
"height": 592,
"content": "## Todoist Task Checker"
},
"typeVersion": 1
},
{
"id": "25bab817-3f49-4c48-9738-a0c7dbfd3653",
"name": "Sticky Note13",
"type": "n8n-nodes-base.stickyNote",
"position": [
-368,
16544
],
"parameters": {
"color": 7,
"width": 1744,
"height": 560,
"content": "#Processing Data Coming from webhooks"
},
"typeVersion": 1
}
],
"connections": {
"Wait3": {
"main": [
[
{
"node": "Update tries2",
"type": "main",
"index": 0
}
]
]
},
"Filter": {
"main": [
[
{
"node": "Update Notion",
"type": "main",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "Differences exist",
"type": "main",
"index": 0
},
{
"node": "Has been completed?2",
"type": "main",
"index": 0
}
]
]
},
"GlobalsN": {
"main": [
[
{
"node": "Execution Data",
"type": "main",
"index": 0
}
]
]
},
"Set tries2": {
"main": [
[
{
"node": "Get Todoist Task3",
"type": "main",
"index": 0
}
]
]
},
"Is Deleted?1": {
"main": [
[
{
"node": "Todoist ID exists2",
"type": "main",
"index": 0
}
],
[
{
"node": "Todoist ID exists?2",
"type": "main",
"index": 0
}
]
]
},
"Get projects1": {
"main": [
[
{
"node": "Prep Dropdown3",
"type": "main",
"index": 0
}
]
]
},
"Get sections1": {
"main": [
[
{
"node": "Choose Timezone1",
"type": "main",
"index": 0
}
]
]
},
"Update tries2": {
"main": [
[
{
"node": "If tries left3",
"type": "main",
"index": 0
}
]
]
},
"Execution Data": {
"main": [
[
{
"node": "Check if Notion ID is locked2",
"type": "main",
"index": 0
}
]
]
},
"Get variables1": {
"main": [
[
{
"node": "Verify security token1",
"type": "main",
"index": 0
}
]
]
},
"If tries left3": {
"main": [
[
{
"node": "Set tries2",
"type": "main",
"index": 0
}
],
[
{
"node": "Retry limit reached3",
"type": "main",
"index": 0
}
]
]
},
"Notion Trigger": {
"main": [
[
{
"node": "GlobalsN",
"type": "main",
"index": 0
}
]
]
},
"Notion Webhook": {
"main": [
[
{
"node": "Notion Trigger",
"type": "main",
"index": 0
}
]
]
},
"Prep Dropdown2": {
"main": [
[
{
"node": "Choose Notion Database1",
"type": "main",
"index": 0
}
]
]
},
"Prep Dropdown3": {
"main": [
[
{
"node": "Choose Todoist Project1",
"type": "main",
"index": 0
}
]
]
},
"Get todoist ID4": {
"main": [
[
{
"node": "Lock Todoist ID5",
"type": "main",
"index": 0
}
]
]
},
"Get todoist ID5": {
"main": [
[
{
"node": "Lock Todoist ID5",
"type": "main",
"index": 0
}
]
]
},
"OAuth redirect1": {
"main": [
[
{
"node": "Get variables1",
"type": "main",
"index": 0
}
]
]
},
"Choose Timezone1": {
"main": [
[
{
"node": "Generate config1",
"type": "main",
"index": 0
}
]
]
},
"Exchange Tokens1": {
"main": [
[
{
"node": "Respond with success1",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond with error1",
"type": "main",
"index": 0
}
]
]
},
"Execution Data10": {
"main": [
[
{
"node": "Is Deleted?1",
"type": "main",
"index": 0
}
]
]
},
"Generate config1": {
"main": [
[
{
"node": "Form1",
"type": "main",
"index": 0
}
]
]
},
"Get Notion task2": {
"main": [
[
{
"node": "Map Notion to Todoist",
"type": "main",
"index": 0
}
]
]
},
"Store Todoist ID": {
"main": [
[
{
"node": "Lock Todoist ID6",
"type": "main",
"index": 0
}
]
]
},
"Store variables1": {
"main": [
[
{
"node": "Redirect to Auth Page1",
"type": "main",
"index": 0
}
]
]
},
"Differences exist": {
"main": [
[
{
"node": "Update task in Todoist2",
"type": "main",
"index": 0
}
]
]
},
"Get Todoist Task3": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
],
[
{
"node": "Catch known error3",
"type": "main",
"index": 0
}
]
]
},
"Catch known error3": {
"main": [
[
{
"node": "Status is not Done2",
"type": "main",
"index": 0
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
notionApiredistodoistApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
How this works
This two-way sync keeps tasks aligned between Notion and Todoist so users can edit in either tool without losing changes or duplicating work. It handles updates from both sides, supports calendar events, and uses Redis to prevent conflicts during simultaneous edits. The process checks locks and flags before pulling fresh data from Notion and pushing updates to Todoist, then reverses the flow.
Use it when teams need real-time consistency across both platforms without manual copying. Avoid it for simple one-off imports or when recurring tasks must be fully supported, as the workflow only manages partial recurrence. Variations include adjusting the webhook trigger for different update frequencies or adding extra filters to ignore specific task fields.
About this workflow
This solution enables you to manage all your Notion and Todoist tasks from different workspaces as well as your calendar events in a single place. This is 2 way sync with partial support for recurring The realtime sync consists of two workflows, both triggered by a registered…
Source: https://n8n.io/workflows/11716/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Notion to Clockify Sync Template. Uses scheduleTrigger, clockify, compareDatasets, stopAndError. Webhook trigger; 68 nodes.
This workflow synchronizes three entities from Notion to Clockify, allowing tracked time to be linked to client-related projects or tasks.
Stopanderror Splitout. Uses outputParserStructured, lmChatOpenAi, formTrigger, chainLlm. Event-driven trigger; 85 nodes.
2. Refresh Pipedrive tokens. Uses stopAndError, stickyNote, supabase, httpRequest. Webhook trigger; 29 nodes.
This workflow provides an OAuth 2.0 auth token refresh process for better control. Developers can utilize it as an alternative to n8n's built-in OAuth flow to achieve improved control and visibility.