Skip to content

API: Tasks

{
"id": "019f74a1-…",
"workspaceId": "019f7464-…",
"title": "Buy paint",
"description": "Matte white, 10l",
"priority": "HIGH",
"status": "PENDING",
"blockedReason": null,
"dueDate": "2026-08-01T00:00:00Z",
"listId": "019f7488-…",
"creator": { "userId": "019f…", "firstName": "Greta", "lastName": "Lengl", "avatarUrl": null, "inactive": false },
"assignee": null,
"version": 3,
"subtasks": [
{ "id": "019f…", "title": "Compare prices", "status": "DONE" }
],
"attachments": [
{ "id": "019f…", "fileName": "color-sample.jpg", "mediaType": "image/jpeg", "sizeBytes": 182734 }
],
"createdAt": "2026-07-18T09:30:00Z"
}

status is PENDING, IN_PROGRESS, or DONE (subtasks stay binary: PENDING/DONE). blockedReason is non-null when the task is flagged blocked — it is cleared automatically when the task is completed. priority, description, dueDate, listId, and assignee are nullable. version increments on every change and guards optimistic locking.

When a member is removed from a workspace, the tasks they authored are reassigned to a reserved “Inactive User” and their assignments are cleared. On those tasks creator.inactive is true — render a generic label rather than the placeholder name.

GET /api/workspaces/{workspaceId}/tasks

Returns all tasks of the workspace as an array of task objects (no pagination yet). Filter client-side by status, listId, or assignee.

The request may carry a client-generated id (UUIDv4/v7) — required when an X-Op-Id header is sent, so replays and queued follow-up operations can reference the task before the server ever saw it.

POST /api/workspaces/{workspaceId}/tasks
{
"title": "Buy paint",
"description": "Matte white, 10l",
"priority": "HIGH",
"dueDate": "2026-08-01T00:00:00Z",
"listId": "019f7488-…"
}

Only title is required. Returns 201 with { "id": "…" }.

PATCH /api/workspaces/{workspaceId}/tasks/{taskId}/fields
{ "title": "New title", "dueDate": null }

Field-masked update for offline sync: only the fields present in the body change; an explicit null clears a field. Patchable: title, description, priority, dueDate, listId, assigneeId, blockedReason (an explicit null or blank unblocks; ignored on DONE tasks). Unlike the PUT, there is no version gate — concurrent changes to the same field resolve last-writer-wins by arrival order. Returns 204. Errors: 400 for a blank title, 404 for an unknown task.

PUT /api/workspaces/{workspaceId}/tasks/{taskId}

Full update — send every field plus the version you last read:

{
"title": "Buy paint",
"description": null,
"priority": "NORMAL",
"dueDate": "2026-08-01T00:00:00Z",
"listId": null,
"assigneeId": "019f…",
"version": 3
}

Returns 200. A stale version answers 409 — re-read and retry. assigneeId must be a member of the workspace; null unassigns.

PATCH /api/workspaces/{workspaceId}/tasks/{taskId}
{ "status": "IN_PROGRESS" }

Accepts PENDING, IN_PROGRESS, or DONE; setting DONE also clears blockedReason. Returns 200. This shortcut needs no version.

POST /api/workspaces/{workspaceId}/tasks/{taskId}/copy
{
"name": "Buy paint (copy)",
"preserveCreator": false,
"preserveAssignee": false,
"includeSubtasks": true,
"listId": null
}

Returns 201 with { "id": "…" }. Copies stay in the same workspace; attachments are never copied. Omitted booleans default as shown.

POST /api/workspaces/{workspaceId}/tasks/{taskId}/move
{ "targetWorkspaceId": "019f…" }

Returns 200. You must be a member of both workspaces. The assignee is cleared unless they are also a member of the target; the list assignment is cleared; attachments move with the task. Answers 413 when the target workspace’s storage limit would be exceeded.

DELETE /api/workspaces/{workspaceId}/tasks/{taskId}

Returns 204. Deletes the task with its subtasks and attachment references.