Intermediate~20 min setupCommunication & Project ManagementVerified April 2026
Slack logo
Todoist logo

How to Broadcast Todoist Task Completions to Slack with n8n

When a task is marked complete in Todoist, n8n catches the webhook and posts a formatted announcement to a Slack channel within seconds.

Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.

Best for

Teams using Todoist for project tracking who want stakeholders to see completed work in Slack without anyone manually posting updates.

Not ideal for

Teams completing hundreds of tasks per day — the Slack channel will become noise; use a daily digest with Make instead.

Sync type

real-time

Use case type

notification

Real-World Example

💡

A 12-person product agency tracks client deliverables in Todoist projects. Every time a designer or developer marks a task done, n8n posts to #project-wins in Slack with the task name, assignee, and project. Before this, the project manager spent 20 minutes each morning manually updating the team on what shipped yesterday — now they see it as it happens.

What Will This Cost?

Drag the slider to your expected monthly volume.

/mo
505005K50K

Each platform counts differently — Zapier: 1 task per trigger. Make: 1 operation per module per record. n8n: 1 execution per run.

Prices shown for annual billing. Based on published pricing as of April 2026.

Estimated ROI

1000

min saved/mo

$583

labor value/mo

Free

no platform cost

Based on ~2 min manual effort per operation at $35/hr fully loaded labor cost.

Implementation

Skip the setup

Import this workflow directly into n8n

Copy the pre-built n8n blueprint and paste it straight into n8n. All modules, filters, and field mappings are already configured — you just need to connect your accounts.

Before You Start

Make sure you have everything ready.

Todoist API token — found at todoist.com > Settings > Integrations > Developer
Todoist account with at least one project containing tasks you can complete
Slack workspace with permission to install apps and post to at least one channel
n8n instance (Cloud or self-hosted) with internet-accessible webhook URLs — localhost will not work with Todoist's outbound webhooks
Postman, curl, or any HTTP client to register webhooks via Todoist's API (no UI for this in Todoist)

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Task Nameevent_data.content
Completed Atevent_data.completed_at
Project IDevent_data.project_id
Slack Channel
Slack Message Text
4 optional fields▸ show
Task Description / Notesevent_data.description
Completer User IDevent_data.user_id
Task Priorityevent_data.priority
Due Dateevent_data.due.date

Step-by-Step Setup

1

n8n Canvas > New Workflow

Create a new n8n workflow

Log into your n8n instance and click the orange 'New Workflow' button in the top right of the canvas. Give it a clear name like 'Todoist → Slack Task Broadcast'. You'll land on a blank canvas with a single grey node labeled 'Start'. You won't use that Start node — you're replacing it with a webhook trigger.

  1. 1Click 'New Workflow' in the top-right corner
  2. 2Click the workflow title at the top and rename it to 'Todoist → Slack Task Broadcast'
  3. 3Click the grey 'Start' node and press the Delete key to remove it
What you should see: You should see an empty canvas with the workflow name updated in the title bar.
Common mistake — If you're on n8n Cloud's free tier, you get 5 active workflows. Archiving unused workflows before creating this one prevents hitting that cap.
2

Canvas > + > Webhook

Add a Webhook trigger node

Click the '+' button in the center of the empty canvas to open the node selector. Search for 'Webhook' and select it. In the right panel, set the HTTP Method to POST and note the webhook URL shown — you'll paste this into Todoist in the next step. Set Authentication to 'None' for now (Todoist sends a secret token you can validate later in a code node).

  1. 1Click the '+' icon on the empty canvas
  2. 2Type 'Webhook' in the search bar and select the Webhook node
  3. 3Set HTTP Method to 'POST'
  4. 4Copy the 'Test URL' shown in the node panel — you'll need it in Todoist
What you should see: The Webhook node appears on the canvas with a green URL displayed. Two URLs are shown: Test URL and Production URL. Use the Test URL during setup.
Common mistake — The Test URL and Production URL are different in n8n. Todoist's webhook subscription must be updated to the Production URL before you activate the workflow — if you forget this swap, the live workflow will never receive events.
n8n
+
click +
search apps
Slack
SL
Slack
Add a Webhook trigger node
Slack
SL
module added
3

Todoist Web App > Settings > Integrations > Developer

Register the webhook in Todoist

Todoist doesn't expose webhook settings in its main UI — you register webhooks via their REST API. Open a terminal or use a tool like Postman. You need your Todoist API token from Settings > Integrations > Developer in the Todoist web app. Send a POST request to https://api.todoist.com/sync/v9/webhooks with your token and the n8n webhook URL. Subscribe only to the 'item:completed' event to avoid flooding n8n with unrelated triggers.

  1. 1In Todoist, go to Settings > Integrations > Developer and copy your API token
  2. 2Open Postman (or curl in a terminal)
  3. 3POST to https://api.todoist.com/sync/v9/webhooks with header Authorization: Bearer YOUR_TOKEN
  4. 4Set the request body: { "url": "YOUR_N8N_WEBHOOK_URL", "events": ["item:completed"] }
  5. 5Confirm the response returns a webhook ID — save it if you need to delete this webhook later
What you should see: Todoist returns a JSON object containing a webhook_id. This confirms Todoist will now POST to your n8n URL every time a task is completed.
Common mistake — Todoist's webhook API only supports one URL per token per event type. If you already have a webhook registered for 'item:completed' on this token, registering a second one will not error — both will fire, and you'll get duplicate Slack messages.
4

n8n Canvas > Webhook Node > Test Workflow

Capture a test event from Todoist

Back in n8n, click 'Test Workflow' so the Webhook node starts listening. Switch to Todoist and complete any task in any project. Within 2-3 seconds, n8n should receive the event and display the raw payload in the Webhook node's output panel. Expand the data to see the fields Todoist sends — you need content (task name), completed_at, project_id, and user_id.

  1. 1Click 'Test Workflow' at the top of the n8n canvas
  2. 2Switch to Todoist in another tab
  3. 3Mark any task as complete by clicking the circle next to it
  4. 4Switch back to n8n and click on the Webhook node to see the incoming data
What you should see: The Webhook node shows green with an output panel listing all fields from Todoist's payload. You should see fields like content, completed_at, project_id, and user_id in the data.
n8n
▶ Run once
executed
Slack
Todoist
Todoist
🔔 notification
received
5

Canvas > + > Code

Add a Code node to format the message

Click the '+' after the Webhook node and add a 'Code' node. This is where you build the Slack message string from the raw Todoist payload. The Todoist webhook body is nested — the task data lives inside event_data, not at the top level. Write a short JavaScript snippet to extract task name, project ID, and completion time, then format them into a readable Slack message string. See the Pro Tip section for the full code.

  1. 1Click '+' after the Webhook node
  2. 2Search for 'Code' and select the Code node
  3. 3Set the language to JavaScript
  4. 4Paste the transformation code from the Pro Tip section below
  5. 5Click 'Execute Node' to verify the output shows a formatted message string
What you should see: The Code node outputs a single item with a message field containing a human-readable string like '✅ *Deploy staging environment* has been completed by Sarah Chen in project #2316456789.'
Common mistake — Todoist sends project_id and user_id as numeric IDs, not human-readable names. The Code node in this step formats what it has — if you need real project names and assignee names, you must add Todoist API lookup nodes after this step before posting to Slack.

Paste this into the first Code node (Step 5). It extracts and normalizes all task fields from Todoist's nested webhook payload, formats the timestamp into human-readable UTC, adds an urgency emoji for priority-4 tasks, and guards against the Todoist verification ping — saving you a separate IF node.

JavaScript — Code Node// n8n Code Node — Todoist Task Completion Parser
▸ Show code
// n8n Code Node — Todoist Task Completion Parser
// Paste into the first Code node after the Webhook trigger
const payload = items[0].json;

... expand to see full code

// n8n Code Node — Todoist Task Completion Parser
// Paste into the first Code node after the Webhook trigger

const payload = items[0].json;

// Guard against Todoist's webhook verification ping
if (!payload.event_data) {
  return [];
}

const task = payload.event_data;

// Extract core fields
const taskName = task.content || 'Unnamed Task';
const projectId = task.project_id ? task.project_id.toString() : null;
const userId = task.user_id ? task.user_id.toString() : null;
const priority = task.priority || 1; // 1=normal, 2=medium, 3=high, 4=urgent
const description = task.description || '';
const dueDate = task.due ? task.due.date : null;

// Format completion timestamp
const rawTimestamp = task.completed_at || new Date().toISOString();
const completedDate = new Date(rawTimestamp);
const formattedTime = completedDate.toLocaleString('en-US', {
  month: 'short',
  day: 'numeric',
  year: 'numeric',
  hour: '2-digit',
  minute: '2-digit',
  timeZone: 'UTC',
  timeZoneName: 'short'
});

// Determine urgency emoji based on Todoist priority
const priorityEmoji = priority === 4 ? '🔥✅' : '✅';

// Check if task was completed after its due date
let lateFlag = '';
if (dueDate) {
  const due = new Date(dueDate);
  if (completedDate > due) {
    lateFlag = ' _(completed late)_';
  }
}

// Build partial message — project name gets added in the merge Code node (Step 7)
const partialMessage = `${priorityEmoji} *${taskName}* has been completed!${lateFlag}`;

