Integrating Todoist with Lovable requires Edge Functions to proxy the Todoist REST API v2. Store your Todoist API token in Cloud Secrets, create an Edge Function that reads and creates tasks, projects, and labels, and build personal productivity dashboards in Lovable's React frontend. Todoist's API supports natural language due dates, priority levels, labels, and project hierarchies — making it ideal for building productivity tools that sync with users' existing task management system.
Why integrate Todoist with Lovable?
Todoist is one of the world's most popular personal productivity apps, used by over 40 million people across iOS, Android, web, and desktop. Its simple but powerful model — projects contain tasks, tasks have due dates, priorities (P1-P4), labels, and comments — combined with natural language date parsing ('tomorrow', 'every Monday', 'in 3 days') makes it the go-to app for individuals and small teams who want frictionless task capture. Many founders and operators manage their daily work in Todoist while building products with other tools.
Integrating Todoist with Lovable creates compelling use cases for personal and team productivity tools: custom morning dashboards that show Todoist's today tasks alongside calendar events and key metrics, intake forms in other apps that create Todoist tasks when specific events occur (a new lead submits a form, a code review is requested, a customer reaches a key milestone), team accountability tools that aggregate Todoist task completions across team members, and focus mode interfaces that surface only the highest-priority tasks from Todoist in a distraction-free view.
Todoist's REST API v2 is clean and well-documented. Authentication uses a Bearer token in the Authorization header, projects and tasks use string IDs, and task creation supports a due_string parameter that accepts natural language — so you can create tasks with due_string='tomorrow at 9am' and Todoist's server-side natural language parser handles the date conversion. This is a significant convenience advantage over APIs that require explicit ISO datetime formats. This tutorial covers all the essential operations for building practical productivity integrations.
Integration method
Todoist has no native Lovable connector. All Todoist API calls use the REST API v2 at api.todoist.com/rest/v2, proxied through Supabase Edge Functions. API tokens are stored in Cloud Secrets and sent as Bearer Authorization headers. Edge Functions create, read, update, and complete tasks with labels, priorities, and natural language due dates — returning task data to your Lovable frontend for custom productivity dashboards.
Prerequisites
- A Lovable project with Cloud enabled
- A Todoist account (free or Pro) with at least one project and some tasks
- Your Todoist API token — find it at todoist.com → Settings → Integrations → Developer → API token (copy the long alphanumeric string)
- Basic understanding of Todoist's data model: projects contain tasks, tasks can have sections, labels, due dates, and priorities P1-P4 (1 is highest)
Step-by-step guide
Find your Todoist API token and store it in Cloud Secrets
Find your Todoist API token and store it in Cloud Secrets
Todoist provides a single API token per account rather than requiring OAuth app registration for personal use. This makes it easy to get started — no app creation, no redirect flow, just find your token and store it. To find your API token, log into Todoist in a browser, click your avatar in the top-right corner, select 'Settings', then click 'Integrations' in the left sidebar. Scroll to the 'Developer' section and you'll see your API token — it's a 40-character alphanumeric string. Click 'Copy to clipboard'. In your Lovable project, click the '+' button to open panels and select the Cloud tab. Navigate to the Secrets section and click 'Add secret'. Enter TODOIST_API_TOKEN as the name and paste your token as the value. Click Save. This token provides full read and write access to your Todoist account — it doesn't expire and doesn't rotate unless you manually regenerate it from the same Settings → Integrations → Developer page. Lovable's security infrastructure ensures this token stays encrypted and is only accessible from your Edge Functions, never from any code running in the browser.
Pro tip: For apps where multiple users connect their own Todoist accounts, you'll need to implement Todoist's OAuth flow instead of personal tokens. Register an app at todoist.com/oauth/apps to get OAuth credentials for the authorization flow.
Expected result: TODOIST_API_TOKEN appears in Cloud Secrets with a masked value. The secret is ready to be accessed by Edge Functions.
Create the Todoist proxy Edge Function
Create the Todoist proxy Edge Function
The Todoist REST API v2 is one of the most approachable project management APIs available. All endpoints use standard HTTP verbs at api.todoist.com/rest/v2/, with Bearer token authentication and JSON request/response bodies. The most important endpoints are /projects (list all projects), /tasks (list and create tasks with flexible filtering), /labels (list labels), and /tasks/{taskId}/close (mark a task complete). The natural language due date support in Todoist's API is a standout feature: you can pass due_string='every Monday at 10am' or 'next Friday' or 'in 3 days' to the task creation endpoint and Todoist's server handles the parsing — no date manipulation code required. The Edge Function below handles all common operations through an action-based router, including the sync endpoint which provides completed task history for activity tracking.
Create a Supabase Edge Function at supabase/functions/todoist-proxy/index.ts. Read TODOIST_API_TOKEN from Deno.env.get(). Handle CORS. Accept POST with action and params. Use Bearer token Authorization header for all requests. Base URL https://api.todoist.com/rest/v2. Implement: 'list_projects' (GET /projects), 'get_project' (GET /projects/{params.projectId}), 'list_tasks' with optional params.projectId, params.filter, params.labelId (GET /tasks with query params), 'get_task' (GET /tasks/{params.taskId}), 'create_task' (POST /tasks with content, description, project_id, due_string, priority, labels array from params), 'update_task' (POST /tasks/{params.taskId} with update fields), 'complete_task' (POST /tasks/{params.taskId}/close), 'list_labels' (GET /labels), 'get_completed' (GET https://api.todoist.com/sync/v9/items/completed/get_all with limit and since params for activity history). Return all responses as JSON.
Paste this in Lovable chat
1// supabase/functions/todoist-proxy/index.ts2import { serve } from "https://deno.land/std@0.168.0/http/server.ts";34const REST_BASE = "https://api.todoist.com/rest/v2";5const SYNC_BASE = "https://api.todoist.com/sync/v9";6const corsHeaders = {7 "Access-Control-Allow-Origin": "*",8 "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",9};1011serve(async (req) => {12 if (req.method === "OPTIONS") {13 return new Response("ok", { headers: corsHeaders });14 }15 const token = Deno.env.get("TODOIST_API_TOKEN");16 if (!token) {17 return new Response(JSON.stringify({ error: "TODOIST_API_TOKEN not set" }), {18 status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" },19 });20 }21 const auth = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };22 const { action, params = {} } = await req.json();23 let resp;2425 switch (action) {26 case "list_projects":27 resp = await fetch(`${REST_BASE}/projects`, { headers: auth });28 break;29 case "get_project":30 resp = await fetch(`${REST_BASE}/projects/${params.projectId}`, { headers: auth });31 break;32 case "list_tasks": {33 const q = new URLSearchParams();34 if (params.projectId) q.set("project_id", params.projectId);35 if (params.filter) q.set("filter", params.filter);36 if (params.labelId) q.set("label_id", params.labelId);37 const qs = q.toString() ? `?${q}` : "";38 resp = await fetch(`${REST_BASE}/tasks${qs}`, { headers: auth });39 break;40 }41 case "get_task":42 resp = await fetch(`${REST_BASE}/tasks/${params.taskId}`, { headers: auth });43 break;44 case "create_task":45 resp = await fetch(`${REST_BASE}/tasks`, {46 method: "POST", headers: auth,47 body: JSON.stringify({48 content: params.content,49 ...(params.description && { description: params.description }),50 ...(params.projectId && { project_id: params.projectId }),51 ...(params.dueString && { due_string: params.dueString }),52 ...(params.priority && { priority: params.priority }),53 ...(params.labels && { labels: params.labels }),54 }),55 });56 break;57 case "update_task":58 resp = await fetch(`${REST_BASE}/tasks/${params.taskId}`, {59 method: "POST", headers: auth,60 body: JSON.stringify(params.updates),61 });62 break;63 case "complete_task":64 resp = await fetch(`${REST_BASE}/tasks/${params.taskId}/close`, {65 method: "POST", headers: auth,66 });67 break;68 case "list_labels":69 resp = await fetch(`${REST_BASE}/labels`, { headers: auth });70 break;71 case "get_completed": {72 const q = new URLSearchParams({ limit: String(params.limit || 30) });73 if (params.since) q.set("since", params.since);74 resp = await fetch(`${SYNC_BASE}/items/completed/get_all?${q}`, { headers: auth });75 break;76 }77 default:78 return new Response(JSON.stringify({ error: `Unknown action: ${action}` }), {79 status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" },80 });81 }8283 const data = await resp.json();84 return new Response(JSON.stringify(data), {85 status: resp.status, headers: { ...corsHeaders, "Content-Type": "application/json" },86 });87});Pro tip: Todoist's filter parameter supports powerful expressions: 'today | overdue' fetches today plus overdue tasks, 'p1 & #Work' fetches P1 tasks in the Work project, 'assigned to: me & 7 days' fetches tasks assigned to you due within 7 days. Use these for targeted dashboard views.
Expected result: The Edge Function deploys. Calling action 'list_projects' returns your Todoist projects. Calling 'list_tasks' with filter='today | overdue' returns today's and overdue tasks.
Build a productivity dashboard showing today's tasks
Build a productivity dashboard showing today's tasks
The most immediately useful Todoist-Lovable integration is a personal dashboard that surfaces the right tasks at the right time. Todoist's filter API is particularly powerful here — the filter='today | overdue' query returns exactly the tasks you need to see for your daily review, sorted by priority. Combining this with project counts and label summaries gives a complete daily overview. The following Lovable prompt generates a task dashboard with three sections: a task list with completion checkboxes, a project summary showing open task counts per project, and a quick-add form that uses natural language for due dates. The component uses React Query for data fetching with automatic background refetching every 5 minutes, keeping the dashboard current without manual refreshes.
Create a TodoistDashboard component. On mount, call these in parallel via my todoist-proxy Edge Function: action 'list_tasks' with filter='today | overdue' (today's view), action 'list_projects', and action 'list_labels'. Display three sections: 1) Today's Tasks — show tasks sorted by priority (P1 first) with a checkbox to call complete_task, the project name as a badge, and due time if set. Completed tasks show with strikethrough and fade out after 1 second. 2) Project Summary — show each project with its open task count (count tasks where project_id matches). 3) Quick Add — a text input where typing a task name and pressing Enter calls create_task with the content and an optional due_string parsed from the text (extract date phrases like 'tomorrow', 'today', 'Friday').
Paste this in Lovable chat
Pro tip: Todoist priority numbers are inverted from what you might expect: priority=4 is P1 (highest/red), priority=3 is P2, priority=2 is P3, priority=1 is P4 (normal). Map these when displaying priority labels to users.
Expected result: The dashboard shows today's tasks sorted by priority with working completion checkboxes, a project task-count summary, and a quick-add input. Completing a task updates Todoist immediately and the task animates out of the list.
Add label-based task filtering and productivity tracking
Add label-based task filtering and productivity tracking
Todoist labels are powerful organizational tools — users can tag tasks with labels like 'urgent', 'waiting', 'deep-work', or 'quick-win' to create cross-project filtered views. Your integration can expose these label-based views as focused work modes: a 'Deep Work' view showing only tasks tagged with a focus label, a 'Waiting' view showing tasks that are blocked pending someone else's action, or a 'Quick Wins' view for 5-minute tasks. For productivity tracking, the Todoist Sync API (v9) provides access to completed task history — the get_completed action in your Edge Function fetches tasks completed in a date range. This enables streak tracking, daily completion counts, and week-over-week productivity comparisons. RapidDev's team can help build more sophisticated productivity analytics, such as comparing estimated vs actual task completion rates using Todoist's time-tracking integrations.
Add a 'Focus Mode' feature to the TodoistDashboard. Add a label selector dropdown (populated from action 'list_labels') that filters the task list to show only tasks with the selected label. When 'Focus Mode' is active, hide all other UI elements and show only the filtered tasks in a large, distraction-free view with a prominent completion checkbox for each task. Also add a 'This Week' stats section below the main task list: call action 'get_completed' with since=beginning of current week. Display: tasks completed today, tasks completed this week, and a 7-day completion bar chart showing daily completion counts.
Paste this in Lovable chat
Pro tip: The Todoist Sync API's get_completed endpoint returns items with a completed_date field in UTC. Convert to local time for display using toLocaleDateString() with the user's timezone. Group by local date to get accurate daily counts.
Expected result: The Focus Mode button appears in the dashboard. Selecting a label filters the task list. The This Week section shows completion counts and a bar chart with daily breakdowns for the past 7 days.
Common use cases
Daily morning dashboard combining Todoist tasks with other data
A founder uses Todoist for personal task management and wants a single morning dashboard that combines today's Todoist tasks with key business metrics from Supabase. The Edge Function fetches tasks due today or overdue, and the frontend displays them alongside a revenue snapshot, pending support tickets, and calendar events — a personalized command center that replaces opening multiple apps.
Build a morning dashboard at /dashboard. In one column, fetch today's Todoist tasks (due_date = today) and overdue tasks sorted by priority (P1 first). Show each task with a checkbox to complete it, its project name as a colored badge, due time if set, and priority indicator (red dot for P1, orange for P2). In a second column, show today's metrics from Supabase: total new users this week, open support tickets, and revenue today from the transactions table. Add a Quick Add input at the top to create new Todoist tasks with natural language due dates.
Copy this prompt to try it in Lovable
Automated task creation from app events
A SaaS app wants to automatically create Todoist tasks for the founder when key events happen: a new trial user signs up (create a task to follow up in 3 days), a payment fails (create an urgent task with the customer name), or a support ticket has been unresponsive for 24 hours (create a task with P1 priority). Edge Functions trigger on Supabase database events and use the Todoist API to create contextual tasks with relevant links and details.
Set up automation: when a new row is inserted in the trials table, create a Todoist task 'Follow up with {user_email} about trial' with due_string='in 3 days', priority=2, label='follow-up', and the user's signup page URL in the task description. When a row is inserted in failed_payments, create a task 'Payment recovery: {customer_name}' with priority=1 (P1), due_string='today', label='urgent'. Store the created task ID back in the source table. Use my Todoist Edge Function for all task creation calls.
Copy this prompt to try it in Lovable
Team task completion tracking across multiple Todoist accounts
A small team with individual Todoist accounts wants a shared accountability dashboard showing how many tasks each person completed today and this week. Each team member authenticates with their own Todoist API token (stored per-user in Supabase), and the Edge Function fetches completed tasks for the date range. The frontend aggregates completions into a leaderboard with streaks and weekly totals.
Build a team accountability page at /team-accountability. Each team member has their Todoist API token stored in the user_settings table in Supabase (column: todoist_token). For each team member, call my Todoist Edge Function with their token to fetch tasks completed today and this week (using the activity log endpoint). Display a leaderboard table: Name, Tasks Today, Tasks This Week, Current Streak (days with at least 1 completion). Update every 30 minutes. Add celebratory confetti animation when someone completes their daily goal (5+ tasks).
Copy this prompt to try it in Lovable
Troubleshooting
Task creation succeeds (200 response) but the task doesn't appear in Todoist
Cause: The task was created in a project the user isn't currently viewing, or the due date filter on the Todoist app is hiding it. If no project_id is specified, tasks are created in the Inbox project.
Solution: After creating a task, check the Inbox project in Todoist to confirm it was created successfully. If you need the task in a specific project, always include project_id in the create_task call. Verify the project ID by calling action 'list_projects' and checking the IDs of your projects.
Natural language due dates are being ignored or creating tasks with no due date
Cause: The due_string parameter might be spelled incorrectly (it's due_string, not dueString or due_date), or the natural language input contains terms Todoist's parser doesn't recognize.
Solution: Verify the parameter name is due_string (with underscore) in the JSON body sent to the Todoist API. Test due_string values that Todoist is known to parse: 'today', 'tomorrow', 'next Monday', 'every Friday', 'in 3 days'. Unusual phrasing or locale-specific terms may not parse correctly — use standard English date expressions for reliability.
1// Correct parameter name2body: JSON.stringify({3 content: "Task title",4 due_string: "tomorrow at 9am", // NOT dueString or due_date5 priority: 4 // 4 = P1 (highest)6})Completed tasks still appear in the list after calling complete_task
Cause: The frontend is not removing the completed task from local state after the API call succeeds. The complete_task endpoint returns a 204 No Content response — if your code checks for a JSON response body, it may interpret the empty response as an error.
Solution: Handle the 204 No Content response from complete_task: check resp.status === 204 for success rather than trying to parse a JSON body. After a successful completion, filter the task out of local state or refetch the task list. The task remains in Todoist's completed history but is removed from the active tasks list.
1case "complete_task":2 resp = await fetch(`${REST_BASE}/tasks/${params.taskId}/close`, { method: "POST", headers: auth });3 // Returns 204 No Content on success — no JSON body to parse4 return new Response(JSON.stringify({ success: resp.status === 204 }), {5 status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" },6 });The filter parameter returns unexpected tasks or an empty array
Cause: Todoist's filter syntax uses specific operators and field names that differ from plain English. Incorrect filter strings return empty results or all tasks rather than the expected subset.
Solution: Test filter strings in Todoist's native app first (in the search/filter bar) before using them in your API calls. Common working filters: 'today' (due today), 'overdue' (past due), 'today | overdue' (today plus overdue), 'p1' (priority 1), '#ProjectName' (specific project). The filter is case-sensitive for project names — use exact project names as they appear in Todoist.
Best practices
- Use Todoist's powerful filter expressions (today, overdue, p1, #Project, @label) in list_tasks calls rather than fetching all tasks and filtering client-side — server-side filtering is faster and reduces data transfer.
- Cache project and label lists in React state after the first fetch, since they change infrequently — only refresh them when the user explicitly triggers a reload or when the app mounts.
- Handle the 204 No Content response from the complete_task endpoint gracefully — it indicates success, not an error, but the response body is empty so JSON parsing will fail.
- Remember that Todoist priority numbers are inverted: priority=4 means P1 (highest), priority=1 means P4 (normal). Map these to display labels before showing to users to avoid confusion.
- For natural language due dates, validate user input by supporting a curated set of common phrases ('today', 'tomorrow', 'this week', 'next week') rather than accepting arbitrary text, which may fail to parse.
- Use X-Request-Id headers in Edge Function requests to make it easy to trace specific API calls in Cloud → Logs when debugging unexpected behavior.
- Store task IDs in Supabase when creating tasks from app events, so you can update or complete the Todoist task later when the corresponding app event resolves.
Alternatives
Trello suits teams who prefer a visual kanban board with cards over Todoist's list-based personal task management focused on individual productivity.
ClickUp provides deeper team collaboration features, time tracking, and custom views for teams that need more than Todoist's personal-to-small-team scope.
Asana is better suited for multi-team project coordination with custom workflows and portfolios compared to Todoist's strength in personal and small-team task management.
Frequently asked questions
Does Todoist have a native connector in Lovable?
No, Todoist is not one of Lovable's 17 shared connectors. Integration uses Edge Functions that proxy the Todoist REST API v2. Your API token is stored in Cloud Secrets and all requests route through the server-side function — the token is never accessible from browser code.
Can I use Todoist's natural language date parsing through the API?
Yes — the due_string parameter in the create_task and update_task endpoints accepts natural language date expressions like 'tomorrow at 9am', 'every Monday', 'next Friday', and 'in 3 days'. Todoist's server-side parser converts these to proper dates based on the user's timezone settings. This is one of Todoist's most developer-friendly features and saves significant date-handling code.
How do I track task completions for productivity analytics?
Use the Todoist Sync API v9's items/completed/get_all endpoint (the 'get_completed' action in the Edge Function). This endpoint accepts a 'since' ISO datetime parameter and returns completed tasks in that date range with completion timestamps. Note that access to completed tasks requires a Todoist Pro account — free accounts cannot query completion history through the API.
What Todoist plan is required for API access?
The REST API v2 for basic task and project operations is available on all Todoist plans, including the free tier. However, some endpoints — particularly completed task history and certain sync features — require a Pro subscription ($4/month). For most productivity dashboard use cases, the free plan's API access is sufficient.
Can I create recurring tasks through the Todoist API?
Yes — use natural language in the due_string parameter to create recurring tasks: 'every day', 'every Monday', 'every month on the 1st', 'every weekday'. Todoist's parser recognizes standard recurrence expressions and sets up the task with the correct repeat schedule. The task will automatically regenerate after each completion, just as if it were created through the Todoist app.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation