Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with Todoist

Integrate Bolt.new with Todoist using your personal API token from Settings → Integrations → Developer and the Todoist REST API v2. Build custom task dashboards, create tasks with natural language due dates, manage projects and labels — all through a Next.js API route that keeps your token server-side. Todoist's clean API and natural language date parsing make it one of the easiest productivity integrations to build.

What you'll learn

  • How to find your Todoist API token in Settings → Integrations → Developer
  • How to fetch active tasks and projects from Todoist via a secure server-side API route
  • How to create tasks using Todoist's natural language due date processing
  • How to build a custom task dashboard or Kanban board using Todoist data in a React component
  • How to handle Todoist webhooks for real-time updates after deploying your app
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read25 minutesProductivityApril 2026RapidDev Engineering Team
TL;DR

Integrate Bolt.new with Todoist using your personal API token from Settings → Integrations → Developer and the Todoist REST API v2. Build custom task dashboards, create tasks with natural language due dates, manage projects and labels — all through a Next.js API route that keeps your token server-side. Todoist's clean API and natural language date parsing make it one of the easiest productivity integrations to build.

Building Custom Task Dashboards and Automations with Todoist and Bolt.new

Todoist's REST API v2 is one of the cleanest and best-documented productivity APIs available. It gives you full access to your tasks, projects, sections, labels, comments, and reminders. With this API you can build experiences that Todoist's own interface does not offer — consolidated multi-project dashboards, automated task creation triggered by form submissions or external events, custom Kanban boards with your own column logic, and reporting views formatted exactly for your workflow.

One of Todoist's standout features for Bolt integrations is natural language due date processing. When you create or update a task, you can pass the due_string field with values like 'every Monday at 9am', 'tomorrow at noon', or 'in 3 days' — and Todoist parses them into proper timestamps automatically. This means users of your Bolt app can type dates in plain English rather than interacting with a date picker, resulting in a much more natural UX with minimal frontend code.

Because the Todoist integration relies entirely on outbound HTTP requests, you can build and test the full feature set — reading tasks, creating tasks, updating priorities, adding comments — inside Bolt's WebContainer without deploying first. The only capability that requires a deployed URL is Todoist webhooks, which push real-time notifications to your server when tasks are added, completed, or updated. For most task dashboards, periodic polling every 30 seconds is a perfectly adequate alternative that avoids the deployment requirement during development.

Integration method

Bolt Chat + API Route

Bolt generates the Todoist integration through conversation — you describe the task board or automation you want and Bolt writes the API route and React component code. Todoist uses a simple Bearer token for authentication, so the full integration can be built and tested in Bolt's development environment using outbound API calls. All API calls flow through server-side Next.js routes to keep your token out of the browser bundle.

Prerequisites

  • A Todoist account with at least one project containing tasks
  • Your Todoist API token (found at todoist.com/app/settings/integrations/developer — scroll to API token section)
  • A Next.js project in Bolt.new (prompt 'Create a Next.js app' to get started)
  • Your project IDs (visible in the URL when you open a project in Todoist: app.todoist.com/app/project/{projectId})

Step-by-step guide

1

Get your Todoist API token and set up environment variables

Todoist provides a personal API token that grants access to all your data. To find it, log into Todoist and navigate to Settings (click your avatar bottom-left) → Integrations → Developer. Your API token is displayed under the 'API token' heading. Copy it — this is what authenticates every API call your Bolt app makes to Todoist. Unlike some APIs that use API keys as query parameters, Todoist uses standard HTTP Bearer token authentication. Every request includes an Authorization header in the format 'Bearer {your-token}'. This is different from ClickUp (which uses the raw token without 'Bearer') — with Todoist you do need the 'Bearer' prefix. Create a .env.local file in your Bolt project root with your token. Since the token must stay server-side, do not prefix it with NEXT_PUBLIC_ and do not prefix it with VITE_. Access it only inside Next.js API routes using process.env.TODOIST_API_TOKEN. Your project ID is safe to expose publicly (it is just an identifier), but for consistency keep it in .env as well. You can find your project IDs by opening a project in Todoist — the project ID appears in the URL: app.todoist.com/app/project/2349123456. You can also fetch all your projects programmatically using the /projects endpoint once your token is configured.

