A reference guide for building AI agents: every method, how to authenticate, and the permissions each one needs.
The Todoist API is how an app or AI agent works with a Todoist account: adding a task, completing one, organizing tasks into projects and sections, and tagging them with labels. Access is granted through a token, either a personal token or an OAuth grant whose scopes set whether a call can read, write, add tasks, or delete. A change in an account can be pushed to a registered endpoint, so an integration learns about activity without polling.
How an app or AI agent connects to Todoist determines what it can reach. There is a route for making calls, a route for receiving events, and a hosted server that exposes Todoist tools to agents, and each is governed by the token behind it and the permissions that token carries.
The unified API takes JSON request bodies, returns JSON, and serves both the resource-style endpoints and the batched sync endpoint, at https://api.todoist.com/api/v1. A call authenticates with a Bearer token, either a personal API token or an OAuth access token. The /sync endpoint accepts up to 100 commands in one request for efficient bulk reads and writes.
Todoist POSTs a JSON payload to an HTTPS endpoint registered on an OAuth app when a subscribed event happens, like a task being added or completed. The payload names the event in an event_name field and carries the changed object in event_data. The receiver verifies the X-Todoist-Hmac-SHA256 header, an HMAC of the raw body computed with the app's client secret, to confirm the request came from Todoist.
Doist, the maker of Todoist, publishes an official hosted Model Context Protocol server at https://ai.todoist.net/mcp that exposes Todoist tools to AI agents and LLM clients. It authenticates through a browser-based OAuth flow on first use, and covers tasks, projects, sections, comments, and labels. It is early-stage, with more tools planned.
OAuth uses the standard authorization-code flow, where a person grants an app a chosen set of scopes and the app exchanges the resulting code for an access token. The scopes set what the token can do: data:read for read-only access, data:read_write for full read and write, task:add to only add tasks, data:delete to delete tasks, labels, and filters, and project:delete to delete projects. This is the route for an integration acting on behalf of other people.
Each Todoist user has a personal API token, found in the app's integration settings, that grants full access to that one account. It is sent as a Bearer token the same way an OAuth token is. It carries no scope limits, so it reaches everything the account can, which makes it best for personal scripts rather than shared integrations.
The Todoist API is split into areas an agent can act on, like tasks, projects, sections, comments, and labels. Each area has its own methods, and a token that can write reaches every project and task the person who granted it can see.
Methods for getting, listing, creating, updating, completing, and deleting tasks.
Methods for working with projects, the top-level containers for tasks.
Methods for working with sections, the groupings of tasks inside a project.
Methods for working with comments attached to tasks and projects.
Methods for working with labels, the reusable tags applied to tasks.
Methods for listing the people who share a project.
Filter by method, access, or permission, or search any path. Select a row for version detail, rate limits, the related webhook event, and the source.
| Method | Endpoint | What it does | Access | Permission | Version | |
|---|---|---|---|---|---|---|
TasksMethods for getting, listing, creating, updating, completing, and deleting tasks.10 | ||||||
| GET | /api/v1/tasks | List active (incomplete) tasks, filtered by project, section, label, or other criteria. | read | data:read | Current | |
Read-only. Returns only active tasks; completed tasks come from the completed endpoint. Acts ontask Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /api/v1/tasks/{id} | Retrieve a single task by its id. | read | data:read | Current | |
Read-only. Acts ontask Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/tasks | Create a new task, with content, due date, priority, project, and labels. | write | task:add | Current | |
Granted by task:add on its own, or by data:read_write which includes it. Acts ontask Permission (capability) task:addVersionAvailable since the API’s base version Webhook event item:addedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/tasks/quick_add | Create a task from a single natural-language string, parsing the due date, project, labels, and priority from the text. | write | task:add | Current | |
Parses input the same way the Todoist quick-add box does. Needs task:add or data:read_write. Acts ontask Permission (capability) task:addVersionAvailable since the API’s base version Webhook event item:addedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/tasks/{id} | Update a task's content, due date, priority, labels, or other fields. | write | data:read_write | Current | |
Needs data:read_write. Acts ontask Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event item:updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/tasks/{id}/move | Move a task to a different project, section, or parent task. | write | data:read_write | Current | |
Needs data:read_write. Acts ontask Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event item:updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/tasks/{id}/close | Complete a task, the same action as ticking it off in the app. | write | data:read_write | Current | |
Needs data:read_write. Completing a recurring task advances it to the next occurrence instead. Acts ontask Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event item:completedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/tasks/{id}/reopen | Reopen a completed task, marking it active again. | write | data:read_write | Current | |
Needs data:read_write. Acts ontask Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event item:uncompletedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /api/v1/tasks/completed | Retrieve completed tasks within a date range, for history and reporting. | read | data:read | Current | |
Read-only. Returns tasks already completed, which the active list endpoint excludes. Acts ontask Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /api/v1/tasks/{id} | Permanently delete a task and any of its sub-tasks. | write | data:delete | Current | |
Irreversible, and removes sub-tasks too. Needs data:delete. Acts ontask Permission (capability) data:deleteVersionAvailable since the API’s base version Webhook event item:deletedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
ProjectsMethods for working with projects, the top-level containers for tasks.5 | ||||||
| GET | /api/v1/projects | List all projects in the account. | read | data:read | Current | |
Read-only. Acts onproject Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /api/v1/projects/{id} | Retrieve a single project by its id. | read | data:read | Current | |
Read-only. Acts onproject Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/projects | Create a new project, optionally nested under a parent project. | write | data:read_write | Current | |
Needs data:read_write. Acts onproject Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event project:addedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/projects/{id} | Update a project's name, colour, favourite status, or view style. | write | data:read_write | Current | |
Needs data:read_write. Acts onproject Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event project:updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /api/v1/projects/{id} | Permanently delete a project and all the tasks, sections, and comments inside it. | write | project:delete | Current | |
Irreversible, and removes every task and section in the project. Needs the dedicated project:delete scope, separate from data:delete. Acts onproject Permission (capability) project:deleteVersionAvailable since the API’s base version Webhook event project:deletedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
SectionsMethods for working with sections, the groupings of tasks inside a project.5 | ||||||
| GET | /api/v1/sections | List sections, optionally limited to a single project. | read | data:read | Current | |
Read-only. Acts onsection Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /api/v1/sections/{id} | Retrieve a single section by its id. | read | data:read | Current | |
Read-only. Acts onsection Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/sections | Create a new section within a project. | write | data:read_write | Current | |
Needs data:read_write. Acts onsection Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/sections/{id} | Update a section's name. | write | data:read_write | Current | |
Needs data:read_write. Acts onsection Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /api/v1/sections/{id} | Delete a section and move its tasks to the project root. | write | data:delete | Current | |
Needs data:delete. Acts onsection Permission (capability) data:deleteVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
CommentsMethods for working with comments attached to tasks and projects.5 | ||||||
| GET | /api/v1/comments | List comments on a given task or project. | read | data:read | Current | |
Read-only. Requires a task_id or project_id. Acts oncomment Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /api/v1/comments/{id} | Retrieve a single comment by its id. | read | data:read | Current | |
Read-only. Acts oncomment Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/comments | Add a comment to a task or project, optionally with an attachment. | write | data:read_write | Current | |
Needs data:read_write. Acts oncomment Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event note:addedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/comments/{id} | Update the content of an existing comment. | write | data:read_write | Current | |
Needs data:read_write. Acts oncomment Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event note:updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /api/v1/comments/{id} | Delete a comment. | write | data:delete | Current | |
Needs data:delete. Acts oncomment Permission (capability) data:deleteVersionAvailable since the API’s base version Webhook event note:deletedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
LabelsMethods for working with labels, the reusable tags applied to tasks.5 | ||||||
| GET | /api/v1/labels | List all personal labels in the account. | read | data:read | Current | |
Read-only. Acts onlabel Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /api/v1/labels/{id} | Retrieve a single label by its id. | read | data:read | Current | |
Read-only. Acts onlabel Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/labels | Create a new personal label. | write | data:read_write | Current | |
Needs data:read_write. Acts onlabel Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event label:addedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /api/v1/labels/{id} | Update a label's name, colour, or order. | write | data:read_write | Current | |
Renaming a label updates it on every task that carries it. Needs data:read_write. Acts onlabel Permission (capability) data:read_writeVersionAvailable since the API’s base version Webhook event label:updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /api/v1/labels/{id} | Delete a personal label, removing it from every task that carries it. | write | data:delete | Current | |
Removes the label from all tasks. Needs data:delete. Acts onlabel Permission (capability) data:deleteVersionAvailable since the API’s base version Webhook event label:deletedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
CollaboratorsMethods for listing the people who share a project.1 | ||||||
| GET | /api/v1/projects/{id}/collaborators | List the people who share a given project. | read | data:read | Current | |
Read-only. Returns each collaborator's name and email address. Acts oncollaborator Permission (capability) data:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Todoist can notify an app when something happens in an account, like a task being added or completed. It sends a payload naming the event and the object that changed, so an integration learns about activity without polling.
| Event | What it signals | Triggered by |
|---|---|---|
item:added | A new task was added. | /api/v1/tasks/api/v1/tasks/quick_add |
item:updated | A task was changed, like its content, due date, or location. | /api/v1/tasks/{id}/api/v1/tasks/{id}/move |
item:completed | A task was completed. | /api/v1/tasks/{id}/close |
item:uncompleted | A completed task was reopened. | /api/v1/tasks/{id}/reopen |
item:deleted | A task was deleted. | /api/v1/tasks/{id} |
note:added | A comment was added to a task or project. | /api/v1/comments |
note:updated | A comment was changed. | /api/v1/comments/{id} |
note:deleted | A comment was deleted. | /api/v1/comments/{id} |
project:added | A new project was created. | /api/v1/projects |
project:updated | A project was changed. | /api/v1/projects/{id} |
project:deleted | A project was deleted. | /api/v1/projects/{id} |
label:added | A new personal label was created. | /api/v1/labels |
label:updated | A label was changed. | /api/v1/labels/{id} |
label:deleted | A label was deleted. | /api/v1/labels/{id} |
Todoist limits how many requests an app can make in a fifteen minute window per user, and batched sync commands count as a single request against that allowance.
Todoist meters requests per user, not by a per-method cost. A token may make up to 1000 requests in a rolling fifteen minute window for the resource-style endpoints, and the same 1000 cap applies to partial sync requests. Full sync requests, which return the whole account, are capped far lower, at 100 per fifteen minutes, so an integration is expected to sync fully once and then read incrementally. Going over returns HTTP 429 with a retry_after value naming the seconds to wait.
List endpoints in the unified API are cursor-based: a limit parameter sets the page size and the response returns a next_cursor when more results remain, which is passed back as cursor to fetch the following page. An empty or absent next_cursor means the last page has been reached.
A single sync request may carry up to 100 commands, and a batch of 100 commands still counts as one request against the rate limit. The default and maximum page sizes are set per endpoint by the limit parameter.
The status codes an agent should handle, and what to do about each.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | bad_request | The request was malformed or a parameter is invalid. The body carries an error_tag, an error_code, and a human-readable error message. | Read the error message and error_extra, fix the request, and resend. |
| 401 | unauthorized | Authentication failed or no token was sent. | Send a valid Bearer token, a personal API token or an OAuth access token, in the Authorization header. |
| 403 | forbidden | The token is valid but lacks the scope or permission for this action, for example a delete without data:delete. | Request the missing scope when authorizing, or use a token that carries it. |
| 404 | not_found | The requested object does not exist, or is not visible to this token. | Verify the object id and confirm the token's account can see it. |
| 429 | too_many_requests | The request allowance for the fifteen minute window was exceeded. A retry_after value in error_extra says how long to wait. | Back off for the retry_after period, then retry. Batch sync commands to use fewer requests. |
| 500 | internal_server_error | An error on Todoist's side, which can also appear as 503 when the service is temporarily unavailable. | Retry with backoff, and contact Todoist support if it persists. |
Todoist now runs a single unified API that merges its former task and sync interfaces under one version, so there is no dated version string to pin.
Todoist merged its separate REST API (rest/v2) and Sync API (sync/v9) into a single unified API at api.todoist.com/api/v1, rebuilding parts of it and shipping official JavaScript and Python SDKs, clearer error messages, and standardized cursor-based pagination. The unified API is the current and recommended interface; there is no dated version string to pin.
Before unification, Todoist offered two separate interfaces: a resource-style REST API at rest/v2 for tasks, projects, sections, comments, and labels, and a Sync API at sync/v9 for batched commands, incremental sync, and webhooks. Both are deprecated in favour of the unified v1 API, though their endpoint shapes carried over.
The unified API replaces the separate REST v2 and Sync v9 interfaces, which are deprecated.
Todoist API reference ↗Bollard AI sits between a team's AI agents and Todoist. Grant each agent exactly the access it needs, read or write, resource by resource, and every call is checked and logged.