return [
  {
    json: {
      taskName,
      projectId,
      userId,
      priority,
      description,
      formattedTime,
      partialMessage,
      rawTimestamp
    }
  }
];
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
6

Canvas > + > HTTP Request

Look up the project name via Todoist API

Add an HTTP Request node after the Code node. Configure it to GET https://api.todoist.com/rest/v2/projects/{{ $json.project_id }} using the Todoist API token in a Bearer Authorization header. This returns the project name so your Slack message reads 'Website Redesign' instead of '2316456789'. Store this result — the next node will merge it with the formatted message.

  1. 1Click '+' after the Code node and add an 'HTTP Request' node
  2. 2Set Method to GET
  3. 3Set URL to https://api.todoist.com/rest/v2/projects/{{ $('Code').item.json.project_id }}
  4. 4Add a header: Authorization = Bearer YOUR_TODOIST_API_TOKEN
  5. 5Click 'Execute Node' to confirm it returns a name field with the project name
What you should see: The HTTP Request node returns a JSON object with a name field containing the project's human-readable name, like 'Website Redesign' or 'Q3 Marketing Campaign'.
Common mistake — If the completed task belongs to a project shared with you (not owned by you), the REST v2 API may return a 403. In that case, use the Sync API endpoint https://api.todoist.com/sync/v9/projects/get?project_id=ID instead — it has broader access.
7

Canvas > + > Code

Merge task data and project name

Add another Code node after the HTTP Request node. Here you combine the formatted message from step 5 with the project name from step 6 to produce the final Slack message. Reference the previous nodes using n8n's $('NodeName').item.json syntax. Build the final message string with emoji, bold task name (using Slack's mrkdwn format), and the resolved project name.

  1. 1Click '+' after the HTTP Request node and add a second Code node
  2. 2Name it 'Build Final Message' for clarity
  3. 3Reference the task name from the first Code node using $('Code').item.json.taskName
  4. 4Reference the project name using $('HTTP Request').item.json.name
  5. 5Output a single object with a slackMessage field
What you should see: The second Code node outputs one item with a slackMessage field like '✅ *Deploy staging environment* has been completed! Project: Website Redesign · Completed at: 2024-03-14 09:32 UTC'.
8

Canvas > + > Slack > Send a Message

Add the Slack node and connect your workspace

Click '+' after the merge Code node and search for 'Slack'. Select the Slack node and choose 'Send a Message' as the operation. Click 'Create New Credential' and authenticate via OAuth2 — this opens Slack's authorization screen where you'll approve the connection. You need the channels:write and chat:write OAuth scopes granted. n8n will store the token after you approve.

  1. 1Click '+' after the second Code node
  2. 2Search for 'Slack' and select the Slack node
  3. 3Set Operation to 'Send a Message'
  4. 4Click 'Credential for Slack API' > 'Create New'
  5. 5Complete the OAuth flow in the popup window and click Allow
What you should see: The Slack credential shows a green checkmark and your workspace name appears in the credential selector. The node is now authorized to post messages.
Common mistake — If your Slack workspace requires admin approval for new app installs, the OAuth flow will stall at a 'Waiting for approval' screen. Have a Slack admin approve the n8n app in Slack's App Management settings before attempting this step.
n8n settings
Connection
Choose a connection…Add
click Add
Slack
Log in to authorize
Authorize n8n
popup window
Connected
green checkmark
9

Slack Node > Parameters > Channel + Message

Configure the Slack message destination and body

