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

How to Send Weekly Todoist Reports to Slack with n8n

Every Friday, n8n pulls the past 7 days of completed Todoist tasks, counts completions by project, and posts a formatted summary to a Slack channel for leadership review.

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

Best for

Engineering or product teams who already use Todoist for task tracking and want a zero-effort Friday summary in Slack without building a dashboard.

Not ideal for

Teams that need real-time task updates throughout the day — use a webhook-based notification workflow instead.

Sync type

scheduled

Use case type

reporting

Real-World Example

💡

A 12-person product team at a SaaS company uses this to post a Friday 4pm Slack digest to #leadership showing how many tasks each project completed that week. Before automation, the project manager spent 20–30 minutes every Friday manually counting completions in Todoist and copy-pasting into Slack. Now the report shows up automatically with per-project breakdowns and a total completion count.

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 account with API access — get your token at todoist.com/app/settings/integrations/developer
Slack workspace admin access or permission to create a Slack app at api.slack.com/apps
Slack bot installed to your workspace with chat:write and channels:read scopes granted
n8n instance running (self-hosted or n8n Cloud) with HTTP Request and Code nodes available
Bot user invited to the target Slack channel using /invite @YourBotName inside Slack

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Task Contentcontent
Project IDproject_id
Completed Atcompleted_at
Project Namename
Completion Count
Week Start Date
Slack Channel
1 optional field▸ show
Total Completions

Step-by-Step Setup

1

n8n Dashboard > + New Workflow

Create a new n8n workflow

Open your n8n instance and click the '+ New Workflow' button in the top-right corner of the dashboard. Give it a clear name like 'Weekly Todoist → Slack Report'. You'll land on an empty canvas with a single gray 'Add first step' node in the center. Everything you build will chain off this canvas left-to-right.

  1. 1Click '+ New Workflow' in the top-right of the n8n dashboard
  2. 2Click the pencil icon next to 'My workflow' at the top and rename it 'Weekly Todoist → Slack Report'
  3. 3Click 'Save' in the top-right to register the name
What you should see: You should see an empty canvas with your new workflow name displayed in the top bar.
2

Canvas > Add first step > Schedule Trigger

Add a Schedule trigger

Click the gray 'Add first step' node on the canvas. In the node picker that slides in from the right, search for 'Schedule' and select the 'Schedule Trigger' node. Set the trigger to run every Friday at 4:00 PM in your team's local timezone — this is critical to get right before anything else. The Schedule Trigger uses cron syntax under the hood; n8n exposes a dropdown UI for common intervals so you don't need to write raw cron.

  1. 1Click the gray 'Add first step' node
  2. 2Type 'Schedule' in the search box and select 'Schedule Trigger'
  3. 3Set 'Trigger Interval' to 'Weeks'
  4. 4Set 'Day of Week' to 'Friday'
  5. 5Set 'Hour' to '16' and 'Minute' to '0'
  6. 6Set 'Timezone' to your team's local timezone from the dropdown
What you should see: The Schedule Trigger node shows 'Every Friday at 4:00 PM [your timezone]' in the node summary panel.
Common mistake — n8n's default timezone is UTC. If you skip the timezone field, your report will fire at the wrong local time — potentially Friday midnight instead of Friday afternoon.
n8n
+
click +
search apps
Slack
SL
Slack
Add a Schedule trigger
Slack
SL
module added
3

Todoist Web App > Settings > Integrations > Developer > API token

Connect Todoist credentials

Add a new node after the Schedule Trigger by clicking the '+' connector on its right edge. Search for 'Todoist' and select the 'Todoist' node. In the node settings, click 'Credential for Todoist API' and then '+ Create New Credential'. n8n will prompt you to paste a Todoist API token. You get this from Todoist's web app under Settings > Integrations > Developer.

  1. 1Click the '+' connector on the right edge of the Schedule Trigger node
  2. 2Search 'Todoist' and click the Todoist node
  3. 3Click 'Credential for Todoist API' dropdown then '+ Create New Credential'
  4. 4Open Todoist in another tab: Settings > Integrations > Developer, copy your API token
  5. 5Paste the token into n8n's credential field and click 'Save'
