8
n8n 中文网amn8n.com

实时Notion Todoist双向同步模板

高级

这是一个Sales, Engineering, Design, HR, Product, Marketing领域的自动化工作流,包含 246 个节点。主要使用 If, Set, Code, Form, Html 等节点。 使用Redis的Notion Todoist实时双向同步

前置要求
  • Google 账号和 Gmail API 凭证
  • Redis 服务器连接信息
  • Notion API Key
  • HTTP Webhook 端点(n8n 会自动生成)
  • 可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "k9abwUyVzl7OCsAl",
  "meta": {
    "instanceId": "fb8bc2e315f7f03c97140b30aa454a27bc7883a19000fa1da6e6b571bf56ad6d"
  },
  "name": "实时 Notion Todoist 双向同步模板",
  "tags": [
    {
      "id": "RKga6I6NviNI12bx",
      "name": "template",
      "createdAt": "2024-09-19T19:09:21.997Z",
      "updatedAt": "2024-09-19T19:09:21.997Z"
    }
  ],
  "nodes": [
    {
      "id": "5e0488a3-270d-46b3-8b4b-f4ee459a3016",
      "name": "获取项目",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -700,
        260
      ],
      "parameters": {
        "url": "https://api.todoist.com/rest/v2/projects",
        "options": {},
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e52b0a6d-4697-4c42-91c9-6c786752877c",
      "name": "获取部分",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        180,
        260
      ],
      "parameters": {
        "url": "https://api.todoist.com/rest/v2/sections",
        "options": {},
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "project_id",
              "value": "={{ $json.project_id }}"
            }
          ]
        },
        "nodeCredentialType": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8af0c04f-a46e-4fb8-ab5a-09952a3c1360",
      "name": "获取 Notion 数据库",
      "type": "n8n-nodes-base.notion",
      "position": [
        -1580,
        260
      ],
      "parameters": {
        "resource": "database",
        "operation": "getAll"
      },
      "credentials": {
        "notionApi": {
          "id": "ObmaBA0dJss3JJPv",
          "name": "Notion (Test)"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "09e43e00-6df6-4844-bda3-f5e8be0aed3a",
      "name": "准备下拉菜单",
      "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
    },
    {
      "id": "426872eb-228a-4b1c-b992-cf17893d29d2",
      "name": "准备下拉菜单1",
      "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
    },
    {
      "id": "bebc6443-6187-4b97-82ff-45b6ce75cd7e",
      "name": "生成配置",
      "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
    },
    {
      "id": "52692c9c-a490-4c2c-8df4-93870bf52ded",
      "name": "选择 Notion 数据库",
      "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
    },
    {
      "id": "7db73660-3f99-42b6-843d-ee4adf758441",
      "name": "选择 Todoist 项目",
      "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
    },
    {
      "id": "3fe51a88-765f-469e-9846-191506b0a894",
      "name": "验证安全令牌",
      "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
    },
    {
      "id": "7a040b65-5cc3-44d6-8bf6-c6bb7dfe5128",
      "name": "生成安全令牌",
      "type": "n8n-nodes-base.crypto",
      "position": [
        -800,
        620
      ],
      "parameters": {
        "action": "generate",
        "dataPropertyName": "state"
      },
      "typeVersion": 1
    },
    {
      "id": "7033d868-2d83-45bb-a48b-f4fd6eb14e0d",
      "name": "存储变量",
      "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
    },
    {
      "id": "d538d40a-599b-4255-a5e0-83d9b72f671e",
      "name": "获取变量",
      "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
    },
    {
      "id": "7c895d16-e522-4b63-bee6-0cea3e3177dd",
      "name": "重定向到认证页面",
      "type": "n8n-nodes-base.form",
      "position": [
        -360,
        620
      ],
      "webhookId": "41b4bc95-2938-4a53-a051-7a3079001329",
      "parameters": {
        "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": "b72c6f9f-f821-4859-b75d-192541ce0d34",
      "name": "OAuth 重定向",
      "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
    },
    {
      "id": "d0adcb66-4817-44f9-ad7e-320cff86aefe",
      "name": "交换令牌",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        -360,
        880
      ],
      "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 redirect').first().json.query.code }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4fda8614-308c-4d66-ac7d-c2491ab5a86a",
      "name": "响应成功",
      "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
    },
    {
      "id": "347fb99f-861f-483a-bc39-7341fe41439f",
      "name": "响应错误",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -140,
        980
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "Something went wrong."
      },
      "typeVersion": 1.1
    },
    {
      "id": "754727b6-1122-46b2-a956-79ed277c8b32",
      "name": "获取 Notion 数据库 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
    },
    {
      "id": "565f4144-549e-4adc-b51b-2d87767c3dc3",
      "name": "获取 Todoist 项目 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
    },
    {
      "id": "6f79cd9e-493a-46b2-a3fb-853852a299be",
      "name": "Notion-Todoist 同步设置助手",
      "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
    },
    {
      "id": "684e5eea-8148-4ddf-8972-dab890083ce2",
      "name": "Todoist Webhook 设置助手",
      "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
    },
    {
      "id": "05f938b9-f2b3-4535-8fea-5c378bbf7944",
      "name": "返回配置 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
    },
    {
      "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
    },
    {
      "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
    },
    {
      "id": "72c05668-ed66-4a17-abd1-d848890d23d2",
      "name": "比较数据集",
      "type": "n8n-nodes-base.compareDatasets",
      "position": [
        3420,
        500
      ],
      "parameters": {
        "options": {},
        "resolve": "mix",
        "mergeByFields": {
          "values": [
            {
              "field1": "id",
              "field2": "id"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "4fe3e94e-cd61-49e3-a37c-1a4a45ae990d",
      "name": "在 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
    },
    {
      "id": "e6917957-0511-489e-aef1-268d54d1ae1b",
      "name": "获取 Todoist 任务",
      "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
    },
    {
      "id": "7ee99bf2-7316-4106-820b-71fb7dc62a69",
      "name": "如果 Todoist ID 存在",
      "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
    },
    {
      "id": "c54b47a5-8c6e-4656-841b-12e64d0dc3af",
      "name": "存储 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": "Todoist ID|rich_text",
              "textContent": "={{ $json.id }}"
            }
          ]
        }
      },
      "credentials": {
        "notionApi": {
          "id": "5AJPpSLgA5nVypUf",
          "name": "Notion (octionic)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "29ccfeb0-f3c1-4141-a7ba-211ae6195e63",
      "name": "在 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
    },
    {
      "id": "7322844b-0f2b-4f33-9aa6-117845bf90e5",
      "name": "在 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
    },
    {
      "id": "44d117ae-f3a1-47ae-8e6d-c668e721add7",
      "name": "在 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
    },
    {
      "id": "57176531-6186-40d4-9306-65eea976583f",
      "name": "遍历项目",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        3660,
        1320
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "25bc1156-cfa2-4633-a635-f7bb2f4f25f2",
      "name": "获取 Notion 任务",
      "type": "n8n-nodes-base.notion",
      "position": [
        3880,
        1420
      ],
      "parameters": {
        "limit": 1,
        "simple": false,
        "filters": {
          "conditions": [
            {
              "key": "Todoist ID|rich_text",
              "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
    },
    {
      "id": "01ec4ae8-22df-45ef-8593-4f16e3571c2e",
      "name": "在 Todoist 中更新任务",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3880,
        520
      ],
      "parameters": {
        "url": "=https://api.todoist.com/rest/v2/tasks/{{ $json.id }}",
        "method": "POST",
        "options": {},
        "jsonQuery": "={{ $json.toJsonString() }}",
        "sendQuery": true,
        "specifyQuery": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "4961331b-7cab-4c5e-b700-cb35e9258426",
      "name": "选择 Todoist 字段",
      "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
    },
    {
      "id": "283370f2-93ff-4272-82bf-ad45c54e47f7",
      "name": "将 Notion 映射到 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 = \"[↗ 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
    },
    {
      "id": "ca33c90f-3204-45e1-9d66-20e7e740a4d7",
      "name": "在关闭前在 Todoist 中更新任务",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4540,
        1120
      ],
      "parameters": {
        "url": "=https://api.todoist.com/rest/v2/tasks/{{ $json.id }}",
        "method": "POST",
        "options": {},
        "jsonQuery": "={{ $json.toJsonString() }}",
        "sendQuery": true,
        "specifyQuery": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "dd79e458-8c60-4de8-bdde-ec0009b2d4dc",
      "name": "将 Notion 映射到 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 = \"[↗ 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
    },
    {
      "id": "7dec20dc-c1a9-4fd7-85b0-c3425a73ff66",
      "name": "处理空日期",
      "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
    },
    {
      "id": "b1bf65ad-b9b1-49ba-a892-678966c520a3",
      "name": "处理空日期1",
      "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
    },
    {
      "id": "8dbd2213-4a7f-44ef-9276-67672c6bb1ec",
      "name": "处理空日期2",
      "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
    },
    {
      "id": "4a538bb6-80f6-4920-b96a-0756e0dc5449",
      "name": "在 Todoist 中创建任务",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4980,
        -280
      ],
      "parameters": {
        "url": "=https://api.todoist.com/rest/v2/tasks",
        "method": "POST",
        "options": {},
        "jsonQuery": "={{ $('If Todoist ID exists').item.json.toJsonString() }}",
        "sendQuery": true,
        "specifyQuery": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "afdad4ec-e8bc-48c0-b80d-e1f4066871c2",
      "name": "添加项目 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
    },
    {
      "id": "fb2f5730-5a69-4bed-983f-9597da7a0065",
      "name": "锁定 Todoist ID",
      "type": "n8n-nodes-base.redis",
      "position": [
        5200,
        -380
      ],
      "parameters": {
        "key": "=lock_{{ $('Compare Datasets').item.json.id }}",
        "ttl": 15,
        "value": "={{ $now.toISO() }}",
        "expire": true,
        "keyType": "string",
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8f4e1ffd-a46c-4161-b6ed-89c0e78c47b1",
      "name": "生成 UUID",
      "type": "n8n-nodes-base.crypto",
      "position": [
        6080,
        -80
      ],
      "parameters": {
        "action": "generate",
        "dataPropertyName": "uuid"
      },
      "typeVersion": 1
    },
    {
      "id": "28b4e10e-a9cb-40c0-9a2c-d45303f4b64e",
      "name": "更新部分(同步 API)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        6740,
        -80
      ],
      "parameters": {
        "url": "https://api.todoist.com/sync/v9/sync",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "commands",
              "value": "={{ $json.commands.toJsonString() }}"
            }
          ]
        },
        "nodeCredentialType": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "82cabb00-43bf-404a-9b3b-0a95bee0ba26",
      "name": "状态已更改",
      "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
    },
    {
      "id": "d4242f35-f49f-4b3e-a99d-dacc90ea912a",
      "name": "聚合",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        6520,
        -80
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "commands"
      },
      "typeVersion": 1
    },
    {
      "id": "4f6d649b-94c7-4826-950c-1f1632037d56",
      "name": "准备批处理项目",
      "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
    },
    {
      "id": "4d5993d2-13af-47e3-b17a-9c19db7d0489",
      "name": "遍历项目1",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        5860,
        -180
      ],
      "parameters": {
        "options": {},
        "batchSize": 100
      },
      "typeVersion": 3
    },
    {
      "id": "5235aab4-70f2-4d21-b9d3-a11ab817dd13",
      "name": "状态为已完成",
      "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
    },
    {
      "id": "608efb15-5e9b-49c2-96ea-ad35445c82bd",
      "name": "锁定 Todoist ID1",
      "type": "n8n-nodes-base.redis",
      "position": [
        4760,
        1020
      ],
      "parameters": {
        "key": "=lock_{{ $('Compare Datasets').item.json.id }}",
        "ttl": 15,
        "value": "={{ $now.toISO() }}",
        "expire": true,
        "keyType": "string",
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2d571e77-6162-4f5c-a147-99878d5b559a",
      "name": "锁定 Todoist ID2",
      "type": "n8n-nodes-base.redis",
      "position": [
        4100,
        320
      ],
      "parameters": {
        "key": "=lock_{{ $('Compare Datasets').item.json.id }}",
        "ttl": 15,
        "value": "={{ $now.toISO() }}",
        "expire": true,
        "keyType": "string",
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "cee5f777-4763-490e-a89f-8ba715cfd5b8",
      "name": "在 Todoist 中更新任务",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        5200,
        -580
      ],
      "parameters": {
        "url": "=https://api.todoist.com/rest/v2/tasks/{{ $('If Todoist ID exists').item.json.id }}",
        "method": "POST",
        "options": {},
        "jsonQuery": "={{ $('If Todoist ID exists').item.json.toJsonString() }}",
        "sendQuery": true,
        "specifyQuery": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 5000
    },
    {
      "id": "0b2c57af-c3b6-4bed-8e4c-ec2f84576a47",
      "name": "获取映射项",
      "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
    },
    {
      "id": "e6d335b2-e848-4802-9809-15ea9eb3fd29",
      "name": "获取映射项1",
      "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
    },
    {
      "id": "ce49a060-0a2b-491f-b6f5-efa49b54e8d3",
      "name": "计划触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        2760,
        500
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 6
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "4402861c-7b7e-47c4-9e5c-91589262c556",
      "name": "准备摘要数据",
      "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
    },
    {
      "id": "3a26cfc0-3e4e-4e14-80ef-2ad00236ae38",
      "name": "准备摘要数据1",
      "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
    },
    {
      "id": "a3e4e83d-6789-4c55-9699-41da5eeac9e4",
      "name": "合并",
      "type": "n8n-nodes-base.merge",
      "position": [
        5640,
        -180
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "9b93f9ff-0c11-49eb-8425-7d3dc71eaa0b",
      "name": "准备摘要数据2",
      "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
    },
    {
      "id": "f6ea7bc7-093b-47ea-8bb0-48a487566218",
      "name": "过滤掉状态更改",
      "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
    },
    {
      "id": "63990a2a-7380-4799-9037-bd98ed779ddd",
      "name": "将 Todoist 映射到 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
    },
    {
      "id": "a5f3a1ed-1a97-441e-afbc-604b5011928e",
      "name": "映射摘要字段",
      "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
    },
    {
      "id": "50a844e8-5c5c-43e6-b774-993af828cb11",
      "name": "转换为 HTML 表格",
      "type": "n8n-nodes-base.html",
      "position": [
        6760,
        720
      ],
      "parameters": {
        "options": {
          "capitalize": true
        },
        "operation": "convertToHtmlTable"
      },
      "typeVersion": 1.2
    },
    {
      "id": "53e56e25-4931-4a1a-b70f-1eda8520c649",
      "name": "生成邮件正文",
      "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
    },
    {
      "id": "aa0507f8-e5b4-45b1-89bb-6ced3dd9b27e",
      "name": "获取映射项2",
      "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
    },
    {
      "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
    },
    {
      "id": "29ea508e-eba9-40a3-9840-d70d4ec51ddb",
      "name": "合并摘要",
      "type": "n8n-nodes-base.merge",
      "position": [
        5880,
        680
      ],
      "parameters": {
        "numberInputs": 5
      },
      "typeVersion": 3
    },
    {
      "id": "9773c5d4-a910-4b26-858d-2abf30c9602f",
      "name": "便签8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3140,
        260
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 300,
        "content": "## 保持同步"
      },
      "typeVersion": 1
    },
    {
      "id": "e4224f99-4063-49a2-a5cd-c2515b117bc5",
      "name": "便签 9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4040,
        980
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 300,
        "content": "## 保持同步"
      },
      "typeVersion": 1
    },
    {
      "id": "3d5662ad-81a4-4a02-abe3-38d59a08bf9f",
      "name": "便签10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2920,
        20
      ],
      "parameters": {
        "width": 220,
        "height": 300,
        "content": "## 设置全局变量"
      },
      "typeVersion": 1
    },
    {
      "id": "e78779d9-32f9-467f-ae2c-ad0cec69f506",
      "name": "全局变量",
      "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
    },
    {
      "id": "f01855a1-e9bf-469a-ad68-bef8d0286e3a",
      "name": "便签14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5800,
        -260
      ],
      "parameters": {
        "color": 7,
        "width": 1100,
        "height": 380,
        "content": "在完全同步模式下使用同步 API 更新 Todoist 中每个任务的部分。如果需要,使用批处理并分成多个批次,因为在 15 分钟内有 100 次完全同步的请求限制。"
      },
      "typeVersion": 1
    },
    {
      "id": "2ec59ee5-5ffb-4b57-9a2d-0f7d10a580ba",
      "name": "便签15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2640,
        -740
      ],
      "parameters": {
        "color": 5,
        "width": 4760,
        "height": 2420,
        "content": "# 从 Notion 到 Todoist 的单向同步"
      },
      "typeVersion": 1
    },
    {
      "id": "a6f87f39-6885-4fff-99f0-1f29f0db25b7",
      "name": "便签16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2920,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 440,
        "content": "从 Notion 和 Todoist 获取打开/未完成的任务"
      },
      "typeVersion": 1
    },
    {
      "id": "fcffe10e-4ab3-4172-8be3-d060a4070922",
      "name": "便签17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6040,
        660
      ],
      "parameters": {
        "color": 7,
        "width": 1100,
        "height": 220,
        "content": "生成包含此工作流所做所有更改的电子邮件报告,因为 Todoist 中的更改正在被覆盖。"
      },
      "typeVersion": 1
    },
    {
      "id": "af70e38e-eca7-4eba-9125-69a8ff222ac0",
      "name": "执行工作流触发器",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "disabled": true,
      "position": [
        2800,
        3540
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "8e09225d-5d7b-468e-a8f6-c0836469799a",
      "name": "存在差异1",
      "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
    },
    {
      "id": "d30a9839-6ff4-49d3-8413-4c4786c47a0a",
      "name": "需要完成更改1",
      "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
    },
    {
      "id": "38edc95d-ae92-46bb-9cc5-338588d720af",
      "name": "在 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": "Todoist ID|rich_text",
              "textContent": "={{ $('Map Todoist to Notion1').item.json.todoist_id }}"
            },
            {
              "key": "Priority|select",
              "selectValue": "={{ $('Map Todoist to Notion1').item.json.priority }}"
            },
            {
              "key": "Status|status",
              "statusValue": "={{ $('Map Todoist to Notion1').item.json.status }}"
            },
            {
              "key": "Due|date",
              "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": "Focus|checkbox",
              "checkboxValue": true
            }
          ]
        }
      },
      "credentials": {
        "notionApi": {
          "id": "5AJPpSLgA5nVypUf",
          "name": "Notion (octionic)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "fd60280f-aef7-43f6-835c-0086adad3952",
      "name": "在 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": "Name|title",
              "title": "={{ $('Map Todoist to Notion1').item.json.name }}"
            },
            {
              "key": "Priority|select",
              "selectValue": "={{ $('Map Todoist to Notion1').item.json.priority }}"
            },
            {
              "key": "Status|status",
              "statusValue": "={{ $('Map Todoist to Notion1').item.json.status }}"
            },
            {
              "key": "Due|date",
              "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
    },
    {
      "id": "5aecc1ce-f83b-43a2-b2a3-44b00b59f26b",
      "name": "在 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": "Status|status",
              "statusValue": "Done"
            }
          ]
        }
      },
      "credentials": {
        "notionApi": {
          "id": "5AJPpSLgA5nVypUf",
          "name": "Notion (octionic)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "fb2c809e-50ad-4627-ad7e-4919cf30b845",
      "name": "在 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": "Status|status",
              "statusValue": "Obsolete"
            }
          ]
        }
      },
      "credentials": {
        "notionApi": {
          "id": "5AJPpSLgA5nVypUf",
          "name": "Notion (octionic)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "6cb3caa1-45f5-4675-ae06-5c15163ccac1",
      "name": "在 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": "Status|status",
              "statusValue": "In progress"
            }
          ]
        }
      },
      "credentials": {
        "notionApi": {
          "id": "5AJPpSLgA5nVypUf",
          "name": "Notion (octionic)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "7aa800dd-620a-40b3-86a3-9fc07aeb51fb",
      "name": "仅当未锁定时继续",
      "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
    },
    {
      "id": "ddf2093b-3528-4141-9694-0bc164851919",
      "name": "锁定 Notion ID",
      "type": "n8n-nodes-base.redis",
      "position": [
        8300,
        3040
      ],
      "parameters": {
        "key": "=lock_{{ $json.id }}",
        "ttl": 15,
        "value": "={{ $now.toISO() }}",
        "expire": true,
        "keyType": "string",
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "05aac18c-e591-430e-9bc9-93a581cb0e89",
      "name": "检查 Todoist ID 是否被锁定",
      "type": "n8n-nodes-base.redis",
      "position": [
        3640,
        2900
      ],
      "parameters": {
        "key": "=lock_{{ $json.body.event_data.id }}",
        "keyType": "string",
        "options": {},
        "operation": "get",
        "propertyName": "item"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4df2d7a9-ca8e-4b64-a797-1cf9ec4786cc",
      "name": "在 Todoist 中更新描述",
      "type": "n8n-nodes-base.todoist",
      "position": [
        7860,
        2220
      ],
      "parameters": {
        "taskId": "={{ $('Map Todoist to Notion1').item.json.todoist_id }}",
        "operation": "update",
        "updateFields": {
          "description": "={{ \"[↗ Open in Notion](\" + $('Create task in Notion').item.json.url + \")\" }}"
        }
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "a2b07adf-6e01-4bbc-a852-8b59fb20046f",
      "name": "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": "={{ \"[↗ Open in Notion](\" + $json.url + \")\" }}"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "5fe846b7-9869-4d1f-a6f0-6cd14e3127d4",
      "name": "在 Todoist 中更新描述1",
      "type": "n8n-nodes-base.todoist",
      "position": [
        7200,
        2920
      ],
      "parameters": {
        "taskId": "={{ $('Map Todoist to Notion1').item.json.todoist_id }}",
        "operation": "update",
        "updateFields": {
          "description": "={{ \"[↗ Open in Notion](\" + $json.url + \")\" }}"
        }
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "879b47ad-5d32-4a40-ad61-690762e202b0",
      "name": "生成 UUID1",
      "type": "n8n-nodes-base.crypto",
      "position": [
        7420,
        2440
      ],
      "parameters": {
        "action": "generate",
        "dataPropertyName": "uuid"
      },
      "typeVersion": 1
    },
    {
      "id": "106f7e35-0cc2-4054-817c-6c3d50407e88",
      "name": "更新部分(同步 API)1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        7640,
        2440
      ],
      "parameters": {
        "url": "https://api.todoist.com/sync/v9/sync",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "authentication": "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": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "20d2cdc2-92ca-497d-a606-445ecf52e908",
      "name": "获取积压部分 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
    },
    {
      "id": "72819157-59db-4a4e-ad13-1bf73c583be7",
      "name": "未找到 Notion 任务",
      "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
    },
    {
      "id": "3b5d3da8-e8b6-46d0-8706-88a9d1a5e95a",
      "name": "检查创建标志是否存在1",
      "type": "n8n-nodes-base.redis",
      "position": [
        6540,
        2440
      ],
      "parameters": {
        "key": "=creating_for_{{ $('Todoist trigger reference').item.json.body.event_data.id }}",
        "keyType": "string",
        "options": {},
        "operation": "get",
        "propertyName": "item"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1dc24cdd-63e7-4307-a945-6cc07babd535",
      "name": "仅当标志不存在时继续1",
      "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
    },
    {
      "id": "2e64fc80-428b-4f14-a7a1-db158bc58b06",
      "name": "设置创建标志1",
      "type": "n8n-nodes-base.redis",
      "position": [
        6980,
        2440
      ],
      "parameters": {
        "key": "=creating_for_{{ $('Todoist trigger reference').item.json.body.event_data.id }}",
        "ttl": 10,
        "value": "={{ $now.toISO() }}",
        "expire": true,
        "keyType": "string",
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4a2cb838-40d6-4e16-bb41-c04548d3394c",
      "name": "既非重点也非计划1",
      "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
    },
    {
      "id": "56df5752-6beb-4ebe-8b6c-1b5752633db3",
      "name": "在 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": "Focus|checkbox",
              "checkboxValue": true
            }
          ]
        }
      },
      "credentials": {
        "notionApi": {
          "id": "5AJPpSLgA5nVypUf",
          "name": "Notion (octionic)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "e031cb0d-41bf-4273-a198-5485cdaa3758",
      "name": "Todoist 触发器引用",
      "type": "n8n-nodes-base.noOp",
      "position": [
        3200,
        2900
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "539bac50-20c8-4212-aaa5-a99b7f2c3cdb",
      "name": "获取 Notion 任务2",
      "type": "n8n-nodes-base.notion",
      "position": [
        6760,
        3700
      ],
      "parameters": {
        "limit": 1,
        "filters": {
          "conditions": [
            {
              "key": "Todoist ID|rich_text",
              "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
    },
    {
      "id": "1585da72-5578-456b-a092-21e66b033350",
      "name": "找到 Notion 任务1",
      "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
    },
    {
      "id": "c5df6b20-a7ed-4e57-9814-091f76ee677b",
      "name": "按事件切换",
      "type": "n8n-nodes-base.switch",
      "position": [
        6760,
        2920
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "item:updated",
              "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": "item:completed",
              "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": "item:uncompleted",
              "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
    },
    {
      "id": "74859244-46a3-45a2-8e90-923c6bdc0d40",
      "name": "如果事件不是 :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
    },
    {
      "id": "f3404606-51e8-4b53-b332-20f9cf26a969",
      "name": "执行数据",
      "type": "n8n-nodes-base.executionData",
      "position": [
        3420,
        2900
      ],
      "parameters": {
        "dataToSave": {
          "values": [
            {
              "key": "database_id",
              "value": "={{ $('Globals1').first().json.database_id }}"
            },
            {
              "key": "project_id",
              "value": "={{ $('Globals1').first().json.project_id }}"
            },
            {
              "key": "todoist_id",
              "value": "={{ $json.body.event_data.id }}"
            },
            {
              "key": "source",
              "value": "Todoist"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "643b67db-43e2-47fb-8f7f-1c5ad7b780fc",
      "name": "执行数据1",
      "type": "n8n-nodes-base.executionData",
      "position": [
        5840,
        2760
      ],
      "parameters": {
        "dataToSave": {
          "values": [
            {
              "key": "task_name",
              "value": "={{ $json.name }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ad4ff5a0-ab39-48f9-82fd-9f920b8160d5",
      "name": "执行数据2",
      "type": "n8n-nodes-base.executionData",
      "position": [
        7640,
        2220
      ],
      "parameters": {
        "dataToSave": {
          "values": [
            {
              "key": "notion_id",
              "value": "={{ $json.id }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ee82b160-35ce-4943-b8f7-6297382f0656",
      "name": "执行数据3",
      "type": "n8n-nodes-base.executionData",
      "position": [
        6540,
        2920
      ],
      "parameters": {
        "dataToSave": {
          "values": [
            {
              "key": "notion_id",
              "value": "={{ $json.id }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "591f2447-1a81-436c-b073-838cb3011f51",
      "name": "执行数据4",
      "type": "n8n-nodes-base.executionData",
      "position": [
        7200,
        3700
      ],
      "parameters": {
        "dataToSave": {
          "values": [
            {
              "key": "notion_id",
              "value": "={{ $json.id }}"
            },
            {
              "key": "task_name",
              "value": "={{ $json.name }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "558f1a95-56ad-43c0-b924-b8a6ad50cf36",
      "name": "全局变量1",
      "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
    },
    {
      "id": "3f39ff17-8700-475e-9ae7-ef3dbef299af",
      "name": "便签12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3360,
        2500
      ],
      "parameters": {
        "width": 220,
        "height": 300,
        "content": "## 设置全局变量"
      },
      "typeVersion": 1
    },
    {
      "id": "44393b98-739a-4478-a485-67b8bb59e901",
      "name": "将 Todoist 映射到 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
    },
    {
      "id": "9c9bf784-1318-4891-a6d8-3303aa09bd82",
      "name": "获取 Todoist 任务1",
      "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
    },
    {
      "id": "0d64962d-079a-4991-9442-8cf1f18b4b4f",
      "name": "获取 Notion 任务1",
      "type": "n8n-nodes-base.notion",
      "position": [
        6060,
        2760
      ],
      "parameters": {
        "limit": 1,
        "filters": {
          "conditions": [
            {
              "key": "Todoist ID|rich_text",
              "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
    },
    {
      "id": "0380f6c3-a3e8-4dba-8e45-f98a53dfa60f",
      "name": "便签23",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5780,
        2660
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "存储更多之前不可用的信息"
      },
      "typeVersion": 1
    },
    {
      "id": "a36cf7d4-d467-428b-b6cc-2ff7c9ccd1ad",
      "name": "便签 26",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7360,
        2140
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "设置重点标志,以便在 Notion 中更新后它仍保留在 Todoist 中"
      },
      "typeVersion": 1
    },
    {
      "id": "79a16ed2-2cc9-4dc0-9efd-996ec3409b73",
      "name": "便签 27",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6460,
        1880
      ],
      "parameters": {
        "color": 7,
        "width": 1800,
        "height": 760,
        "content": "**创建 Notion 页面**(如果不存在)"
      },
      "typeVersion": 1
    },
    {
      "id": "124820ca-86d1-4b99-8437-2fcdc399cef7",
      "name": "便签 28",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6460,
        2660
      ],
      "parameters": {
        "color": 7,
        "width": 1340,
        "height": 900,
        "content": "**更新 Notion 页面**(如果需要更改)"
      },
      "typeVersion": 1
    },
    {
      "id": "597e44ec-8c2c-4e2e-9d8c-e9fd8495dcd3",
      "name": "便签 29",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3580,
        2800
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 280,
        "content": "检查 Redis 中的缓存标志,以防止覆盖和无限循环。"
      },
      "typeVersion": 1
    },
    {
      "id": "88302c02-82d5-4e5d-9716-49e01d4d1b12",
      "name": "便签31",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6000,
        2660
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "检索 Notion 页面,以便进行比较"
      },
      "typeVersion": 1
    },
    {
      "id": "31f3c572-4f38-4133-85ff-699ab6359d0e",
      "name": "便签 32",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6480,
        2820
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "存储更多之前不可用的信息"
      },
      "typeVersion": 1
    },
    {
      "id": "f2f6d6b4-3c6e-46ef-906b-7293d16a0ce2",
      "name": "便签33",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3360,
        2800
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "在突出显示的执行数据中存储相关信息,以便调试"
      },
      "typeVersion": 1
    },
    {
      "id": "3529a085-c1dc-4467-927f-cebbbc7ec128",
      "name": "便签35",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2640,
        1780
      ],
      "parameters": {
        "color": 5,
        "width": 5880,
        "height": 2160,
        "content": "## 基于触发器的从 Todoist 到 Notion 的同步(双向同步的一部分)"
      },
      "typeVersion": 1
    },
    {
      "id": "7bd78d2f-8452-48d0-8b85-c2ec9023336f",
      "name": "便签36",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3140,
        2800
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "以下节点引用此节点,因此可以轻松替换触发器"
      },
      "typeVersion": 1
    },
    {
      "id": "e87ad29c-5eb8-4abb-86a2-d2dd937ab81d",
      "name": "便签37",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6480,
        2380
      ],
      "parameters": {
        "color": 7,
        "width": 660,
        "height": 240,
        "content": "通过在 Redis 中存储包含 Todoist ID 的特殊标志来防止在 Notion 中创建重复项"
      },
      "typeVersion": 1
    },
    {
      "id": "e496f9f0-28be-4ae2-8a69-47805e858724",
      "name": "需要完成更改",
      "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
    },
    {
      "id": "6f0f1698-638a-49ec-af54-8a11ddfb4a3a",
      "name": "是否已完成?",
      "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
    },
    {
      "id": "dab457b1-f419-4916-bc93-35029e8a8136",
      "name": "存在差异",
      "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
    },
    {
      "id": "57489d22-0081-48f5-883d-d646cde35908",
      "name": "仅当未锁定时继续1",
      "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
    },
    {
      "id": "6bbb506e-407b-4357-935e-692cdccac165",
      "name": "检查 Notion ID 是否被锁定",
      "type": "n8n-nodes-base.redis",
      "position": [
        -1900,
        3040
      ],
      "parameters": {
        "key": "=lock_{{ $json.body.data.id }}",
        "keyType": "string",
        "options": {},
        "operation": "get",
        "propertyName": "item"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "abd91f47-f21c-4805-ae8a-d52a6b576304",
      "name": "获取 Notion 任务",
      "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
    },
    {
      "id": "62a7503b-d605-484b-95c0-864ea698f31b",
      "name": "检查创建标志是否存在",
      "type": "n8n-nodes-base.redis",
      "position": [
        1440,
        3400
      ],
      "parameters": {
        "key": "=creating_for_{{ $('Notion trigger reference').item.json.body.data.id }}",
        "keyType": "string",
        "options": {},
        "operation": "get",
        "propertyName": "item"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e7981df5-7d8e-4f2d-b25f-9b74b177c95e",
      "name": "仅当标志不存在时继续",
      "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
    },
    {
      "id": "22869aac-c36b-4aad-ab3b-00971cfe7c4e",
      "name": "设置创建标志",
      "type": "n8n-nodes-base.redis",
      "position": [
        1880,
        3400
      ],
      "parameters": {
        "key": "=creating_for_{{ $('Notion trigger reference').item.json.body.data.id }}",
        "ttl": 10,
        "value": "={{ $now.toISO() }}",
        "expire": true,
        "keyType": "string",
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "86e4a245-d8e3-4a2a-adab-57f908fadaca",
      "name": "Todoist ID 是否存在?",
      "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
    },
    {
      "id": "0f49f736-eeec-4159-bd12-056ef4270fb5",
      "name": "获取 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
    },
    {
      "id": "0f1eea07-0750-46a8-801b-76254461437f",
      "name": "Todoist ID 存在",
      "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
    },
    {
      "id": "5fe0975a-9b10-4046-8037-54e20ebd8804",
      "name": "获取 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
    },
    {
      "id": "7025479a-b14e-471e-bc7d-211241b37d8f",
      "name": "状态已更改但不为空",
      "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
    },
    {
      "id": "e8b84dd0-9f5e-4670-a2ee-50e75448b611",
      "name": "状态是否为已废弃?",
      "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
    },
    {
      "id": "aba681f0-a079-442f-9538-fb81abe8a9b7",
      "name": "状态不是已完成",
      "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
    },
    {
      "id": "8262fbbd-3fc4-478c-8ffc-3d8d6ece743c",
      "name": "既非重点也非计划",
      "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
    },
    {
      "id": "d0ab88eb-deed-4a2f-85f9-7aa381659c55",
      "name": "Notion 触发器引用",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -2340,
        3040
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "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
    },
    {
      "id": "35178847-629c-4672-858a-9b7de7344ef7",
      "name": "存储 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": "Todoist ID|rich_text",
              "textContent": "={{ $json.id }}"
            }
          ]
        }
      },
      "credentials": {
        "notionApi": {
          "id": "5AJPpSLgA5nVypUf",
          "name": "Notion (octionic)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "1310034f-b035-43d0-9448-122bf0018e63",
      "name": "在 Todoist 中标记为已完成1",
      "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
    },
    {
      "id": "baa2d8ad-3eee-46ec-bd20-cea4615f5cec",
      "name": "在 Todoist 中更新任务1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1660,
        2260
      ],
      "parameters": {
        "url": "=https://api.todoist.com/rest/v2/tasks/{{ $json.id }}",
        "method": "POST",
        "options": {},
        "jsonQuery": "={{ $('Map Notion to Todoist2').item.json.toJsonString() }}",
        "sendQuery": true,
        "specifyQuery": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "ad9d9069-a09b-41d0-a4f5-e83ac28f04cf",
      "name": "全局变量2",
      "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
    },
    {
      "id": "165db83b-47fd-4aa6-9d04-c7ce07e8f8bf",
      "name": "在 Todoist 中标记为未完成1",
      "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
    },
    {
      "id": "a5bca570-f213-4d40-ac5f-7f4bbd532432",
      "name": "生成 UUID2",
      "type": "n8n-nodes-base.crypto",
      "position": [
        1440,
        2900
      ],
      "parameters": {
        "action": "generate",
        "dataPropertyName": "uuid"
      },
      "typeVersion": 1
    },
    {
      "id": "00cea352-9026-433b-b351-28720e0b6a40",
      "name": "更新部分(同步 API)2",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1660,
        2900
      ],
      "parameters": {
        "url": "https://api.todoist.com/sync/v9/sync",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "authentication": "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": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "1cd9bf69-0e3c-4ace-8e79-894fea1c0045",
      "name": "锁定 Todoist ID4",
      "type": "n8n-nodes-base.redis",
      "position": [
        2320,
        2580
      ],
      "parameters": {
        "key": "=lock_{{ $json.id }}",
        "ttl": 15,
        "value": "={{ $now.toISO() }}",
        "expire": true,
        "keyType": "string",
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "id": "t7IN9qSccRezBIzV",
          "name": "Redis account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "dc21db22-41a0-4a3b-aeba-dc3c81fdf251",
      "name": "在 Todoist 中删除任务2",
      "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
    },
    {
      "id": "e90c2ef5-61f9-4e16-879f-14c9d7974f88",
      "name": "将 Notion 映射到 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 = \"[↗ 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
    },
    {
      "id": "e5e12b26-6c45-4c63-9c7d-05ae88a457aa",
      "name": "获取 Todoist 任务2",
      "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
    },
    {
      "id": "05441894-9904-44b3-94f7-bb600bcbe7ba",
      "name": "在 Todoist 中创建任务1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2100,
        3400
      ],
      "parameters": {
        "url": "=https://api.todoist.com/rest/v2/tasks",
        "method": "POST",
        "options": {},
        "jsonQuery": "={{ $('Map Notion to Todoist2').item.json.toJsonString() }}",
        "sendQuery": true,
        "specifyQuery": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "todoistApi"
      },
      "credentials": {
        "todoistApi": {
          "id": "Dp3VoWGH5IhrK22k",
          "name": "Todoist (mario.haarmann)"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 3000
    },
    {
      "id": "354b6df0-b51e-476c-bb54-baaa1a6ca788",
      "name": "执行数据5",
      "type": "n8n-nodes-base.executionData",
      "position": [
        -2120,
        3040
      ],
      "parameters": {
        "dataToSave": {
          "values": [
            {
              "key": "database_id",
              "value": "={{ $('Globals2').first().json.database_id }}"
            },
            {
              "key": "project_id",
              "value": "={{ $('Globals2').first().json.project_id }}"
            },
            {
              "key": "notion_id",
              "value": "={{ $json.body.data.id }}"
            },
            {
              "key": "source",
              "value": "Notion"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "04fe1427-30ba-45ae-904a-05c7b4c2269f",
      "name": "执行数据6",
      "type": "n8n-nodes-base.executionData",
      "position": [
        -1020,
        3040
      ],
      "parameters": {
        "dataToSave": {
          "values": [
            {
              "key": "todoist_id",
              "value": "={{ $json.id }}"
            },
            {
              "key": "task_name",
              "value": "={{ $json.content }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2b8262d7-298b-4a70-a420-50d8d066cb40",
      "name": "便签22",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1380,
        2840
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 220,
        "content": "在完全同步模式下使用同步 API 在 Todoist 中更新部分(**15 分钟内最多 100 个请求!**)"
      },
      "typeVersion": 1
    },
    {
      "id": "10571edb-ed79-43d3-bcb9-a64273c3eeb7",
      "name": "便签24",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1140,
        1880
      ],
      "parameters": {
        "color": 7,
        "width": 900,
        "height": 300,
        "content": "**删除任务**(如果不再符合筛选条件)"
      },
      "typeVersion": 1
    },
    {
      "id": "85124819-6f06-44e8-a5dd-0c31c1aec5e3",
      "name": "便签 25",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1140,
        2200
      ],
      "parameters": {
        "color": 7,
        "width": 900,
        "height": 880,
        "content": "**更新任务**(如果需要更改)"
      },
      "typeVersion": 1
    },
    {
      "id": "6a9d1e64-39d1-4361-aa60-2368ba36a2cd",
      "name": "便签38",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1520,
        2940
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "Notion 会尝试再次发送 webhook。为确保数据是最新的,必须再次检索有效负载。"
      },
      "typeVersion": 1
    },
    {
      "id": "4787a4de-f5d2-4983-a658-916c497ce683",
      "name": "便签39",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2180,
        2640
      ],
      "parameters": {
        "width": 220,
        "height": 300,
        "content": "## 设置全局变量"
      },
      "typeVersion": 1
    },
    {
      "id": "5afdea5e-e6ed-463a-99bb-35bda66c25e6",
      "name": "便签41",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1600,
        1920
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "出错时继续,因此无需事先检索任务"
      },
      "typeVersion": 1
    },
    {
      "id": "6353737c-ea2b-4907-94d1-d5b4c743ca29",
      "name": "便签42",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2400,
        2940
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "以下节点引用此节点,因此可以轻松替换触发器"
      },
      "typeVersion": 1
    },
    {
      "id": "caa80f7d-1c1a-4bbf-a92d-4df2a65e6e2f",
      "name": "便签43",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2180,
        2940
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "在突出显示的执行数据中存储相关信息,以便调试"
      },
      "typeVersion": 1
    },
    {
      "id": "58332f57-5bee-4559-922b-f2ec025af90d",
      "name": "便签44",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1080,
        2940
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "存储更多之前不可用的信息"
      },
      "typeVersion": 1
    },
    {
      "id": "785526ba-39e6-42aa-83f0-731a81e821ec",
      "name": "便签45",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -860,
        2940
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "与其在 Notion 中删除页面,更安全的做法是将它们标记为已废弃。"
      },
      "typeVersion": 1
    },
    {
      "id": "933c2fd0-c378-4984-b34a-6a1f89d61299",
      "name": "便签46",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1380,
        3320
      ],
      "parameters": {
        "color": 7,
        "width": 660,
        "height": 260,
        "content": "在极少数情况下,Notion 在创建新任务时会在短时间内触发多个 webhook。这可以防止在 Todoist 中创建重复项。"
      },
      "typeVersion": 1
    },
    {
      "id": "14b97b13-0689-481b-8a21-61940e742b2a",
      "name": "便签47",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1300,
        2940
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "映射更高级,并且还包括边缘情况的回退值"
      },
      "typeVersion": 1
    },
    {
      "id": "ce6ed70d-4fa0-4291-9ac8-13cd23e899cc",
      "name": "便签48",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1960,
        2940
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 280,
        "content": "检查 Redis 中的缓存标志,以防止覆盖和无限循环。"
      },
      "typeVersion": 1
    },
    {
      "id": "21378455-34e5-42bf-857d-543a7a972c33",
      "name": "便利贴5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3340,
        1780
      ],
      "parameters": {
        "color": 5,
        "width": 5880,
        "height": 1960,
        "content": "# 基于触发器的从 Notion 到 Todoist 的同步(双向同步的一部分)"
      },
      "typeVersion": 1
    },
    {
      "id": "0688c57e-9de6-43b5-8b75-b214760a8445",
      "name": "便签20",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3280,
        2820
      ],
      "parameters": {
        "width": 220,
        "height": 400,
        "content": "## 配置 Webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "d3a90492-d62f-4b01-bf2b-1b82b74c04cc",
      "name": "便签 7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        3040
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "出于效率原因,仅同步重点和计划的任务"
      },
      "typeVersion": 1
    },
    {
      "id": "d0b03cb0-8c69-4a6a-854a-afdfc690219f",
      "name": "便签19",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -420,
        3120
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "Notion 和 Todoist 任务之间的映射存储在 Notion 中"
      },
      "typeVersion": 1
    },
    {
      "id": "c34e228b-eee5-43f4-9876-8ffcd3f64488",
      "name": "便签11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2260,
        2460
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "Todoist ID 会在 Redis 中缓存 15 秒,因此不会触发另一个差异同步。"
      },
      "typeVersion": 1
    },
    {
      "id": "c78c9264-683f-4ee5-9cd2-086627ce4fd1",
      "name": "便签13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1140,
        3100
      ],
      "parameters": {
        "color": 7,
        "width": 1340,
        "height": 580,
        "content": "**创建任务**(如果它不存在(不再存在))"
      },
      "typeVersion": 1
    },
    {
      "id": "7940c709-6107-4efa-83dc-639b88286a84",
      "name": "便签 6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1160,
        3160
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "边缘情况:任务已从已废弃移动到已完成"
      },
      "typeVersion": 1
    },
    {
      "id": "2d6b7423-0818-4a2d-b672-0d09f7eaab59",
      "name": "便签49",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2700,
        3180
      ],
      "parameters": {
        "color": 7,
        "width": 1020,
        "height": 700,
        "content": "## Todoist Webhook 路由器示例(可选)"
      },
      "typeVersion": 1
    },
    {
      "id": "b1b38cc8-3dda-43de-9fd3-8341b5fcd798",
      "name": "便签51",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4020,
        2800
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "使用 Todoist 的事件类型是实际识别任务是否被删除的唯一选项"
      },
      "typeVersion": 1
    },
    {
      "id": "5eb3caf9-ae18-47ce-80c1-52b1029e627d",
      "name": "便签52",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5560,
        2660
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "映射更高级,并且还包括边缘情况的回退值"
      },
      "typeVersion": 1
    },
    {
      "id": "d8935c51-34a4-4d69-890e-23a7194ba5ea",
      "name": "便签53",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6220,
        2660
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "前一个节点总是返回输出数据,因为它使用过滤器来检索匹配项"
      },
      "typeVersion": 1
    },
    {
      "id": "d7f254d6-026a-48bb-b3db-1104181db443",
      "name": "便签54",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7140,
        2380
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "反向映射部分/状态"
      },
      "typeVersion": 1
    },
    {
      "id": "68536590-3bd2-443a-804a-e433910b0ccc",
      "name": "便签55",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7360,
        2380
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 240,
        "content": "在完全同步模式下使用同步 API 在 Todoist 中更新部分(**15 分钟内最多 100 个请求!**)"
      },
      "typeVersion": 1
    },
    {
      "id": "18013962-c950-4976-8639-b00b3c2c43ed",
      "name": "便签56",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7580,
        2140
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "存储更多之前不可用的信息"
      },
      "typeVersion": 1
    },
    {
      "id": "02b2cddc-565e-46b1-aaec-b3fc3db0611c",
      "name": "便签57",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        8240,
        2920
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 280,
        "content": "Notion ID 会在 Redis 中缓存 15 秒,因此不会触发另一个差异同步。"
      },
      "typeVersion": 1
    },
    {
      "id": "9d1db117-f41a-4529-a2e8-503d65cff0ad",
      "name": "便签58",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6460,
        3580
      ],
      "parameters": {
        "color": 7,
        "width": 1340,
        "height": 320,
        "content": "**删除 Notion 页面**(如果 Todoist 任务已被删除)"
      },
      "typeVersion": 1
    },
    {
      "id": "9de565e0-3edb-4ea0-bace-8c17b10d5aac",
      "name": "便签59",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7140,
        3620
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 260,
        "content": "存储更多之前不可用的信息"
      },
      "typeVersion": 1
    },
    {
      "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
    },
    {
      "id": "d48d3e29-d1b1-4751-adad-692cddaf49e7",
      "name": "便签60",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3600,
        -640
      ],
      "parameters": {
        "color": 7,
        "width": 1980,
        "height": 860,
        "content": "处理**仅在 Notion 中找到的任务**。这可能仍然意味着任务存在于 Todoist 中,但由于已完成而不符合筛选条件。在这种情况下,将检索 Todoist 任务。如果存在,Todoist 任务将被重新打开,否则将被创建。"
      },
      "typeVersion": 1
    },
    {
      "id": "bfbf04c7-8e27-4afd-998c-1720eb7f05bd",
      "name": "便签61",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3600,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 1760,
        "height": 640,
        "content": "处理显示**两个系统之间存在差异**的任务。如果存在差异,Todoist 任务将被 Notion 的所有属性覆盖。此外,状态将在后续步骤中相应更新。"
      },
      "typeVersion": 1
    },
    {
      "id": "8edde5d8-649a-42a3-bf81-83c36ac1ff03",
      "name": "便签62",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3600,
        900
      ],
      "parameters": {
        "color": 7,
        "width": 1760,
        "height": 720,
        "content": "处理**仅在 Todoist 中找到的任务**。这可能仍然意味着任务存在于 Notion 中,但由于已完成而不符合筛选条件。在这种情况下,将检索 Notion 页面。如果由于被删除而不存在,Todoist 任务也将被删除。"
      },
      "typeVersion": 1
    },
    {
      "id": "284199c4-40ba-4590-9eb8-1af4dedfedf4",
      "name": "便签63",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2700,
        360
      ],
      "parameters": {
        "width": 220,
        "height": 280,
        "content": "## 设置计划"
      },
      "typeVersion": 1
    },
    {
      "id": "2cad2f03-b24d-4146-883d-78bdd11ac127",
      "name": "便签64",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3140,
        560
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 200,
        "content": "准备比较数据"
      },
      "typeVersion": 1
    },
    {
      "id": "453dd0b4-1163-46dd-849b-39179f8ddb7c",
      "name": "Sticky Note65",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7140,
        560
      ],
      "parameters": {
        "width": 220,
        "height": 320,
        "content": "## 设置邮件地址"
      },
      "typeVersion": 1
    },
    {
      "id": "f77d8d57-0a5c-4b33-be07-9806c55330c3",
      "name": "执行工作流",
      "type": "n8n-nodes-base.executeWorkflow",
      "disabled": true,
      "position": [
        3520,
        3440
      ],
      "parameters": {
        "options": {},
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "4bd15a48-39d1-41c3-8b15-7de6abf3d35d",
      "name": "提取 ID",
      "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
    },
    {
      "id": "6e61887e-8022-44ee-b9ab-94058b08a07b",
      "name": "分离 Notion 更改",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -2780,
        2940
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "body"
      },
      "typeVersion": 1
    },
    {
      "id": "5ed59854-c972-42bd-9b41-8e6c1d616c8f",
      "name": "正文是数组吗?",
      "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
    },
    {
      "id": "a6003e6f-84a7-4c5e-99d7-47b31dee10d1",
      "name": "便签66",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3060,
        2880
      ],
      "parameters": {
        "color": 7,
        "width": 660,
        "height": 340,
        "content": "如果使用 Notion Webhook Emulator 的数据而不是常规的 Notion Webhook,数据结构不同,并且可以包含多个项目。此部分使工作流与两种方法兼容。"
      },
      "typeVersion": 1
    },
    {
      "id": "07740ae3-3472-4125-bae9-d1c54e9584f1",
      "name": "按项目切换",
      "type": "n8n-nodes-base.switch",
      "position": [
        2980,
        2900
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "[project name]",
              "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
    },
    {
      "id": "a76190bf-f467-4bbe-82f3-581c2ab27e2a",
      "name": "便签21",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2700,
        2680
      ],
      "parameters": {
        "width": 220,
        "height": 400,
        "content": "## 在 Todoist 应用中注册 Webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "689992a3-c8bc-4d2e-856b-2c512afdb2da",
      "name": "便签67",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2920,
        2680
      ],
      "parameters": {
        "width": 220,
        "height": 400,
        "content": "## 设置项目 ID 和名称"
      },
      "typeVersion": 1
    },
    {
      "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
    },
    {
      "id": "f875a8c7-2223-4296-bd55-8f42789d11b7",
      "name": "分支",
      "type": "n8n-nodes-base.switch",
      "disabled": true,
      "position": [
        3300,
        3540
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "project1",
              "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": "project2",
              "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
    },
    {
      "id": "e603a21c-238f-45a9-8d7d-d8ccabe25217",
      "name": "执行工作流1",
      "type": "n8n-nodes-base.executeWorkflow",
      "disabled": true,
      "position": [
        3520,
        3640
      ],
      "parameters": {
        "options": {},
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "91fe0191-0c57-404b-95e8-c3257426ccde",
      "name": "便签50",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2740,
        3440
      ],
      "parameters": {
        "width": 220,
        "height": 280,
        "content": "### 替换触发器"
      },
      "typeVersion": 1
    },
    {
      "id": "98bbef00-86b5-46b2-8242-45f8eec47de4",
      "name": "便签68",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3020,
        3420
      ],
      "parameters": {
        "width": 220,
        "height": 300,
        "content": "### 注册 Webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "650d3da2-4dee-44ab-afcd-680fb0a0d74a",
      "name": "便签69",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3240,
        3380
      ],
      "parameters": {
        "width": 220,
        "height": 340,
        "content": "### 设置项目 ID 和名称"
      },
      "typeVersion": 1
    },
    {
      "id": "b9c01e3c-bb9c-4bfd-aa22-b1b4a7824e42",
      "name": "便签 70",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3460,
        3320
      ],
      "parameters": {
        "width": 220,
        "height": 500,
        "content": "### 选择子工作流"
      },
      "typeVersion": 1
    },
    {
      "id": "007599a2-8c60-423c-80a8-7599ba709db8",
      "name": "便签72",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1920,
        -580
      ],
      "parameters": {
        "width": 980,
        "height": 560,
        "content": "# 0. 从这里开始"
      },
      "typeVersion": 1
    },
    {
      "id": "a30814bb-205c-488b-aa0b-bf315f3e32b2",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1920,
        0
      ],
      "parameters": {
        "width": 2780,
        "height": 460,
        "content": "# 1. 为全局变量节点生成配置 JSON"
      },
      "typeVersion": 1
    },
    {
      "id": "1f273075-0648-44b1-a8b7-7e226671293a",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1920,
        480
      ],
      "parameters": {
        "width": 800,
        "height": 680,
        "content": "# 2. 设置 Todoist Webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "4915c92e-aca2-409a-909f-7b2efa440bed",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        60,
        480
      ],
      "parameters": {
        "width": 800,
        "height": 680,
        "content": "# 4. 设置 Notion Webhook"
      },
      "typeVersion": 1
    },
    {
      "id": "de16a551-7864-4758-9da0-8f25856ca33d",
      "name": "便签1",
      "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
    },
    {
      "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
    },
    {
      "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
    },
    {
      "id": "dae00353-4b24-4ab1-a2c3-86ffd6860ae3",
      "name": "End here",
      "type": "n8n-nodes-base.noOp",
      "position": [
        5180,
        2860
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "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
    },
    {
      "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
    },
    {
      "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
    },
    {
      "id": "c67ba192-2207-47a3-a3c5-91d059bc4e91",
      "name": "等待",
      "type": "n8n-nodes-base.wait",
      "position": [
        4980,
        20
      ],
      "webhookId": "9d3b561f-4afd-478c-8f6e-60641d4f1d0b",
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "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
    },
    {
      "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
    },
    {
      "id": "a7f467fb-4ef2-4251-aa0d-47b069fe1cd1",
      "name": "便签18",
      "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
    },
    {
      "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
    },
    {
      "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
    },
    {
      "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
    },
    {
      "id": "a9696b6e-f134-46ba-9b25-862ae73df812",
      "name": "等待1",
      "type": "n8n-nodes-base.wait",
      "position": [
        740,
        3340
      ],
      "webhookId": "9d3b561f-4afd-478c-8f6e-60641d4f1d0b",
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "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
    },
    {
      "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
    },
    {
      "id": "92ff51e9-b9c7-47b4-a0ed-a5a0fddd7ef7",
      "name": "便签4",
      "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
    },
    {
      "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
    },
    {
      "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
    },
    {
      "id": "614e1fa0-aac4-425f-87e0-85ba084319cb",
      "name": "等待2",
      "type": "n8n-nodes-base.wait",
      "position": [
        5180,
        3060
      ],
      "webhookId": "9d3b561f-4afd-478c-8f6e-60641d4f1d0b",
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "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
    },
    {
      "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
    },
    {
      "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
    },
    {
      "id": "a760a6a7-f93c-4674-a21e-d10867c523de",
      "name": "便签 30",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        7800,
        2140
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "Overwrite description with Notion URL"
      },
      "typeVersion": 1
    },
    {
      "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
    },
    {
      "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
    },
    {
      "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
    },
    {
      "id": "8ba90a10-f79d-4bac-af19-f61e7bbad979",
      "name": "便签34",
      "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
    },
    {
      "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
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a3febb48-a786-4168-8e4f-61821e15858e",
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Update tries",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Loop Over Items1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait1": {
      "main": [
        [
          {
            "node": "Update tries1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait2": {
      "main": [
        [
          {
            "node": "Update tries2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notion": {
      "main": [
        [
          {
            "node": "Map Notion to Todoist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "Execute Workflow",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Execute Workflow1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Todoist": {
      "main": [
        [
          {
            "node": "Pick Todoist Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Update section (Sync API)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set tries": {
      "main": [
        [
          {
            "node": "Get Todoist Task",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set tries1": {
      "main": [
        [
          {
            "node": "Get Todoist Task2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set tries2": {
      "main": [
        [
          {
            "node": "Get Todoist Task1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract IDs": {
      "main": [
        [
          {
            "node": "Notion trigger reference",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get projects": {
      "main": [
        [
          {
            "node": "Prep Dropdown1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get sections": {
      "main": [
        [
          {
            "node": "Generate config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update tries": {
      "main": [
        [
          {
            "node": "If tries left",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate UUID": {
      "main": [
        [
          {
            "node": "Prepare Batching Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get variables": {
      "main": [
        [
          {
            "node": "Verify security token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If tries left": {
      "main": [
        [
          {
            "node": "Set tries",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Retry limit reached",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge summary": {
      "main": [
        [
          {
            "node": "Filter out status changes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep Dropdown": {
      "main": [
        [
          {
            "node": "Choose Notion Database",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update tries1": {
      "main": [
        [
          {
            "node": "If tries left1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update tries2": {
      "main": [
        [
          {
            "node": "If tries left2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add project ID": {
      "main": [
        [
          {
            "node": "If Todoist ID exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Body is array?": {
      "main": [
        [
          {
            "node": "Split out Notion changes",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notion trigger reference",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Due date empty": {
      "main": [
        [
          {
            "node": "Mark as Focussed in Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execution Data": {
      "main": [
        [
          {
            "node": "Check if Todoist ID is locked",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate UUID1": {
      "main": [
        [
          {
            "node": "Update section (Sync API)1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate UUID2": {
      "main": [
        [
          {
            "node": "Update section (Sync API)2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get todoist ID": {
      "main": [
        [
          {
            "node": "Lock Todoist ID4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If tries left1": {
      "main": [
        [
          {
            "node": "Set tries1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Retry limit reached1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If tries left2": {
      "main": [
        [
          {
            "node": "Set tries2",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Retry limit reached2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notion Webhook": {
      "main": [
        [
          {
            "node": "Body is array?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OAuth redirect": {
      "main": [
        [
          {
            "node": "Get variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prep Dropdown1": {
      "main": [
        [
          {
            "node": "Choose Todoist Project",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Status changed": {
      "main": [
        [
          {
            "node": "Get mapped item1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Status is Done": {
      "main": [
        [
          {
            "node": "Mark as Completed in Todoist",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Delete Task in Todoist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Exchange Tokens": {
      "main": [
        [
          {
            "node": "Respond with success",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond with error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execution Data1": {
      "main": [
        [
          {
            "node": "Get Notion Task1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execution Data2": {
      "main": [
        [
          {
            "node": "Update Description in Todoist",
            "type": "main",
            "index": 0
          },
          {
            "node": "Lock Notion ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execution Data3": {
      "main": [
        [
          {
            "node": "Switch by Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execution Data4": {
      "main": [
        [
          {
            "node": "Mark as Obsolete in Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execution Data5": {
      "main": [
        [
          {
            "node": "Check if Notion ID is locked",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execution Data6": {
      "main": [
        [
          {
            "node": "Status is Obsolete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate config": {
      "main": [
        [
          {
            "node": "Return config JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Notion Task": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Notion task": {
      "main": [
        [
          {
            "node": "Map Notion to Todoist2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get mapped item": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get todoist ID1": {
      "main": [
        [
          {
            "node": "Lock Todoist ID4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Exists/Completed in Notion",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Notion Task",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store variables": {
      "main": [
        [
          {
            "node": "Redirect to Auth Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch by Event": {
      "main": [
        [
          {
            "node": "Differences exist1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Description has changed in Todoist",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Requires completion change1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Requires completion change1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Todoist Webhook": {
      "main": [
        [
          {
            "node": "Switch by project",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Datasets": {
      "main": [
        [
          {
            "node": "Handle empty dates2",
            "type": "main",
            "index": 0
          }
        ],
        [],
        [
          {
            "node": "Handle empty dates",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Notion Task1": {
      "main": [
        [
          {
            "node": "Notion Task not found",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Notion Task2": {
      "main": [
        [
          {
            "node": "Notion Task found1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Todoist Task": {
      "main": [
        [
          {
            "node": "Mark as Incomplete in Todoist",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Catch known error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get mapped item1": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get mapped item2": {
      "main": [
        [
          {
            "node": "Merge summary",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Loop Over Items1": {
      "main": [
        [],
        [
          {
            "node": "Generate UUID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Notion",
            "type": "main",
            "index": 0
          },
          {
            "node": "Todoist",
            "type": "main",
            "index": 0
          },
          {
            "node": "Globals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Todoist ID": {
      "main": [
        [
          {
            "node": "Get mapped item2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Catch known error": {
      "main": [
        [
          {
            "node": "Create task in Todoist",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Differences exist": {
      "main": [
        [
          {
            "node": "Update task in Todoist1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Todoist Task1": {
      "main": [
        [
          {
            "node": "Map Todoist to Notion1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Catch known error2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Todoist Task2": {
      "main": [
        [
          {
            "node": "Differences exist",
            "type": "main",
            "index": 0
          },
          {
            "node": "Requires completion change",
            "type": "main",
            "index": 0
          },
          {
            "node": "Status changed but not empty",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Catch known error1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set creating flag": {
      "main": [
        [
          {
            "node": "Create task in Todoist1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch by project": {
      "main": [
        [
          {
            "node": "Todoist trigger reference",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Todoist ID exists": {
      "main": [
        [
          {
            "node": "Delete Task in Todoist2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Catch known error1": {
      "main": [
        [
          {
            "node": "Status is not Done",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Catch known error2": {
      "main": [
        [
          {
            "node": "End here",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Differences exist1": {
      "main": [
        [
          {
            "node": "Update task in Notion",
            "type": "main",
            "index": 0
          },
          {
            "node": "Due date empty",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle empty dates": {
      "main": [
        [
          {
            "node": "Update Task in Todoist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map summary fields": {
      "main": [
        [
          {
            "node": "Convert to HTML Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notion Task found1": {
      "main": [
        [
          {
            "node": "Execution Data4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set creating flag1": {
      "main": [
        [
          {
            "node": "Get Backlog Section ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Status is not Done": {
      "main": [
        [
          {
            "node": "Check if creating flag exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Todoist ID exists?": {
      "main": [
        [
          {
            "node": "Set tries1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check if creating flag exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Notion Block": {
      "main": [
        [
          {
            "node": "Lock Notion ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate email body": {
      "main": [
        [
          {
            "node": "Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle empty dates1": {
      "main": [
        [
          {
            "node": "Update task in Todoist before closing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle empty dates2": {
      "main": [
        [
          {
            "node": "Add project ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has been completed?": {
      "main": [
        [
          {
            "node": "Mark as Completed in Todoist1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Mark as Incomplete in Todoist1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pick Todoist Fields": {
      "main": [
        [
          {
            "node": "Compare Datasets",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Status is Obsolete?": {
      "main": [
        [
          {
            "node": "Todoist ID exists",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Neither focussed nor planned",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Notion Databases": {
      "main": [
        [
          {
            "node": "Prep Dropdown",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Todoist ID exists": {
      "main": [
        [
          {
            "node": "Set tries",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create task in Todoist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare summary data": {
      "main": [
        [
          {
            "node": "Merge summary",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Convert to HTML Table": {
      "main": [
        [
          {
            "node": "Generate email body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create task in Notion": {
      "main": [
        [
          {
            "node": "Execution Data2",
            "type": "main",
            "index": 0
          },
          {
            "node": "Turn Markdown into Notion Blocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map Notion to Todoist": {
      "main": [
        [
          {
            "node": "Compare Datasets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map Todoist to Notion": {
      "main": [
        [
          {
            "node": "Map summary fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notion Task not found": {
      "main": [
        [
          {
            "node": "Check if creating flag exists1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Execution Data3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare summary data1": {
      "main": [
        [
          {
            "node": "Merge summary",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Prepare summary data2": {
      "main": [
        [
          {
            "node": "Merge summary",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "Update task in Notion": {
      "main": [
        [
          {
            "node": "Lock Notion ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify security token": {
      "main": [
        [
          {
            "node": "Exchange Tokens",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond with error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Choose Notion Database": {
      "main": [
        [
          {
            "node": "Get Notion Database ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Choose Todoist Project": {
      "main": [
        [
          {
            "node": "Get Todoist Project ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create task in Todoist": {
      "main": [
        [
          {
            "node": "Store Todoist ID",
            "type": "main",
            "index": 0
          },
          {
            "node": "Lock Todoist ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Delete Task in Todoist": {
      "main": [
        [
          {
            "node": "Prepare summary data2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Backlog Section ID": {
      "main": [
        [
          {
            "node": "Generate UUID1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Create task in Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Notion Database ID": {
      "main": [
        [
          {
            "node": "Get projects",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Todoist Project ID": {
      "main": [
        [
          {
            "node": "Get sections",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map Notion to Todoist1": {
      "main": [
        [
          {
            "node": "Handle empty dates1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map Notion to Todoist2": {
      "main": [
        [
          {
            "node": "Execution Data6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map Todoist to Notion1": {
      "main": [
        [
          {
            "node": "Execution Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Done in Notion": {
      "main": [
        [
          {
            "node": "Lock Notion ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Batching Items": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Task in Todoist": {
      "main": [
        [
          {
            "node": "Status changed",
            "type": "main",
            "index": 0
          },
          {
            "node": "Lock Todoist ID2",
            "type": "main",
            "index": 0
          },
          {
            "node": "Prepare summary data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update task in Todoist": {
      "main": [
        [
          {
            "node": "Get mapped item",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create task in Todoist1": {
      "main": [
        [
          {
            "node": "Store Todoist ID1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Lock Todoist ID4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Delete Task in Todoist2": {
      "main": [
        [
          {
            "node": "Get todoist ID1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate security token": {
      "main": [
        [
          {
            "node": "Store variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has not been completed?": {
      "main": [
        [
          {
            "node": "Mark as In Progress in Notion",
            "type": "main",
            "index": 0
          },
          {
            "node": "Neither focussed nor planned1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Mark as Done in Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update task in Todoist1": {
      "main": [
        [
          {
            "node": "Get todoist ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute Workflow Trigger": {
      "main": [
        []
      ]
    },
    "If event is not :deleted": {
      "main": [
        [
          {
            "node": "Set tries2",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Notion Task2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notion trigger reference": {
      "main": [
        [
          {
            "node": "Globals2",
            "type": "main",
            "index": 0
          },
          {
            "node": "Execution Data5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split out Notion changes": {
      "main": [
        [
          {
            "node": "Extract IDs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter out status changes": {
      "main": [
        [
          {
            "node": "Map Todoist to Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Todoist trigger reference": {
      "main": [
        [
          {
            "node": "Execution Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Globals1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update section (Sync API)": {
      "main": [
        [
          {
            "node": "Loop Over Items1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Exists/Completed in Notion": {
      "main": [
        [
          {
            "node": "Map Notion to Todoist1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Delete Task in Todoist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Focussed in Notion": {
      "main": [
        [
          {
            "node": "Lock Notion ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Obsolete in Notion": {
      "main": [
        [
          {
            "node": "Lock Notion ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Requires completion change": {
      "main": [
        [
          {
            "node": "Has been completed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update section (Sync API)2": {
      "main": [
        [
          {
            "node": "Get todoist ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only continue if not locked": {
      "main": [
        [
          {
            "node": "If event is not :deleted",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Requires completion change1": {
      "main": [
        [
          {
            "node": "Has not been completed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Notion ID is locked": {
      "main": [
        [
          {
            "node": "Only continue if not locked1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle each block separately": {
      "main": [
        [
          {
            "node": "Append Notion Block",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Completed in Todoist": {
      "main": [
        [
          {
            "node": "Prepare summary data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Neither focussed nor planned": {
      "main": [
        [
          {
            "node": "Todoist ID exists",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Todoist ID exists?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only continue if not locked1": {
      "main": [
        [
          {
            "node": "Get Notion task",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Status changed but not empty": {
      "main": [
        [
          {
            "node": "Generate UUID2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Todoist Webhook Setup Helper": {
      "main": [
        [
          {
            "node": "Generate security token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Todoist ID is locked": {
      "main": [
        [
          {
            "node": "Only continue if not locked",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if creating flag exists": {
      "main": [
        [
          {
            "node": "Only continue if flag does not exist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Completed in Todoist1": {
      "main": [
        [
          {
            "node": "Get todoist ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as In Progress in Notion": {
      "main": [
        [
          {
            "node": "Lock Notion ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Incomplete in Todoist": {
      "main": [
        [
          {
            "node": "Update task in Todoist",
            "type": "main",
            "index": 0
          },
          {
            "node": "Lock Todoist ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Neither focussed nor planned1": {
      "main": [
        [
          {
            "node": "Mark as Focussed in Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if creating flag exists1": {
      "main": [
        [
          {
            "node": "Only continue if flag does not exist1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Incomplete in Todoist1": {
      "main": [
        [
          {
            "node": "Get todoist ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notion-Todoist Sync Setup Helper": {
      "main": [
        [
          {
            "node": "Get Notion Databases",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Turn Markdown into Notion Blocks": {
      "main": [
        [
          {
            "node": "Handle each block separately",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Description has changed in Todoist": {
      "main": [
        [
          {
            "node": "Update Description in Todoist1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only continue if flag does not exist": {
      "main": [
        [
          {
            "node": "Set creating flag",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only continue if flag does not exist1": {
      "main": [
        [
          {
            "node": "Set creating flag1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update task in Todoist before closing": {
      "main": [
        [
          {
            "node": "Status is Done",
            "type": "main",
            "index": 0
          },
          {
            "node": "Lock Todoist ID1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 销售, 工程, 设计, 人力资源, 产品, 营销

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量246
分类6
节点类型29
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者

Workflow Optimization Expert | Software Architect. Use my link to book an initial consultation for custom built workflows using n8n.

外部链接
在 n8n.io 查看

分享此工作流