In the Slack node, set Channel to the channel where completed tasks should broadcast — type the channel name like #project-wins. For the message text, reference the slackMessage field from your merge Code node using the expression {{ $json.slackMessage }}. Enable 'Use Blocks' only if you want richer formatting — for most teams, plain mrkdwn text in the Message field is enough and faster to set up.

  1. 1In the Channel field, type the target channel name (e.g. #project-wins)
  2. 2Click the expression toggle (the lightning bolt icon) next to the Message field
  3. 3Type {{ $json.slackMessage }} in the expression editor
  4. 4Leave 'As User' unchecked unless you want the message to appear from your personal account
  5. 5Click 'Execute Node' to send a test message to Slack
What you should see: A message appears in your chosen Slack channel within 3 seconds showing the completed task name, project, and timestamp. The sender will show as your n8n Slack app name.
Common mistake — If you type the channel name without the # prefix AND the channel is private, the Slack API returns a channel_not_found error even if the channel exists. For private channels, add the n8n Slack app to the channel first, then use the channel's ID (starts with C) instead of its name.
10

n8n Canvas > Webhook Node > Production URL > Active Toggle

Activate the workflow with the Production URL

Before activating, go back to the Webhook node and copy the Production URL (not the Test URL). Update your Todoist webhook registration — make a DELETE request to remove the old webhook, then POST a new one with the Production URL. Back in n8n, click the 'Active' toggle in the top right of the canvas to turn the workflow on. Todoist will now send completed task events to the production endpoint.

  1. 1Click the Webhook node and copy the 'Production URL'
  2. 2In Postman or curl, DELETE the existing Todoist webhook: DELETE https://api.todoist.com/sync/v9/webhooks/YOUR_WEBHOOK_ID
  3. 3Register a new Todoist webhook with the Production URL using the same POST request as Step 3
  4. 4In n8n, click the grey 'Inactive' toggle in the top-right corner to set the workflow to Active
  5. 5Complete a task in Todoist and confirm the Slack message fires within 5 seconds
What you should see: The toggle turns green and shows 'Active'. A completed Todoist task triggers a Slack message in under 5 seconds. The n8n execution log shows a successful run in the Executions tab.
Common mistake — Copy the webhook URL carefully — it expires if you regenerate it, and any scenarios using the old URL will silently stop working.

Going live

Production Checklist

Before you turn this on for real, confirm each item.

Troubleshooting

Common errors and how to fix them.

Frequently Asked Questions

Common questions about this workflow.

Analysis

VerdictWhy n8n for this workflow

Use n8n for this if you want full control over the message format without paying per-task fees. Todoist users on active teams complete 50-200 tasks per week — at that volume, Zapier costs real money ($20-50/month on paid tiers) while n8n on a $5 VPS handles the same load for free. The other reason to pick n8n: the Todoist webhook sends project_id and user_id as raw integers, so you need a code node or HTTP lookup step to resolve them to readable names. n8n's Code node and chained HTTP Request nodes handle this cleanly in a single workflow. If your team has zero technical people and no one will maintain a self-hosted instance, use Zapier instead — the setup takes 10 minutes and Zapier's Todoist integration resolves project names automatically.

Cost

The math here is straightforward. Each task completion runs 4 n8n nodes: Webhook, Code, HTTP Request (project lookup), Slack. That's one execution per completed task. A 10-person team completing 20 tasks/day = 600 executions/month. n8n Cloud's free tier gives you 2,500 executions/month — this workflow fits comfortably within it at no cost. Zapier's free tier caps at 100 tasks/month, so you'd hit the wall in the first week. Make's free tier gives 1,000 operations/month — at 4 operations per run, that's 250 task completions before you pay. Self-hosted n8n has no execution limits at all, just server costs.

Tradeoffs

Zapier has one real advantage here: its Todoist trigger resolves project names automatically — no API lookup step needed. That saves two nodes and 10 minutes of setup. Make's advantage is its visual router module, which makes filtering by project or priority easier to read and maintain than an n8n IF/Switch chain. Power Automate can do this workflow but Todoist has no official Power Automate connector, so you'd build a custom HTTP trigger that takes 3x as long to configure. Pipedream is the closest competitor to n8n for this use case — its Todoist event source handles the webhook registration for you, skipping the Postman step entirely. For most developers, that's a meaningful time saver. n8n still wins on self-hosting cost and the ability to keep all data on your own infrastructure, which matters for teams with privacy constraints.

Three things you'll hit after setup. First: Todoist's webhook verification ping. When you first register the webhook, Todoist sends a POST with no event_data to confirm the endpoint is alive. If your Code node doesn't handle this, it throws an error on the very first execution — add a guard clause returning [] when event_data is absent. Second: the Slack channel membership issue. n8n's Slack app needs to be explicitly added to private channels — it won't auto-join and will return a cryptic not_in_channel error instead. Third: Todoist's webhook auto-disable behavior. Ten consecutive non-200 responses from your n8n endpoint and Todoist silently stops sending events. If your n8n instance restarts or errors during a deploy, you can lose the webhook without any notification. Build a weekly sanity check — a scheduled n8n workflow that hits the Todoist webhooks list API and alerts you if the webhook is missing or inactive.

Ideas for what to build next

  • Route completions to different Slack channels by projectAdd a Switch node between the merge Code node and the Slack node. Map specific Todoist project_ids to their corresponding Slack channels — completions from 'Website Redesign' go to #design-wins, completions from 'Q4 Sales' go to #sales-wins.
  • Send a daily digest instead of per-task messagesReplace the webhook trigger with a Schedule trigger set to 9 AM daily. Query the Todoist Sync API for all tasks completed in the past 24 hours, group them by project, and post a single formatted summary to Slack instead of one message per task.
  • Add a Todoist comment when the Slack post succeedsAfter the Slack node, add an HTTP Request node that POSTs a comment to the completed task via https://api.todoist.com/rest/v2/comments. The comment can say 'Announced in #project-wins at [time]' — creating a paper trail directly on the task.

Related guides

Was this guide helpful?
Slack + Todoist overviewn8n profile →