What you should see: The credential dropdown shows your Todoist account email and a green checkmark confirming the connection is valid.
Common mistake — Todoist's API token does not expire, but it's tied to a personal account. Use a shared service account token if multiple team members will manage this workflow — otherwise it breaks when the original user's account is deactivated.
4

Todoist Node > Resource: Task > Operation: Get All

Fetch completed tasks from the past 7 days

With the Todoist node open, set 'Resource' to 'Task' and 'Operation' to 'Get All'. You need to filter by completion status and date range. Todoist's REST API v2 doesn't support completed task queries directly — you must use the Sync API endpoint via an HTTP Request node instead (see Step 5). For now, set this node's Operation to 'Get All' with 'Filter' set to 'today' just to confirm auth works, then you'll replace it.

  1. 1Set 'Resource' to 'Task'
  2. 2Set 'Operation' to 'Get All'
  3. 3Click 'Test step' to confirm tasks load
  4. 4Verify your projects appear in the output panel on the right
What you should see: The output panel shows a list of your active Todoist tasks with fields like id, content, project_id, and due date.
Common mistake — The standard Todoist node only fetches active (incomplete) tasks. To get completed tasks, you need the Sync API — which requires the HTTP Request node in Step 5. Don't spend time trying to find a 'completed' filter in the Todoist node; it doesn't exist there.
5

Canvas > HTTP Request Node > Parameters

Fetch completed tasks via Todoist Sync API

Delete the Todoist node from Step 4 and replace it with an 'HTTP Request' node connected directly to the Schedule Trigger. The Todoist Sync API endpoint for completed tasks is https://api.todoist.com/sync/v9/items/completed/get_all. You'll pass your API token as a Bearer token in the Authorization header and set since as a query parameter calculated 7 days back from today. Use n8n's expression editor to compute the date dynamically.

  1. 1Add an HTTP Request node after the Schedule Trigger
  2. 2Set 'Method' to 'GET'
  3. 3Set 'URL' to 'https://api.todoist.com/sync/v9/items/completed/get_all'
  4. 4Under 'Authentication', select 'Generic Credential Type', choose 'Header Auth', set Header Name to 'Authorization' and Header Value to 'Bearer YOUR_TODOIST_TOKEN'
  5. 5Add Query Parameter: Name = 'since', Value = '{{ $now.minus({days: 7}).toISO() }}'
  6. 6Add Query Parameter: Name = 'limit', Value = '200'
  7. 7Click 'Test step'
What you should see: The output panel shows a JSON object with an 'items' array. Each item contains fields like task_id, content, project_id, completed_at, and user_id.
Common mistake — The Sync API returns a flat list — not paginated the same way as REST v2. The maximum `limit` is 200. If your team completes more than 200 tasks per week, you'll need to add offset-based pagination in a subsequent Code node before the aggregation step.
6

Canvas > Code Node > JavaScript

Extract and aggregate completions by project

Add a 'Code' node after the HTTP Request node. This is where you process the raw API response into a structured summary. The Sync API returns a nested JSON object, so you need to access items.items (double-nested). Write JavaScript to count completions per project_id, then map project IDs to human-readable names. See the Pro Tip code section for the full transformation script.

  1. 1Click '+' after the HTTP Request node and search for 'Code'
  2. 2Select 'Code' node and set 'Language' to 'JavaScript'
  3. 3Set 'Mode' to 'Run Once for All Items'
  4. 4Paste the aggregation script from the Pro Tip section into the code editor
  5. 5Click 'Test step' and verify the output shows one item per project with a count field