Bolt.new Prompt

Create a Next.js app with a Todoist integration. Add a .env.local file with TODOIST_API_TOKEN and TODOIST_PROJECT_ID as placeholder variables. Create a /api/todoist/projects route that fetches all my Todoist projects using the Todoist REST API v2 and returns them as JSON. Use Bearer token authentication.

Paste this in Bolt.new chat

.env.local
1// .env.local
2TODOIST_API_TOKEN=your_api_token_here
3TODOIST_PROJECT_ID=your_default_project_id

Pro tip: Your Todoist API token is a 40-character hex string. If you share your Bolt project or push to GitHub, make sure .env.local is in your .gitignore file — it should be by default in Next.js projects.

Expected result: Your .env.local file is created, and calling /api/todoist/projects returns a JSON array of all your Todoist projects with their IDs, names, and colors.

2

Fetch active tasks and display them in a dashboard

The Todoist REST API v2 uses a simple, intuitive endpoint structure. To fetch tasks, make a GET request to https://api.todoist.com/rest/v2/tasks. Without any query parameters, this returns all active (not completed) tasks across all projects. You can filter using query parameters: project_id to limit to a specific project, label to filter by label name, filter to use Todoist's native filter query language (e.g., 'today | overdue' shows today's and overdue tasks), assignee_id for shared projects, and priority for filtering by priority level (1=normal, 2=medium, 3=high, 4=urgent — note this is the reverse of ClickUp's numbering). Task objects in the response include: id, content (the task name), description (plain text notes), project_id, section_id, parent_id (for subtasks), order, labels (array of label names), priority (1-4), due (object with date, datetime, string, timezone), url (link to task in Todoist app), comment_count, creator_id, created_at, and assignee_id. The due object is particularly useful — it contains both the human-readable string (like 'every Monday') and the parsed date/datetime values. Use due.date for all-day tasks and due.datetime for tasks with specific times. Since these are outbound API calls, they work perfectly in Bolt's WebContainer preview without any deployment needed. You can build and fully test the entire read-only dashboard in Bolt before deploying.

Bolt.new Prompt

Create a Todoist task dashboard page in Next.js. Build a /api/todoist/tasks route that fetches active tasks from the Todoist API, with optional project_id and filter query parameters. Build a React component that displays tasks as cards showing: task content, project name (color-coded dot), priority badge (urgent=red, high=orange, medium=yellow, normal=gray), due date (red if overdue), and labels as chips. Add filter buttons for Today, Next 7 Days, and All Tasks.

Paste this in Bolt.new chat

app/api/todoist/tasks/route.ts
1// app/api/todoist/tasks/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4const TODOIST_API = 'https://api.todoist.com/rest/v2';
5const API_TOKEN = process.env.TODOIST_API_TOKEN;
6
7type TodoistTask = {
8 id: string;
9 content: string;
10 description: string;
11 project_id: string;
12 priority: number;
13 due: { date: string; datetime: string | null; string: string } | null;
14 labels: string[];
15 url: string;
16};
17
18export async function GET(request: NextRequest) {
19 const { searchParams } = new URL(request.url);
20 const projectId = searchParams.get('project_id');
21 const filter = searchParams.get('filter');
22
23 const params = new URLSearchParams();
24 if (projectId) params.set('project_id', projectId);
25 if (filter) params.set('filter', filter);
26
27 const url = `${TODOIST_API}/tasks${params.toString() ? '?' + params.toString() : ''}`;
28
29 const response = await fetch(url, {
30 headers: { Authorization: `Bearer ${API_TOKEN}` },
31 });
32
33 if (!response.ok) {
34 return NextResponse.json(
35 { error: `Todoist API error: ${response.status}` },
36 { status: response.status }
37 );
38 }
39
40 const tasks: TodoistTask[] = await response.json();
41
42 // Sort by priority descending, then by due date
43 const sorted = tasks.sort((a, b) => {
44 if (b.priority !== a.priority) return b.priority - a.priority;
45 if (!a.due && !b.due) return 0;
46 if (!a.due) return 1;
47 if (!b.due) return -1;
48 return new Date(a.due.date).getTime() - new Date(b.due.date).getTime();
49 });
50
51 return NextResponse.json({ tasks: sorted, count: sorted.length });
52}

