Tasks
Create, update, move, and finalize tasks via the REST API.
A task is the core unit of work. Every task belongs to exactly one workspace and one stage, optionally to a project, and may have assignees, tags, subtasks, comments, attachments, and time entries.
Endpoints
| Method | Path | Notes |
|---|---|---|
GET | /api/v1/tasks | List with filters. Cursor pagination. |
POST | /api/v1/tasks | Create. Requires workspaceId. Accepts Idempotency-Key. |
GET | /api/v1/tasks/{id} | Single task with assignees, tags, type, project, stage, subtasks. |
PATCH | /api/v1/tasks/{id} | Update fields, or move by sending workspaceId / stageId / sortOrder. |
DELETE | /api/v1/tasks/{id} | Delete the task. |
Subtasks have their own nested resource.
Task model
{
"id": "t1…",
"organizationId": "...",
"workspaceId": "...",
"stageId": "...",
"sortOrder": 2,
"projectId": null,
"taskTypeId": null,
"subject": "Wire the OAuth login button",
"descriptionHtml": "<p>Hook the button up to Better Auth.</p>",
"priority": "high",
"expectedStartDate": null,
"expectedEndDate": null,
"estimatedHours": 4,
"finalizedAt": null,
"finalizationDate": null,
"assignees": [{ "user": { "id": "...", "name": "Alex" } }],
"tags": [{ "tag": { "id": "...", "name": "backend" } }],
"taskType": null,
"project": null,
"stage": { "id": "...", "name": "Doing", "isFinalizing": false },
"subtasks": []
}| Field | Type | Notes |
|---|---|---|
priority | enum | low · normal · high · urgent |
descriptionHtml | string | Sanitized HTML produced by the Tiptap editor. Inline image URLs must be app-hosted (no base64). |
estimatedHours | decimal | null | Hours planned; reported time is computed separately from TimeEntry rows. |
finalizedAt | ISO date | null | Set when the task is closed; auto-set when moved into a finalizing stage. |
Listing tasks
GET /api/v1/tasks?workspaceId=...&priority=high&limit=50| Param | Type | Notes |
|---|---|---|
workspaceId | UUID | Filter to one workspace. |
stageId | UUID | Filter to one stage. |
projectId | UUID | Project filter. Combines with project visibility. |
taskTypeId | UUID | Task-type filter. |
assigneeId | UUID | Tasks assigned to a specific member. |
tagId | UUID | Tasks carrying a specific tag. |
priority | enum | One of the priority values. |
query | string | Case-insensitive search on subject. |
cursor | UUID | From a previous response. |
limit | int 1–100 | Default 50. |
Returns { data, pagination: { nextCursor } }. Tasks are ordered by (createdAt DESC, id DESC) so newer work surfaces first.
Creating a task
POST /api/v1/tasks
Content-Type: application/json
Idempotency-Key: ...
{
"workspaceId": "1f3c…",
"subject": "Wire the OAuth login button",
"priority": "high",
"estimatedHours": 4
}Required: workspaceId, subject.
The task is placed in the workspace's leftmost stage (lowest sortOrder) — you cannot pass stageId on create. If that stage is the finalizing stage, the task is finalized on creation.
| Optional field | Type | Notes |
|---|---|---|
descriptionHtml | string | ≤ 50 000 chars. |
projectId | UUID | null | Project access enforced. |
taskTypeId | UUID | null | Must belong to the org. |
priority | enum | Defaults to normal. |
expectedStartDate / expectedEndDate | ISO date | null | — |
estimatedHours | number ≥ 0 | ≤ 99 999.99. |
assigneeIds | UUID[] | Must be org members. Triggers in-app + email notifications. |
tagIds | UUID[] | Must belong to the org. |
Response: 201 Created with the full task.
Updating or moving a task
PATCH /api/v1/tasks/{id}
{ "subject": "OAuth wiring", "priority": "normal" }All fields are optional. Sending any of workspaceId, stageId, or sortOrder switches the request to move mode:
- Move within the same stage (
sortOrderonly) — reorders the task. - Move to a new stage —
stageIdmust belong to the same (or target) workspace. - Cross-workspace move — pass
workspaceId(and optionallystageId). IfstageIdis omitted, the task lands in the target workspace's leftmost stage. - Moving into a finalizing stage auto-sets
finalizedAt/finalizationDateto today (in the calling user's timezone). - Moving out of a finalizing stage clears them.
A move on a project-restricted task requires project edit access; otherwise the response is 404.
Deleting a task
DELETE /api/v1/tasks/{id}Returns { "data": { "success": true } }. Cascade-deletes subtasks, comments, attachments, and time entries owned by the task.
Common precondition failures
| Code | Cause |
|---|---|
404 | Cross-org id, missing workspace, missing task-type, or project access denied. |
412 | Target workspace has no stages — you cannot place a task in it. |
400 | stageId belongs to a different workspace than the target. |