What you should see: The output panel shows an array of objects, each with project_name, completed_count, task_titles, and week_start fields.
Common mistake — If you use 'Run Once for Each Item' mode instead of 'Run Once for All Items', the code runs separately per task rather than across all tasks — your aggregation counts will all be 1. Set the mode correctly before pasting the script.
7

Canvas > HTTP Request Node (second) > REST v2 Projects endpoint

Fetch project names from Todoist

The Sync API returns project_id values, not names. Add a second HTTP Request node before the Code node (between Steps 5 and 6) to fetch all projects. Use GET https://api.todoist.com/rest/v2/projects with the same Bearer token. This returns an array of project objects with id and name fields. Pass this data into the Code node alongside the completed tasks so you can do the ID-to-name lookup inside one Code node.

  1. 1Add a second HTTP Request node between the completed-tasks node and the Code node
  2. 2Set Method to 'GET', URL to 'https://api.todoist.com/rest/v2/projects'
  3. 3Add the same Bearer token Authorization header
  4. 4In the Code node, reference this node's output using $('HTTP Request Projects').all()
What you should see: The Code node now has access to both completed tasks and a project name lookup array, and the output shows project names like 'Product Roadmap' instead of numeric IDs.
8

api.slack.com/apps > Your App > OAuth & Permissions > Bot Token Scopes

Connect Slack credentials

Add a Slack node after the Code node. Click 'Credential for Slack API' and select '+ Create New Credential'. Choose 'OAuth2' as the auth type — n8n will walk you through authorizing a Slack app. If you don't have a Slack app yet, you'll need to create one at api.slack.com/apps, add the chat:write and channels:read scopes, install it to your workspace, and paste the Bot OAuth Token into n8n.

  1. 1Add a Slack node after the Code node
  2. 2Click 'Credential for Slack API' > '+ Create New Credential'
  3. 3Go to api.slack.com/apps and create or open your existing Slack app
  4. 4Under 'OAuth & Permissions', add scopes: chat:write, channels:read
  5. 5Click 'Install to Workspace' and copy the 'Bot User OAuth Token'
  6. 6Paste the token into n8n's Slack credential field and click 'Save'
What you should see: The Slack credential dropdown shows your workspace name with a green checkmark.
Common mistake — The bot must be manually invited to the target Slack channel with /invite @YourBotName before it can post there. This step is not handled by n8n — if you skip it, you'll get a 'not_in_channel' error at runtime.
9

Slack Node > Resource: Message > Operation: Post > Channel + Text

Format and send the Slack message