Pro tip: Use Todoist's built-in filter language for powerful queries. Pass filter=today to get today's tasks, filter=overdue for overdue tasks, or filter=p1 for urgent priority tasks. See the Todoist filter documentation for the full syntax.

Expected result: The dashboard displays your Todoist tasks as color-coded cards sorted by priority, with filter buttons that switch between Today, Next 7 Days, and All Tasks views.

3

Create tasks with natural language due dates

Creating tasks in Todoist via the API is where the integration really shines. The POST /tasks endpoint accepts the content field (the task name, which can include Markdown formatting) and a rich set of optional fields. The most powerful optional field is due_string — Todoist's natural language parser will convert values like 'every Monday at 9am', 'next Friday', 'in 2 weeks', 'tomorrow', or 'June 15' into proper recurring or one-time due dates. This means you can build forms where users type natural language dates instead of using a date picker. Other useful fields for task creation: description (plain text or Markdown notes appended below the task), project_id (defaults to Inbox if omitted), section_id (places the task in a specific section within the project), labels (array of label name strings — labels are created automatically if they don't exist), priority (1=normal, 2=medium, 3=high, 4=urgent), parent_id (to create a subtask under an existing task), and assignee_id (for shared projects with multiple collaborators). To update an existing task, use POST /tasks/{taskId} with only the fields you want to change. To mark a task complete, send POST /tasks/{taskId}/close — the response is an empty 204 No Content. To reopen a completed task, use POST /tasks/{taskId}/reopen. All these mutation operations work fine as outbound HTTP requests from Bolt's WebContainer. You can create, update, and close tasks in development without deploying. Webhook notifications — where Todoist pushes events to your server — require a deployed URL, but you do not need webhooks to build a fully functional task creation interface.

Bolt.new Prompt

Add task creation to my Todoist integration. Create a /api/todoist/tasks/create POST route that creates a task with: content (required), description (optional), project_id (optional, defaults to env var), priority (1-4, defaults to 1), due_string (natural language date like 'tomorrow' or 'every Monday'), and labels (array of strings). Create a simple task creation form with all these fields. Add a /api/todoist/tasks/[id]/close route for marking tasks complete.

Paste this in Bolt.new chat

app/api/todoist/tasks/create/route.ts
1// app/api/todoist/tasks/create/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { randomUUID } from 'crypto';
4
5const TODOIST_API = 'https://api.todoist.com/rest/v2';
6const API_TOKEN = process.env.TODOIST_API_TOKEN;
7const DEFAULT_PROJECT_ID = process.env.TODOIST_PROJECT_ID;
8
9export async function POST(request: NextRequest) {
10 const {
11 content,
12 description,
13 project_id,
14 priority,
15 due_string,
16 labels,
17 parent_id,
18 } = await request.json();
19
20 if (!content || typeof content !== 'string' || content.trim() === '') {
21 return NextResponse.json({ error: 'content is required' }, { status: 400 });
22 }
23
24 const taskBody: Record<string, unknown> = {
25 content: content.trim(),
26 project_id: project_id ?? DEFAULT_PROJECT_ID,
27 priority: priority ?? 1,
28 };
29
30 if (description) taskBody.description = description;
31 if (due_string) taskBody.due_string = due_string;
32 if (labels && Array.isArray(labels)) taskBody.labels = labels;
33 if (parent_id) taskBody.parent_id = parent_id;
34
35 const response = await fetch(`${TODOIST_API}/tasks`, {
36 method: 'POST',
37 headers: {
38 Authorization: `Bearer ${API_TOKEN}`,
39 'Content-Type': 'application/json',
40 'X-Request-Id': randomUUID(), // Idempotency key for safe retries
41 },
42 body: JSON.stringify(taskBody),
43 });
44
45 if (!response.ok) {
46 const errorText = await response.text();
47 return NextResponse.json(
48 { error: errorText || `Todoist API error: ${response.status}` },
49 { status: response.status }
50 );
51 }
52
53 const task = await response.json();
54 return NextResponse.json({ id: task.id, content: task.content, url: task.url });
55}

