Tasks
Tasks are the work items in your planner. These endpoints act on your own tasks only: an id belonging to anyone else returns 404 Not Found.
List tasks
Section titled “List tasks”Returns one page of your tasks, with filters for status, space, project, label and due date.
GET /api/tasksFine-tune the list with these query parameters:
Query parameters
Section titled “Query parameters”| Name | Type | Required | Description |
|---|---|---|---|
status |
backlog | todo | in_progress | completed | cancelled | archived |
No | Only tasks with this status. Setting it replaces the default status filter |
spaceId |
string | No | Only tasks in this space |
projectId |
string | No | Only tasks in this project |
labelId |
string | No | Only tasks carrying this label |
dueBefore |
string | No | Only tasks with a due date at or before this ISO 8601 timestamp |
includeCompleted |
true |
No | Include completed, cancelled and archived tasks |
page |
integer | No | Page number from 1; defaults to 1 |
limit |
integer | No | Tasks per page; defaults to 50, maximum 100 |
Unless you set status or includeCompleted=true, the list leaves out completed, cancelled and archived tasks. Each task carries its space, project, labels, checklist items and non-cancelled time blocks. Results come back ordered by status, then priority (highest first), due date and sortOrder.
Response
Section titled “Response”Returns a data array of tasks and a pagination object with page, limit, total and pages:
{ "data": [ { "id": "9c1f4b2e-7d83-4c6a-9e50-3f8a2b7d1c04", "userId": "6f0d2a9b-1c74-4e8f-8a3b-5c9d0e2f6a71", "spaceId": "0e5a7c3b-2f16-4d98-b7a4-8c1e3f5d9b02", "projectId": "b7d3e9a1-5c02-4f47-9a86-1d6b0e4c8a53", "parentTaskId": null, "title": "Write project brief", "description": "First draft for the client review.", "status": "todo", "priority": "high", "dueAt": "2026-10-07T13:00:00.000Z", "startAfter": null, "completedAt": null, "estimateMinutes": 90, "actualMinutes": null, "minSessionMinutes": 10, "maxSessionMinutes": 60, "cooldownMinutes": 5, "autoSchedule": true, "autoSplit": true, "schedulePreferenceId": null, "locked": false, "sortOrder": 0, "createdAt": "2026-09-30T01:12:03.000Z", "updatedAt": "2026-09-30T01:12:03.000Z", "space": { "id": "0e5a7c3b-2f16-4d98-b7a4-8c1e3f5d9b02", "name": "Work", "color": "#3B82F6", "icon": "briefcase" }, "project": { "id": "b7d3e9a1-5c02-4f47-9a86-1d6b0e4c8a53", "name": "Website refresh", "color": "#10B981" }, "labels": [ { "id": "d2a6f8c4-9b31-4e57-8c02-7a4d1e6b3f89", "name": "writing", "color": "#F59E0B" } ], "checklistItems": [ { "id": "4e8b0c6d-3a92-4f15-8d7e-2b6c9a0f4e51", "taskId": "9c1f4b2e-7d83-4c6a-9e50-3f8a2b7d1c04", "title": "Gather notes", "completed": true, "sortOrder": 0 } ], "timeBlocks": [ { "id": "7a2c5e8b-4d61-4a93-b0f8-9e3c6d2a7b40", "userId": "6f0d2a9b-1c74-4e8f-8a3b-5c9d0e2f6a71", "taskId": "9c1f4b2e-7d83-4c6a-9e50-3f8a2b7d1c04", "title": "Write project brief", "type": "task", "startAt": "2026-10-01T00:30:00.000Z", "endAt": "2026-10-01T02:00:00.000Z", "locked": false, "defended": false, "source": "auto", "sortOrder": 0, "status": "scheduled", "explanation": "Fits before the team meeting", "createdAt": "2026-09-30T01:12:05.000Z", "updatedAt": "2026-09-30T01:12:05.000Z" } ] } ], "pagination": { "page": 1, "limit": 50, "total": 1, "pages": 1 }}total counts every task matching the filters, so use page and limit to fetch the rest.
Status codes
Section titled “Status codes”| Code | When |
|---|---|
200 OK |
the list returned |
401 Unauthorized |
no valid session or API key |
Create a task
Section titled “Create a task”Creates one task.
POST /api/tasksSend these fields in the JSON body:
Body parameters
Section titled “Body parameters”| Name | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Task title, 1–500 characters |
description |
string | No | Longer text |
priority |
none | low | medium | high | urgent |
No | Defaults to none |
status |
backlog | todo | in_progress | completed | cancelled | archived |
No | Defaults to todo |
dueAt |
string | No | ISO 8601 timestamp the task is due |
startAfter |
string | No | ISO 8601 timestamp; the scheduler won’t place work before it |
estimateMinutes |
integer | No | How long the task takes, 0 to 960 minutes (16 hours). Longer than one working day is accepted but flagged as oversized |
minSessionMinutes |
integer | No | Shortest scheduling session, 1 to 60 minutes; defaults to 5 |
maxSessionMinutes |
integer | No | Longest scheduling session, 1 to 480 minutes; defaults to 60 |
cooldownMinutes |
integer | No | Gap between scheduling sessions; defaults to 5 |
autoSchedule |
boolean | No | Let the scheduler place time blocks; defaults to true |
autoSplit |
boolean | No | Let the scheduler split the estimate across sessions; defaults to true |
spaceId |
string | No | Id of one of your spaces; without it the task has no space |
projectId |
string | No | Id of one of your projects |
parentTaskId |
string | No | Id of one of your tasks, which makes this task a subtask |
schedulePreferenceId |
string | No | Id of one of your schedule preferences |
labelIds |
array of string | No | Ids of your labels; repeated ids are dropped |
The spaceId, projectId, parentTaskId, schedulePreferenceId and labelIds fields must name records you own, and each id must be a UUID. With autoSchedule on and an estimateMinutes set, the scheduler places work for the task — see How the scheduler places work.
Response
Section titled “Response”The new task comes back with 201 Created, including its space, project, labels and checklist items:
{ "id": "9c1f4b2e-7d83-4c6a-9e50-3f8a2b7d1c04", "userId": "6f0d2a9b-1c74-4e8f-8a3b-5c9d0e2f6a71", "spaceId": "0e5a7c3b-2f16-4d98-b7a4-8c1e3f5d9b02", "projectId": "b7d3e9a1-5c02-4f47-9a86-1d6b0e4c8a53", "parentTaskId": null, "title": "Write project brief", "description": "First draft for the client review.", "status": "todo", "priority": "high", "dueAt": "2026-10-07T13:00:00.000Z", "startAfter": null, "completedAt": null, "estimateMinutes": 90, "actualMinutes": null, "minSessionMinutes": 10, "maxSessionMinutes": 60, "cooldownMinutes": 5, "autoSchedule": true, "autoSplit": true, "schedulePreferenceId": null,
"locked": false, "sortOrder": 0, "createdAt": "2026-09-30T01:12:03.000Z", "updatedAt": "2026-09-30T01:12:03.000Z", "space": { "id": "0e5a7c3b-2f16-4d98-b7a4-8c1e3f5d9b02", "name": "Work", "color": "#3B82F6", "icon": "briefcase" }, "project": { "id": "b7d3e9a1-5c02-4f47-9a86-1d6b0e4c8a53", "name": "Website refresh", "color": "#10B981" }, "labels": [ { "id": "d2a6f8c4-9b31-4e57-8c02-7a4d1e6b3f89", "name": "writing", "color": "#F59E0B" } ], "checklistItems": []}A fresh task has no time blocks yet and an empty checklist; those come from the scheduler and from checklist items you add. An estimate longer than one working day is accepted but flagged as oversized — it appears in oversizedTaskIds when you next run the planner, so you can split the task or turn it into a project.
Example: create a task with curl
Section titled “Example: create a task with curl”This request creates a 90-minute task in a space, with one label and a due time:
curl -X POST https://tasks.example.com/api/tasks \ -H "Authorization: Bearer dp_change-me" \ -H "Content-Type: application/json" \ -d '{"title": "Write project brief", "estimateMinutes": 90, "dueAt": "2026-10-07T13:00:00Z", "spaceId": "0e5a7c3b-2f16-4d98-b7a4-8c1e3f5d9b02", "labelIds": ["d2a6f8c4-9b31-4e57-8c02-7a4d1e6b3f89"]}'Change the spaceId and labelIds values to ids from your own spaces and labels.
Status codes
Section titled “Status codes”| Code | When |
|---|---|
201 Created |
the task was created |
400 Bad Request |
the body failed validation (Validation failed), or named a record you don’t own (spaceId not found and similar) |
401 Unauthorized |
no valid session or API key |
Get a task
Section titled “Get a task”Returns one task with everything attached to it: time blocks, checklist items, subtasks, parent task and linked documents.
GET /api/tasks/{id}Set {id} to the task’s id.
Response
Section titled “Response”The full task comes back as one JSON object:
{ "id": "9c1f4b2e-7d83-4c6a-9e50-3f8a2b7d1c04", "userId": "6f0d2a9b-1c74-4e8f-8a3b-5c9d0e2f6a71", "spaceId": "0e5a7c3b-2f16-4d98-b7a4-8c1e3f5d9b02", "projectId": "b7d3e9a1-5c02-4f47-9a86-1d6b0e4c8a53", "parentTaskId": null, "title": "Write project brief", "description": "First draft for the client review.", "status": "in_progress", "priority": "high", "dueAt": "2026-10-07T13:00:00.000Z", "startAfter": null, "completedAt": null, "estimateMinutes": 90, "actualMinutes": null, "minSessionMinutes": 10, "maxSessionMinutes": 60, "cooldownMinutes": 5, "autoSchedule": true, "autoSplit": true, "schedulePreferenceId": null,
"locked": false, "sortOrder": 0, "createdAt": "2026-09-30T01:12:03.000Z", "updatedAt": "2026-09-30T02:45:19.000Z", "space": { "id": "0e5a7c3b-2f16-4d98-b7a4-8c1e3f5d9b02", "name": "Work", "color": "#3B82F6", "icon": "briefcase" }, "project": { "id": "b7d3e9a1-5c02-4f47-9a86-1d6b0e4c8a53", "name": "Website refresh", "color": "#10B981" }, "labels": [ { "id": "d2a6f8c4-9b31-4e57-8c02-7a4d1e6b3f89", "name": "writing", "color": "#F59E0B" } ], "checklistItems": [ { "id": "4e8b0c6d-3a92-4f15-8d7e-2b6c9a0f4e51", "taskId": "9c1f4b2e-7d83-4c6a-9e50-3f8a2b7d1c04", "title": "Gather notes", "completed": true, "sortOrder": 0 } ], "timeBlocks": [ { "id": "7a2c5e8b-4d61-4a93-b0f8-9e3c6d2a7b40", "userId": "6f0d2a9b-1c74-4e8f-8a3b-5c9d0e2f6a71", "taskId": "9c1f4b2e-7d83-4c6a-9e50-3f8a2b7d1c04", "title": "Write project brief", "type": "task", "startAt": "2026-10-01T00:30:00.000Z", "endAt": "2026-10-01T02:00:00.000Z", "locked": false, "defended": false, "source": "auto", "sortOrder": 0, "status": "scheduled", "explanation": "Fits before the team meeting", "createdAt": "2026-09-30T01:12:05.000Z", "updatedAt": "2026-09-30T01:12:05.000Z" } ], "subtasks": [], "parentTask": null, "documents": [ { "id": "c9f4a1b7-6e28-4d50-a3c9-8b5e0d7f2a63", "title": "Brief template", "docType": "reference", "url": "https://example.com/brief-template" } ]}Unlike the task list, timeBlocks includes every block, cancelled ones too. subtasks holds task objects without their own relations, parentTask gives the parent’s id and title (or null), and documents gives each linked document’s id, title, docType and url.
Status codes
Section titled “Status codes”| Code | When |
|---|---|
200 OK |
the task returned |
401 Unauthorized |
no valid session or API key |
404 Not Found |
no task with that id belongs to you (Task not found) |
Update a task
Section titled “Update a task”Changes fields of one task. Omit the fields you want to keep.
PATCH /api/tasks/{id}Send only the fields to change in the JSON body:
Body parameters
Section titled “Body parameters”| Name | Type | Required | Description |
|---|---|---|---|
title |
string | No | New title, 1–500 characters |
description |
string | No | New description; send "" to empty it |
status |
backlog | todo | in_progress | completed | cancelled | archived |
No | New status |
priority |
none | low | medium | high | urgent |
No | New priority |
dueAt |
string | No | ISO 8601 timestamp; send "" to clear it |
startAfter |
string | No | ISO 8601 timestamp; send "" to clear it |
estimateMinutes |
integer | No | How long the task takes, 0 to 960 minutes (16 hours). Longer than one working day is accepted but flagged as oversized |
minSessionMinutes |
integer | No | Shortest scheduling session, 1 to 60 minutes |
maxSessionMinutes |
integer | No | Longest scheduling session, 1 to 480 minutes |
cooldownMinutes |
integer | No | Gap between scheduling sessions |
autoSchedule |
boolean | No | Let the scheduler place time blocks |
autoSplit |
boolean | No | Let the scheduler split the estimate across sessions |
spaceId |
string or null |
No | Move to one of your spaces, or null for no space |
projectId |
string or null |
No | Move to one of your projects, or null to detach |
schedulePreferenceId |
string or null |
No | Attach one of your schedule preferences, or null to detach |
locked |
boolean | No | Stop the scheduler moving this task |
completedAt |
string | No | When the task was finished; send "" to clear it |
sortOrder |
number | No | Position within lists |
labelIds |
array of string | No | Replaces every label on the task |
Sending labelIds replaces the task’s whole set of labels. Setting status to completed copies estimateMinutes into actualMinutes when actualMinutes is unset; it does not stamp completedAt, so send completedAt yourself if you want one.
Response
Section titled “Response”The updated task comes back with its space, project, labels and checklist items, in the same shape as the create response:
{ "id": "9c1f4b2e-7d83-4c6a-9e50-3f8a2b7d1c04", "userId": "6f0d2a9b-1c74-4e8f-8a3b-5c9d0e2f6a71", "spaceId": "0e5a7c3b-2f16-4d98-b7a4-8c1e3f5d9b02", "projectId": "b7d3e9a1-5c02-4f47-9a86-1d6b0e4c8a53", "parentTaskId": null, "title": "Write project brief", "description": "First draft for the client review.", "status": "in_progress", "priority": "high", "dueAt": "2026-10-07T13:00:00.000Z", "startAfter": null, "completedAt": null, "estimateMinutes": 90, "actualMinutes": null, "minSessionMinutes": 10, "maxSessionMinutes": 60, "cooldownMinutes": 5, "autoSchedule": true, "autoSplit": true, "schedulePreferenceId": null,
"locked": true, "sortOrder": 0, "createdAt": "2026-09-30T01:12:03.000Z", "updatedAt": "2026-09-30T02:45:19.000Z", "space": { "id": "0e5a7c3b-2f16-4d98-b7a4-8c1e3f5d9b02", "name": "Work", "color": "#3B82F6", "icon": "briefcase" }, "project": { "id": "b7d3e9a1-5c02-4f47-9a86-1d6b0e4c8a53", "name": "Website refresh", "color": "#10B981" }, "labels": [ { "id": "d2a6f8c4-9b31-4e57-8c02-7a4d1e6b3f89", "name": "writing", "color": "#F59E0B" } ], "checklistItems": []}updatedAt changes on every edit. This is the only task endpoint that accepts locked and sortOrder — time blocks accepts both as well.
Example: complete a task
Section titled “Example: complete a task”This marks a task finished and records when:
curl -X PATCH https://tasks.example.com/api/tasks/9c1f4b2e-7d83-4c6a-9e50-3f8a2b7d1c04 \ -H "Authorization: Bearer dp_change-me" \ -H "Content-Type: application/json" \ -d '{"status": "completed", "completedAt": "2026-10-01T05:00:00Z"}'The response’s actualMinutes becomes 90 because the task had a 90-minute estimate and no logged time; drop completedAt if you don’t want a completion time recorded.
Status codes
Section titled “Status codes”| Code | When |
|---|---|
200 OK |
the task was updated |
400 Bad Request |
the body failed validation (Validation failed), or named a record you don’t own (spaceId not found and similar) |
401 Unauthorized |
no valid session or API key |
404 Not Found |
no task with that id belongs to you (Task not found) |
Delete a task
Section titled “Delete a task”Marks one task as archived. Nothing is removed from the database.
DELETE /api/tasks/{id}No body is needed. Add ?permanent=true to delete the task for good instead: its checklist items, label links, dependencies and document links go with it, while its time blocks and subtasks are kept and detached. Permanent delete can’t be undone.
Response
Section titled “Response”The endpoint confirms the change:
{ "success": true}The task keeps its archived status, so GET /api/tasks/{id} still finds it while the task list leaves it out. Archived tasks older than 30 days are permanently removed the next time the planner runs, so the trash stays bounded on its own.
Status codes
Section titled “Status codes”| Code | When |
|---|---|
200 OK |
the task was archived (or deleted, with ?permanent=true) |
401 Unauthorized |
no valid session or API key |
404 Not Found |
no task with that id belongs to you (Task not found) |
Complete or archive tasks in bulk
Section titled “Complete or archive tasks in bulk”Applies one action to many of your tasks in a single request.
POST /api/tasks/batchSend the action and the task ids in the JSON body:
Body parameters
Section titled “Body parameters”| Name | Type | Required | Description |
|---|---|---|---|
action |
complete | delete | archive |
Yes | complete finishes tasks; delete and archive both set status to archived |
taskIds |
array of string | Yes | Ids of the tasks to act on; the array must not be empty |
Only tasks that belong to you are touched. Ids that don’t match one of your tasks are skipped and counted in invalid. complete sets status to completed, stamps completedAt with the current time and copies estimateMinutes into actualMinutes where actualMinutes is unset. delete is a soft delete: like archive, it sets status to archived.
Response
Section titled “Response”The counts come back in one object:
{ "affected": 3, "requested": 4, "invalid": 1}requested is how many ids you sent, affected is how many tasks changed, and invalid is how many were skipped.
Status codes
Section titled “Status codes”| Code | When |
|---|---|
200 OK |
the action ran |
400 Bad Request |
action or taskIds[] was missing (action and taskIds[] are required), or action wasn’t one of the three (Invalid action. Use: complete, delete, archive) |
401 Unauthorized |
no valid session or API key |
See also
Section titled “See also”- REST API: authentication, errors and pagination
- Spaces, projects and tasks: how work is organised
- How the scheduler places work: what
autoScheduleand estimates do - create_task: create a task through the MCP server
- Checklist items: sub-items within a task
- Time blocks: the slots a task is scheduled into