In the Slack node, set 'Resource' to 'Message' and 'Operation' to 'Post'. Set 'Channel' to the channel ID or name where leadership reviews updates (e.g., #leadership-updates). For the message body, use n8n's expression editor to build a formatted string from the Code node's output. Use Slack's Block Kit or a plain mrkdwn text block with *bold* project names and task counts on separate lines.

  1. 1Set 'Resource' to 'Message' and 'Operation' to 'Post'
  2. 2Set 'Channel' to your target channel name or ID
  3. 3Set 'Text' to a dynamic expression referencing the Code node output (see Pro Tip for the full formatter)
  4. 4Enable 'Blocks' if you want structured Block Kit layout instead of plain text
  5. 5Click 'Test step' to send a test message to the channel
What you should see: A formatted Slack message appears in the target channel showing project names, completion counts, and the reporting week.
Common mistake — Slack's mrkdwn in the 'text' field renders differently than in Block Kit 'section' blocks. If your formatting looks broken, switch to a Block Kit body — it's more predictable for multi-line reports with headers.
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
10

Node Settings > Continue on Fail | Canvas > Error Branch > Slack Alert Node

Add error handling with a fallback notification

Click the Slack node, then open the 'Settings' tab inside the node panel. Enable 'Continue on Fail' to prevent the workflow from silently dying if Slack rejects the message. Add a separate Slack node connected to the error output of the Code node and the HTTP Request nodes — this fallback node sends a plain alert to a #alerts channel if the data fetch fails. This takes 5 extra minutes and saves you from missing a report without knowing why.

  1. 1Click the Code node, open the 'Settings' tab, enable 'Continue on Fail'
  2. 2Do the same for both HTTP Request nodes
  3. 3Hover over the Code node and click the red error output connector
  4. 4Add a Slack node on the error branch
  5. 5Set its channel to '#alerts' and text to 'Weekly Todoist report failed — check n8n workflow logs'
What you should see: The canvas shows a main execution path (green connectors) and a red error branch leading to the alert Slack node.
11

Canvas > Top-right toggle > Active | Executions tab

Activate the workflow

Click the gray 'Inactive' toggle in the top-right of the canvas and flip it to 'Active'. n8n will now run this workflow automatically every Friday at 4:00 PM in your configured timezone. The first real execution happens at the next scheduled Friday — click 'Execute Workflow' manually now to confirm everything works end-to-end before then. Check the Executions tab to review the full run log.

  1. 1Click 'Save' to save the current workflow state
  2. 2Click 'Execute Workflow' to run it manually right now
  3. 3Verify the Slack message appears in your target channel
  4. 4Click the 'Inactive' toggle to switch it to 'Active'
  5. 5Open the Executions tab and confirm the manual run shows a green success status
What you should see: The toggle shows 'Active' in green. The Executions tab shows a successful run with all nodes in green. The Slack channel shows the formatted weekly report.

Paste this into the Code node (set to 'Run Once for All Items'). It pulls completed tasks from the Sync API node, fetches project names from the separate REST v2 node, groups completions by project, builds the formatted Slack message string, and returns a single item containing the final text ready for the Slack node.

JavaScript — Code Node// Node names assumed:
▸ Show code
// Node names assumed:
// 'HTTP Completed Tasks' = Sync API response
// 'HTTP Projects' = REST v2 projects list

... expand to see full code

// Node names assumed:
// 'HTTP Completed Tasks' = Sync API response
// 'HTTP Projects' = REST v2 projects list

const completedRaw = $('HTTP Completed Tasks').all();
const projectsRaw = $('HTTP Projects').all();

// Extract the items array from the Sync API response
const tasks = completedRaw[0].json.items || [];

// Build a project ID → name lookup map
const projectMap = {};
for (const p of projectsRaw[0].json) {
  projectMap[p.id] = p.name;
}

// Group tasks by project_id
const grouped = {};
for (const task of tasks) {
  const pid = task.project_id;
  if (!grouped[pid]) {
    grouped[pid] = { name: projectMap[pid] || `Project ${pid}`, tasks: [] };
  }
  grouped[pid].tasks.push(task.content);
}

// Calculate week range label
const now = new Date();
const weekStart = new Date(now);
weekStart.setDate(now.getDate() - 7);
const fmt = (d) => d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
const weekLabel = `${fmt(weekStart)} – ${fmt(now)}`;

// Build Slack message text
let totalCount = 0;
let lines = [`*Weekly Progress Report — ${weekLabel}*\n`];

for (const pid of Object.keys(grouped)) {
  const proj = grouped[pid];
  const count = proj.tasks.length;
  totalCount += count;
  lines.push(`*${proj.name}* — ${count} task${count !== 1 ? 's' : ''} completed`);
  for (const t of proj.tasks) {
    lines.push(`  • ${t}`);
  }
  lines.push('');
}

lines.push(`*Total: ${totalCount} task${totalCount !== 1 ? 's' : ''} completed this week*`);

return [{ json: { slackMessage: lines.join('\n') } }];

Scaling Beyond 200+ tasks completed per week+ Records

If your volume exceeds 200+ tasks completed per week records, apply these adjustments.

1

Paginate the Sync API with offset

The Sync API's max limit is 200 items per call. Add a loop using n8n's Loop Over Items node: fetch page 1, check if the response returned exactly 200 items, and if so fetch page 2 with offset=200. Continue until a response returns fewer than 200 items.

2

Merge paginated results in the Code node

Collect all paginated responses into a single array before aggregating. Pass each page's items array into a shared accumulator variable. Run the project grouping logic once after all pages are collected, not once per page — otherwise your counts will be fragmented across multiple Slack messages.

3

Watch the Todoist Sync API rate limit

Todoist enforces a limit of 450 requests per 15 minutes per user token. If your pagination loop makes many calls in quick succession for large teams, add a Wait node between paginated requests — set it to 2 seconds. This keeps you well inside the rate limit even at high volumes.

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 self-host your automation stack, want to keep Todoist API tokens off third-party servers, or need the Code node to do non-trivial data transformation — grouping tasks by project, building formatted strings, handling edge cases like empty weeks. n8n gives you a real JavaScript environment, not a formula box with limited functions. The one scenario where you'd skip n8n: if your team has no one comfortable reading a JavaScript error message and you just want a basic task-count-to-Slack message, Make's scenario builder with its Array/Collection modules will get you there faster without touching code.

Cost

Cost math: this workflow makes 3 API calls per run (Todoist Sync API, Todoist projects REST API, Slack post) and runs 52 times per year. That's 156 executions annually on n8n Cloud. n8n Cloud's Starter plan is $20/month and includes 2,500 executions/month — this workflow uses about 13 executions/month, which is well under the limit even with multiple other workflows running. Self-hosted n8n is free with your own server costs. Compare that to Zapier, where the same workflow requires a multi-step Zap — the Schedule trigger plus HTTP Request steps — which runs on the Professional plan at $49/month minimum. You're paying 2.5x more on Zapier for a workflow that's actually harder to build there.

Tradeoffs

Make handles this well with its HTTP modules and the built-in Array Aggregator — no custom code needed if your formatting requirements are simple. Zapier can technically do it with Code by Zapier but the lack of a real code editor makes debugging the Sync API response painful. Power Automate has a solid Schedule trigger and HTTP action but formatting multi-line Slack messages in its expression language (concat, replace, newline characters) takes longer to get right than n8n's JavaScript template literals. Pipedream is the strongest alternative: native Node.js steps, clean async/await syntax, and a built-in Todoist source. If you're already on Pipedream for other workflows, build it there — it'll feel more natural. n8n wins here if self-hosting matters to you or if you're already running other n8n workflows and don't want to add a second platform.

Three things you'll hit after setup: First, the Todoist Sync API returns project_id as a string in some responses and an integer in others depending on task age — your projectMap lookup will silently fail if you don't coerce both sides to strings with String(pid). Second, Slack's mrkdwn asterisks for bold (*text*) don't render in the Block Kit 'plain_text' element type — use 'mrkdwn' element type or switch to the plain text field in the Slack node. Third, n8n's Schedule Trigger doesn't retry if your server is down at exactly 4pm Friday. If uptime matters, add a second Schedule Trigger set to 4:15pm pointing to the same workflow — it will re-post, so add a simple deduplication flag using n8n's static data store to prevent double-posting.

Ideas for what to build next

  • Add per-assignee breakdownsThe Todoist Sync API returns a user_id on each completed task. Fetch collaborator data from the REST v2 /collaborators endpoint and extend the Code node to group by person — useful when leadership wants to see individual output, not just project totals.
  • Post a Monday goals digestAdd a second scheduled workflow that runs Monday morning and pulls all tasks due that week from Todoist's active task endpoint. Post it to the same Slack channel as a counterpart to Friday's completion report — so leadership sees what's planned and what got done.
  • Archive reports to Google SheetsAfter the Slack node, add a Google Sheets node that appends one row per project per week with the project name, completion count, and week date. Over 8–12 weeks you'll have a trend dataset that's easy to pull into a chart without any manual tracking.

Related guides

Was this guide helpful?
Slack + Todoist overviewn8n profile →