Pro tip: Always include an X-Request-Id header with a unique UUID when creating tasks. Todoist uses this as an idempotency key — if your request fails and you retry, the same task will not be created twice.

Expected result: Submitting the task creation form creates a new task in Todoist with the specified due date, priority, and labels. Tasks with due_string values like 'every Monday' appear as recurring tasks in Todoist.

4

Deploy to Netlify or Vercel and set up Todoist webhooks

While task reading and creation work perfectly in Bolt's WebContainer preview, Todoist webhooks require a publicly accessible URL to deliver real-time notifications. Webhooks let Todoist push events to your server the instant something changes — a task is completed, a new task is added, a comment is posted — rather than your app polling for changes. This is essential for keeping a dashboard up-to-date in real time. To deploy your Bolt app to Netlify, click the Publish button in the top-right corner of Bolt, or go to Settings → Applications → Connect Netlify. After deployment, you will have a public URL like your-app.netlify.app. For Vercel, use Bolt's GitHub integration to push your code to a repository, then connect that repository in Vercel's dashboard. Once deployed, register a Todoist webhook through the Todoist developer console at developer.todoist.com/appconsole. You will need to create a Todoist app to get a client ID and secret for webhook signature verification. Set the webhook URL to your-app.netlify.app/api/todoist/webhook. Subscribe to the event types you care about: item:added, item:completed, item:updated, item:deleted. When Todoist sends a webhook, it includes an X-Todoist-Hmac-SHA256 header containing an HMAC-SHA256 signature of the request body. You must verify this signature in your webhook handler using your app's client secret to prevent processing fake events. The webhook payload includes the event_name, user_id, and event_data object containing the full task or project data. For development testing without deploying, use a tool like ngrok to expose your local Bolt dev server to the internet temporarily, then register that ngrok URL as your webhook endpoint.

Bolt.new Prompt

Add a Todoist webhook handler to my app at /api/todoist/webhook. The handler should: (1) verify the X-Todoist-Hmac-SHA256 signature using the TODOIST_CLIENT_SECRET environment variable, (2) parse the event_name and event_data from the payload, (3) handle item:completed and item:added events by logging them and returning 200. Add TODOIST_CLIENT_SECRET to the .env.local file.

Paste this in Bolt.new chat

app/api/todoist/webhook/route.ts
1// app/api/todoist/webhook/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { createHmac } from 'crypto';
4
5const CLIENT_SECRET = process.env.TODOIST_CLIENT_SECRET ?? '';
6
7function verifyTodoistSignature(body: string, signature: string): boolean {
8 const expected = createHmac('sha256', CLIENT_SECRET)
9 .update(body)
10 .digest('base64');
11 return expected === signature;
12}
13
14export async function POST(request: NextRequest) {
15 const signature = request.headers.get('x-todoist-hmac-sha256') ?? '';
16 const rawBody = await request.text();
17
18 if (!verifyTodoistSignature(rawBody, signature)) {
19 return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
20 }
21
22 const payload = JSON.parse(rawBody);
23 const { event_name, event_data } = payload;
24
25 console.log(`Todoist webhook: ${event_name}`, event_data);
26
27 switch (event_name) {
28 case 'item:added':
29 // Handle new task creation
30 break;
31 case 'item:completed':
32 // Handle task completion
33 break;
34 case 'item:updated':
35 // Handle task update
36 break;
37 default:
38 break;
39 }
40
41 return NextResponse.json({ received: true });
42}

Pro tip: Todoist webhooks are only sent over HTTPS and must receive a 200 response within 7 seconds. If your handler takes longer, Todoist may retry the webhook. Keep webhook processing fast by acknowledging immediately and doing any heavy work asynchronously.

