Todoist Webhook Not Triggering

Describe the problem/error/question

Hi everyone! I’m new to this community, so thank you in advanced for sharing your input. :slight_smile:

I’m trying to create an n8n workflow that automates my tracking/recording of:

  • todoist task created
  • todoist task completed

then logs these as new rows in a Google Sheet every time an event is triggered. (similar to the IFTTT application of Sheets + Todoist).

I’ve set it up using the /sync webhook on Todoist, but it seems that my webhook is never triggering even if I create or complete a new event on todoist. Am I missing something in my setup? Would appreciate any advice. Thank you!

What is the error message (if any)?

No error message, the webhook doesn’t trigger so the flow isn’t able to start even if new events should hypothetically trigger the flow.

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

{
  "nodes": [
    {
      "parameters": {
        "jsCode": "const staticData = $getWorkflowStaticData('global');\n\nconst syncToken = staticData.sync_token || '*';\n\nreturn {\n  json: {\n    sync_token: syncToken\n  }\n};"
      },
      "id": "c4101c65-7a08-4da1-8b96-8235bbaa07dc",
      "name": "Get Sync Token",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -672,
        96
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.todoist.com/sync/v9/sync",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "sync_token",
              "value": "={{ $json.sync_token }}"
            },
            {
              "name": "resource_types",
              "value": "[\"items\"]"
            }
          ]
        },
        "options": {}
      },
      "id": "3d0f465a-ed14-4a85-8c81-78e288503834",
      "name": "Todoist Sync API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -448,
        96
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "Me6UOmhM8SAuXUNt",
          "name": "Authorization"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Update Sync Token for next run\nconst staticData = $getWorkflowStaticData('global'); // Added $ here\nstaticData.sync_token = $input.first().json.sync_token;\n\n// Pass items forward with a 'source' tag to identify them later\nconst items = $input.first().json.items || [];\nreturn items.map(item => ({ \n  json: { \n    ...item, \n    _source: 'todoist' \n  } \n}));"
      },
      "id": "f00a3779-0797-43ea-9c2e-f91334ec9b25",
      "name": "Update Token & Extract Items",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -256,
        96
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1uws4XaPlETx4O4JfNfyPR6EaoZIqeNWXVPwBHIFGwg4",
          "mode": "list",
          "cachedResultName": "todoist-n8n-webhook",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1uws4XaPlETx4O4JfNfyPR6EaoZIqeNWXVPwBHIFGwg4/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1uws4XaPlETx4O4JfNfyPR6EaoZIqeNWXVPwBHIFGwg4/edit#gid=0"
        },
        "options": {}
      },
      "id": "0e81ac8c-7697-41d7-8b49-885a298fef41",
      "name": "Get Existing Rows",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.3,
      "position": [
        -208,
        -96
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "D04x7uKtze4p5m1w",
          "name": "Amber Google Sheets"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "jsCode": "// 1. Get all data from the previous Append node\nconst allData = $input.all().map(item => item.json);\n\n// 2. Separate the list back into two groups using the '_source' tag\nconst todoistItems = allData.filter(item => item._source === 'todoist');\nconst existingRows = allData.filter(item => item._source !== 'todoist');\n\nconst newRows = [];\n\n// Helper to check if this specific state (Created/Completed) is already in the sheet\nconst logExists = (id, isCompleted) => {\n  // We check if a row exists with the same ID AND the same completion status\n  // Note: Sheets might return 'TRUE'/'FALSE' strings or boolean values, so we compare loosely\n  return existingRows.some(row => \n    row['taskId'] == id && String(row['completed']).toLowerCase() == String(isCompleted).toLowerCase()\n  );\n};\n\n// 3. Process the Todoist items\ntodoistItems.forEach(item => {\n  // Skip deleted items\n  if (item.is_deleted) return;\n\n  const id = item.id;\n  \n  // Determine if this is a completion event or creation event\n  const isCompleted = item.checked === true; \n\n  // LOGIC:\n  // If task is Active (checked=false) -> Log it if we haven't logged an active version yet.\n  // If task is Done (checked=true)   -> Log it if we haven't logged a done version yet.\n  \n  if (!logExists(id, isCompleted)) {\n      newRows.push({\n        json: {\n          'taskId': id,\n          'taskName': item.content,\n          'completed': isCompleted, // This will be FALSE for created, TRUE for completed\n          'due': item.due ? item.due.date : '',\n          'priority': item.priority,\n          'description': item.description,\n          'sectionId': item.section_id,\n          'parentTaskId': item.parent_id,\n          'assigneeId': item.responsible_uid,\n          'createdDate': item.added_at, \n          'labels': item.labels ? item.labels.join(\", \") : \"\"\n        }\n      });\n  }\n});\n\nreturn newRows;"
      },
      "id": "ca3e6d92-d874-49fd-8c9a-181fa3651c95",
      "name": "Prepare New Rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        112
      ]
    },
    {
      "parameters": {},
      "id": "5dedd061-fac1-49e4-afa6-a63712d4e21e",
      "name": "Merge Data",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 2.1,
      "position": [
        16,
        112
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1uws4XaPlETx4O4JfNfyPR6EaoZIqeNWXVPwBHIFGwg4",
          "mode": "list",
          "cachedResultName": "todoist-n8n-webhook",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1uws4XaPlETx4O4JfNfyPR6EaoZIqeNWXVPwBHIFGwg4/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1uws4XaPlETx4O4JfNfyPR6EaoZIqeNWXVPwBHIFGwg4/edit#gid=0"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": [
            {
              "id": "taskId",
              "displayName": "taskId",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "taskName",
              "displayName": "taskName",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "sectionId",
              "displayName": "sectionId",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "parentTaskId",
              "displayName": "parentTaskId",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "completed",
              "displayName": "completed",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "due",
              "displayName": "due",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "priority",
              "displayName": "priority",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "description",
              "displayName": "description",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "parentTask",
              "displayName": "parentTask",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "section",
              "displayName": "section",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "assignee",
              "displayName": "assignee",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "createdDate",
              "displayName": "createdDate",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "labels",
              "displayName": "labels",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "4cf3a954-3e78-47c7-a87c-ee7de4904578",
      "name": "Add to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.3,
      "position": [
        496,
        160
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "D04x7uKtze4p5m1w",
          "name": "Amber Google Sheets"
        }
      }
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "todoist-updates",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -944,
        80
      ],
      "id": "2bc8e2e4-5d75-46b2-ae43-005cf4f4f657",
      "name": "Webhook",
      "webhookId": "ff8da601-2cf2-4ab8-84f3-2c766f9e9a98"
    }
  ],
  "connections": {
    "Get Sync Token": {
      "main": [
        [
          {
            "node": "Todoist Sync API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Todoist Sync API": {
      "main": [
        [
          {
            "node": "Update Token & Extract Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Token & Extract Items": {
      "main": [
        [
          {
            "node": "Merge Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Existing Rows": {
      "main": [
        [
          {
            "node": "Merge Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Prepare New Rows": {
      "main": [
        [
          {
            "node": "Add to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Data": {
      "main": [
        [
          {
            "node": "Prepare New Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Get Sync Token",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Existing Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "1d94077dbadd478a3fa551e5be967685a64c130b7870a76243ce62b52e1c02e2"
  }
}

Share the output returned by the last node

Expected output should be a new row here:

But getting no executions:

Information on your n8n setup

  • n8n version: Version 2.2.4
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app): GCP
  • Operating system: Mac

Did you d that?

Hi @Simon_Hubs thanks for your reply! Yes I did do that in browser yet my flow is not working still :frowning: is there anything else I should be looking at?

can you try to receive the webhook on another url or some testing tool like: https://beeceptor.com/

Do you have any kind of log in todoist for those events and webhooks?