Todoist API Skill
This skill provides procedural guidance for working with the Todoist REST API v2 via curl and jq.
Authentication
Token Resolution
Resolve the API token in this order:
-
Check environment variable TODOIST_API_TOKEN
-
Check if the user has provided a token in the conversation context
-
If neither is available, use AskUserQuestion (or similar tool) to request the token from the user
To verify a token exists in the environment:
[ -n "$TODOIST_API_TOKEN" ] && echo "Token available" || echo "Token not set"
Making Authenticated Requests
All requests require the Authorization header with Bearer token:
curl -s -H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/ENDPOINT"
For POST requests with JSON body, include Content-Type:
curl -s -X POST
-H "Authorization: Bearer $TODOIST_API_TOKEN"
-H "Content-Type: application/json"
-d '{"key": "value"}'
"https://api.todoist.com/rest/v2/ENDPOINT"
Base URL
All REST API v2 endpoints use: https://api.todoist.com/rest/v2/
Confirmation Requirement
Before executing any destructive action (DELETE, close, update, archive), always ask the user for confirmation using AskUserQuestion or similar tool. A single confirmation suffices for a logical group of related actions.
Destructive actions include:
-
Deleting tasks, projects, sections, labels, or comments
-
Closing (completing) tasks
-
Updating existing resources
-
Archiving projects
Read-only operations (GET requests) do not require confirmation.
Endpoints Reference
Tasks
Operation Method Endpoint
List active tasks GET /tasks
Get task GET /tasks/{id}
Create task POST /tasks
Update task POST /tasks/{id}
Close task POST /tasks/{id}/close
Reopen task POST /tasks/{id}/reopen
Delete task DELETE /tasks/{id}
Task filters (query params for GET /tasks):
-
project_id
-
Filter by project
-
section_id
-
Filter by section
-
label
-
Filter by label name
-
filter
-
Todoist filter query (e.g., "today", "overdue")
Task creation/update fields:
-
content (required for creation) - Task text
-
description
-
Additional details
-
project_id , section_id , parent_id
-
Organisation
-
priority
-
1 (normal) to 4 (urgent)
-
due_string
-
Natural language ("tomorrow", "every monday")
-
due_date
-
YYYY-MM-DD format
-
due_datetime
-
RFC3339 format
-
labels
-
Array of label names
-
assignee_id
-
For shared projects
-
duration , duration_unit
-
Estimated time
Projects
Operation Method Endpoint
List projects GET /projects
Get project GET /projects/{id}
Create project POST /projects
Update project POST /projects/{id}
Archive project POST /projects/{id}/archive
Unarchive project POST /projects/{id}/unarchive
Delete project DELETE /projects/{id}
List collaborators GET /projects/{id}/collaborators
Project fields:
-
name (required for creation)
-
parent_id
-
For nested projects
-
color
-
Colour name (e.g., "berry_red", "blue")
-
is_favorite
-
Boolean
-
view_style
-
"list" or "board"
Sections
Operation Method Endpoint
List sections GET /sections
Get section GET /sections/{id}
Create section POST /sections
Update section POST /sections/{id}
Delete section DELETE /sections/{id}
Section filters (query params for GET):
- project_id
- Filter by project (recommended)
Section fields:
-
name (required)
-
project_id (required for creation)
-
order
-
Position within project
Labels
Operation Method Endpoint
List personal labels GET /labels
Get label GET /labels/{id}
Create label POST /labels
Update label POST /labels/{id}
Delete label DELETE /labels/{id}
List shared labels GET /shared_labels
Rename shared label POST /shared_labels/{name}/rename
Remove shared label DELETE /shared_labels/{name}
Label fields:
-
name (required)
-
color
-
Colour name
-
order
-
Display order
-
is_favorite
-
Boolean
Comments
Operation Method Endpoint
List comments GET /comments
Get comment GET /comments/{id}
Create comment POST /comments
Update comment POST /comments/{id}
Delete comment DELETE /comments/{id}
Comment filters (query params for GET):
-
task_id
-
Comments on a task (required if no project_id)
-
project_id
-
Comments on a project (required if no task_id)
Comment fields:
-
content (required) - Markdown supported
-
task_id or project_id (one required for creation)
Pagination
Some endpoints return paginated results. Handle pagination by checking for a next_cursor field in the response and making subsequent requests with the cursor parameter.
Pagination Pattern
Initial request
response=$(curl -s -H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/ENDPOINT")
Check for more results
next_cursor=$(echo "$response" | jq -r '.next_cursor // empty')
If cursor exists, fetch next page
if [ -n "$next_cursor" ]; then
curl -s -H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/ENDPOINT?cursor=$next_cursor"
fi
Complete Data Retrieval Loop
To retrieve all data when pagination is involved:
all_results="[]" cursor=""
while true; do
if [ -z "$cursor" ]; then
response=$(curl -s -H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/ENDPOINT")
else
response=$(curl -s -H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/ENDPOINT?cursor=$cursor")
fi
Merge results (adjust .items or root array based on endpoint)
items=$(echo "$response" | jq '.items // .') all_results=$(echo "$all_results $items" | jq -s 'add')
Check for next page
cursor=$(echo "$response" | jq -r '.next_cursor // empty') has_more=$(echo "$response" | jq -r '.has_more // false')
if [ "$has_more" != "true" ] || [ -z "$cursor" ]; then break fi done
echo "$all_results"
Common Patterns
List All Tasks in a Project
curl -s -H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/tasks?project_id=PROJECT_ID" | jq '.'
Create a Task with Due Date
curl -s -X POST
-H "Authorization: Bearer $TODOIST_API_TOKEN"
-H "Content-Type: application/json"
-d '{"content": "Task name", "due_string": "tomorrow", "priority": 2}'
"https://api.todoist.com/rest/v2/tasks"
Complete a Task
curl -s -X POST
-H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/tasks/TASK_ID/close"
Get Today's Tasks
curl -s -H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/tasks?filter=today" | jq '.'
Get Overdue Tasks
curl -s -H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/tasks?filter=overdue" | jq '.'
Error Handling
Check HTTP status codes and handle errors appropriately:
-
200
-
Success with response body
-
204
-
Success, no content
-
400
-
Bad request (check parameters)
-
401
-
Authentication failed (check token)
-
403
-
Forbidden (insufficient permissions)
-
404
-
Resource not found
-
429
-
Rate limited (wait and retry)
-
5xx
-
Server error (safe to retry)
Example with Error Handling
response=$(curl -s -w "\n%{http_code}"
-H "Authorization: Bearer $TODOIST_API_TOKEN"
"https://api.todoist.com/rest/v2/tasks")
http_code=$(echo "$response" | tail -1) body=$(echo "$response" | sed '$d')
if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then echo "$body" | jq '.' else echo "Error: HTTP $http_code" echo "$body" fi
Idempotency
For safe retries on write operations, include the X-Request-Id header (max 36 characters):
curl -s -X POST
-H "Authorization: Bearer $TODOIST_API_TOKEN"
-H "Content-Type: application/json"
-H "X-Request-Id: $(uuidgen)"
-d '{"content": "New task"}'
"https://api.todoist.com/rest/v2/tasks"
Duplicate requests with the same X-Request-Id are discarded by the server.
Completed Tasks
The REST API v2 /tasks endpoint returns only active tasks. For completed tasks, use the Sync API or the newer unified API v1 endpoints:
-
GET /tasks/completed/by_completion_date
-
Retrieve by completion date
-
GET /tasks/completed/by_due_date
-
Retrieve by original due date
See references/completed-tasks.md for details on retrieving completed task history.
Additional Reference
For detailed information on specific topics, consult:
-
references/completed-tasks.md
-
Retrieving completed task history
-
references/filters.md
-
Todoist filter query syntax
Workflow Summary
-
Resolve token - Environment, context, or ask user
-
Verify authentication - Test with a simple GET request
-
Read operations - Execute directly without confirmation
-
Write operations - Ask for confirmation before executing
-
Handle pagination - Loop with cursor for complete data
-
Parse responses - Use jq to extract and format data