Expected result: After deploying and registering the webhook URL in the Todoist developer console, your app receives real-time notifications when tasks are added or completed, and the webhook handler logs the event data to your server logs.

Common use cases

Personal productivity dashboard

Build a consolidated view of all your Todoist tasks due today, sorted by project and priority, with overdue tasks highlighted in red. The dashboard refreshes every 30 seconds and shows a completion progress bar for the current day.

Bolt.new Prompt

Create a Todoist daily dashboard in Next.js. Fetch all tasks due today or overdue using the Todoist REST API v2. Group them by project name and sort by priority (priority 4 = urgent at top). Show overdue tasks with a red badge. Add a progress bar showing completed vs total tasks for today. Auto-refresh every 30 seconds.

Copy this prompt to try it in Bolt.new

Automated task creation from a form

When a user submits a contact form, bug report, or feature request in your app, automatically create a Todoist task in the appropriate project with the form data as the task content, set the priority based on urgency, and use Todoist's natural language parser to set a due date like 'in 2 business days'.

Bolt.new Prompt

Add a Todoist integration to my feedback form. When a user submits a bug report with a title, description, and severity (low/medium/high/critical), create a Todoist task in project ID [PROJECT_ID] with the title as the task content, add the description as a comment, and set priority: critical=4, high=3, medium=2, low=1. Set due_string to 'tomorrow' for critical and 'in 3 days' for others.

Copy this prompt to try it in Bolt.new

Recurring task template generator

Build an interface where users select a workflow template (like 'Weekly Review' or 'New Client Onboarding') and your app creates a batch of recurring tasks in Todoist with properly structured due dates, labels, and project assignments — all in one click.

Bolt.new Prompt

Build a Todoist task template page. Create a /api/todoist/batch-create route that accepts an array of task objects and creates them all in Todoist. Build a UI with a dropdown of templates (Weekly Review, Project Kickoff, Content Calendar). When a template is selected, show the tasks it will create and a 'Create All Tasks' button. Each task should have a name, project ID, priority, and due_string.

Copy this prompt to try it in Bolt.new

Troubleshooting

All API calls return 401 Unauthorized

Cause: The Authorization header is incorrectly formatted, the API token is invalid, or the .env variable is not being read. Todoist requires the 'Bearer' prefix in the Authorization header — unlike some other APIs that use raw tokens.

Solution: Verify that the Authorization header in your API route reads exactly: Authorization: `Bearer ${process.env.TODOIST_API_TOKEN}`. Confirm the token value in your .env.local matches the current token in Todoist Settings → Integrations → Developer. Regenerate the token if it was recently changed.

typescript
1// Correct format — with Bearer prefix:
2headers: { Authorization: `Bearer ${process.env.TODOIST_API_TOKEN}` }
3
4// Wrong — missing Bearer prefix:
5headers: { Authorization: process.env.TODOIST_API_TOKEN }

Task creation returns 400 Bad Request with no helpful error message

Cause: The request body is missing the required content field, or the content value is an empty string. Todoist also returns 400 if project_id points to a project that does not exist or that your token cannot access.

Solution: Ensure the request body includes a non-empty content field as a string. Verify your TODOIST_PROJECT_ID environment variable contains a valid project ID. Call GET /api/todoist/projects to get a list of valid project IDs for your account.

API calls work in Bolt preview but Todoist webhooks are never received

Cause: Todoist webhooks require a publicly accessible HTTPS URL. Bolt's WebContainer development environment does not have a public URL — it runs entirely in the browser and cannot receive inbound HTTP requests from external services.

Solution: Deploy your app to Netlify or Vercel first using Bolt's publish workflow. After deployment, register your production URL (e.g., your-app.netlify.app/api/todoist/webhook) in the Todoist developer console. For local development testing, use ngrok to temporarily expose your local server to the internet.

Natural language due dates are not being parsed correctly — tasks have no due date or the wrong date

Cause: The due_string value may be in a format Todoist's NLP parser does not recognize, or the timezone is not set correctly. Todoist parses dates relative to the user's timezone configured in their account settings.

