Subtasks
Nested subtasks under a parent task.
A subtask is a checklist item under a parent task. Subtasks have no standalone REST resource — every operation goes through /api/v1/tasks/{id}/subtasks/..., and they share the parent task's project visibility.
Subtasks are organized into groups. Every task has at least one group (a default group named for the creator's language). A subtask always belongs to exactly one group, exposed as groupId. Group management itself is not yet part of the public API — but you can target a group when creating a subtask, and the create endpoint stays backward compatible if you ignore groups entirely.
Endpoints
| Method | Path | Notes |
|---|---|---|
GET | /api/v1/tasks/{id}/subtasks | List subtasks of a task, ordered by group, then by sortOrder within each group. |
POST | /api/v1/tasks/{id}/subtasks | Create. Accepts Idempotency-Key. |
PATCH | /api/v1/tasks/{id}/subtasks/{subId} | Update, finalize, or reopen. |
DELETE | /api/v1/tasks/{id}/subtasks/{subId} | Delete. |
Subtasks are also returned inline with the parent task by GET /api/v1/tasks/{id}.
Subtask model
{
"id": "s1…",
"taskId": "t1…",
"groupId": "g1…",
"name": "Hook up the Google provider",
"descriptionHtml": null,
"sortOrder": 0,
"finalizedAt": null,
"finalizationDate": null,
"assignees": [{ "user": { "id": "...", "name": "Alex" } }],
"createdAt": "...",
"updatedAt": "..."
}sortOrder is scoped within a group, so two subtasks in different groups can share the same value. Order a full list by group first (the GET endpoint already does this).
Creating a subtask
POST /api/v1/tasks/{id}/subtasks
{
"name": "Hook up the Google provider",
"descriptionHtml": "<p>Use the existing config.</p>",
"assigneeIds": ["..."],
"groupId": "g1…"
}| Field | Type | Notes |
|---|---|---|
name | string | 1–300 chars, required. |
descriptionHtml | string | ≤ 50 000 chars, sanitized server-side. |
assigneeIds | UUID[] | Must be org members. Triggers in-app + email notifications. |
groupId | UUID | Optional. Must belong to the task. Omit it and the subtask lands in the task's first (default) group. |
Response: 201 Created with the subtask.
Updating, finalizing, reopening
PATCH /api/v1/tasks/{id}/subtasks/{subId}
{ "name": "Hook up Google OAuth" }| Field | Type | Notes |
|---|---|---|
name | string | 1–300 chars. |
descriptionHtml | string | ≤ 50 000 chars. |
finalizationDate | ISO date | null | Pass a date to finalize. Pass null to reopen a finalized subtask. |
Invalid state transitions return 412 Precondition Failed:
- Setting
finalizationDateon an already-finalized subtask. - Setting
finalizationDate: nullon a subtask that isn't finalized.
Deleting a subtask
DELETE /api/v1/tasks/{id}/subtasks/{subId}Returns { "data": { "success": true } }. Cascade-deletes the subtask's time entries.