Solution: Use the due_lang parameter to explicitly specify the language (e.g., 'en' for English). Test your due_string values in the Todoist app first by typing them directly into the task creation field. Common reliable formats: 'tomorrow', 'next Monday', 'June 15', 'every week on Monday'. Avoid ambiguous formats like '3/15' which could be March or May depending on locale.

typescript
1// Include explicit language for reliable parsing:
2const taskBody = {
3 content: 'Review analytics report',
4 due_string: 'every Monday at 9am',
5 due_lang: 'en',
6};

Best practices

  • Never expose your Todoist API token in client-side code — always access it via process.env.TODOIST_API_TOKEN in server-side Next.js API routes only, never with a NEXT_PUBLIC_ prefix.
  • Include X-Request-Id with a unique UUID on every POST request to enable idempotent task creation — this prevents duplicate tasks if a network error causes your app to retry the request.
  • Cache project and label data on the server side and refresh it every few minutes rather than fetching on every request — projects and labels rarely change and count against your API rate limits.
  • Use Todoist's filter query language (the filter parameter) instead of fetching all tasks and filtering client-side — passing filter=today is far more efficient than downloading your entire task list.
  • Handle the 429 Too Many Requests response by checking the Retry-After header and waiting that many seconds before retrying — Todoist enforces rate limits based on your plan tier.
  • For real-time task updates, prefer Todoist webhooks over polling once you deploy — webhooks push events instantly rather than waiting for your next poll interval, reducing API usage and latency.
  • Validate and sanitize the content field before sending to Todoist — very long strings or special characters can cause unexpected behavior; trim whitespace and enforce a reasonable character limit in your form validation.
  • Use Todoist's sync API (rather than the REST API) for bulk operations like importing many tasks at once — the sync API processes arrays of commands in a single request, much more efficient than individual POST calls.

Alternatives

Frequently asked questions

How do I connect Bolt.new to Todoist?

Get your personal API token from Todoist Settings → Integrations → Developer and add it as TODOIST_API_TOKEN in your Bolt project's .env.local file. Then use Bolt chat to generate Next.js API routes that call the Todoist REST API v2 with Bearer token authentication. All outbound API calls — reading and creating tasks — work in Bolt's development preview without deploying.

Does Bolt.new support Todoist natural language due dates?

Yes. When creating tasks via the Todoist API, include the due_string field with any natural language value like 'tomorrow at 9am', 'every Monday', or 'next Friday'. Todoist's server-side NLP parser converts these into proper due dates automatically. You can also include due_lang: 'en' to ensure English parsing regardless of your account's default language setting.

Can I test the Todoist integration in Bolt's preview before deploying?

Yes, for reading and writing tasks. Outbound API calls to Todoist work perfectly in Bolt's WebContainer preview — you can fetch tasks, create tasks, update priorities, add comments, and close tasks all without deploying. The only limitation is incoming webhooks: if you want Todoist to notify your app in real-time when tasks change, you need to deploy to a public URL first.

How do I find my Todoist project IDs?

Open any project in Todoist and look at the URL in your browser — the project ID is the long number at the end, for example app.todoist.com/app/project/2349123456. You can also call GET https://api.todoist.com/rest/v2/projects with your API token to retrieve all project IDs, names, and colors programmatically.

What is Todoist's API rate limit?

Todoist limits API requests based on your account plan. Free accounts can make up to 1,000 requests per 15 minutes. If you exceed the limit, the API returns a 429 status with a Retry-After header indicating how many seconds to wait. For most dashboard use cases making a few dozen calls on page load, this limit is rarely reached.

How do I deploy a Bolt.new app with Todoist to Netlify?

In Bolt, go to Settings → Applications → Connect Netlify and authorize with OAuth. Click Publish in the top-right corner to deploy your app to a .netlify.app URL. After deployment, add your TODOIST_API_TOKEN and any other environment variables in the Netlify dashboard under Site Settings → Environment Variables. Then redeploy from Netlify or trigger a new Bolt publish to apply the variables.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.