Skip to content

Tasks API

All endpoints require authentication and tasks tool permissions. Data scoping applies: employees/leads see own tasks (assigned, reported, created, or watched), managers see own + squad, executives/heads see all.

GET /api/tasks

Query parameters:

ParamTypeDescription
assignee_idstringFilter by assignee (comma-separated for multiple)
status_idstringFilter by status config item ID (comma-separated)
priority_idstringFilter by priority config item ID (comma-separated)
type_idstringFilter by type config item ID (comma-separated)
squadstringFilter by squad name (comma-separated)
resource_typestringFilter by linked resource type (e.g. project)
resource_idstringFilter by linked resource ID
roadmap_item_idstringFilter by roadmap item ID
parent_idstringFilter by parent task ID. Use null or empty string for top-level tasks only
show_subtasksstringInclude subtasks in results (default true). Set to false to hide them
duestringDue date filter: today, upcoming (this week), or overdue
overduestringLegacy overdue filter — set to true (prefer due=overdue instead)
searchstringSearch title and description

Response:

{
"tasks": [
{
"id": "string",
"title": "string",
"description": "string | null",
"resource_type": "string | null",
"resource_id": "string | null",
"status_id": "string | null",
"priority_id": "string | null",
"type_id": "string | null",
"phase_id": "string | null",
"assignee_id": "string | null",
"reporter_id": "string | null",
"parent_task_id": "string | null",
"roadmap_item_id": "string | null",
"due_date": "string | null",
"start_date": "string | null",
"estimated_hours": "number | null",
"internal_hours": "number | null",
"is_private": 0,
"sort_order": "number | null",
"assignee_name": "string | null",
"assignee_picture": "string | null",
"reporter_name": "string | null",
"created_by_name": "string | null",
"status_label": "string | null",
"status_color": "string | null",
"status_value": "string | null",
"priority_label": "string | null",
"priority_color": "string | null",
"priority_value": "string | null",
"type_label": "string | null",
"type_value": "string | null",
"phase_label": "string | null",
"phase_color": "string | null",
"approved_by_name": "string | null",
"subtask_count": "number",
"todo_count": "number",
"todo_done_count": "number"
}
]
}

Sorted by sort_order, then due_date, then created_at descending.

GET /api/tasks/:id

Returns the task with related data: todos (checklist items), subtasks, and watchers.

Response:

{
"task": { "..." },
"todos": [
{
"id": "string",
"task_id": "string",
"title": "string",
"is_done": 0,
"position": 0,
"assignee_id": "string | null",
"assignee_name": "string | null",
"assignee_picture": "string | null",
"due_date": "string | null"
}
],
"subtasks": [
{
"id": "string",
"title": "string",
"assignee_name": "string | null",
"assignee_picture": "string | null",
"status_label": "string | null",
"status_color": "string | null",
"status_value": "string | null",
"priority_label": "string | null",
"priority_color": "string | null"
}
],
"watchers": [
{
"id": "string",
"name": "string",
"picture": "string | null"
}
]
}

Returns 404 if the task does not exist.

POST /api/tasks

Requires tasks update permission.

Body:

{
"title": "string (required)",
"description": "string",
"resource_type": "string",
"resource_id": "string",
"type_id": "string",
"status_id": "string",
"priority_id": "string",
"assignee_id": "string",
"parent_task_id": "string",
"due_date": "string (YYYY-MM-DD)",
"start_date": "string (YYYY-MM-DD)",
"estimated_hours": "number",
"internal_hours": "number",
"is_private": "boolean",
"reported_by_contact_id": "string"
}

If status_id is omitted, defaults to the configured default task status (typically “To Do”).

Side effects:

  • Logs a created history event
  • Sends a task_assigned notification to the assignee (if different from creator)
  • Auto-creates agent_task_meta row if the task type is agent_task
  • Auto-assigns SLA if the task is linked to a support project

Response: 201 with { task }

POST /api/tasks/:id/duplicate

Requires tasks update permission. Creates a copy of the specified task with (copy) appended to the title. The status resets to the default, and the reporter is set to the current user. All other fields (description, assignee, priority, dates, etc.) are copied from the original.

Response: 201 with { task }

Returns 404 if the original task does not exist.

PUT /api/tasks/:id

Requires tasks update permission.

Body: Any combination of updatable fields:

FieldTypeNotes
titlestring
descriptionstring
resource_typestring
resource_idstring
type_idstring
status_idstringLogs history on change
priority_idstring
assignee_idstringLogs history + notifies new assignee
parent_task_idstring
sprint_idstring
roadmap_item_idstring
phase_idstring
due_datestring
start_datestring
estimated_hoursnumber
internal_hoursnumber
is_privateboolean
sort_ordernumber
reported_by_contact_idstring
requirementsstring
scopestring
estimate_compute_hoursnumber
estimate_total_costnumber
approved_by_user_idstring
approved_atstring
delivery_notesstring
testing_notesstring

Side effects:

  • Sets updated_at to current timestamp
  • Logs status_changed history event when status changes
  • Logs assigned history event and sends task_assigned notification when assignee changes
  • Auto-creates agent_task_meta row if type is changed to agent_task

Response: { task } with joined status, priority, and type labels.

Returns 400 if no fields are provided. Returns 404 if the task does not exist.

DELETE /api/tasks/:id

Requires tasks manage permission.

Response: { ok: true }

Returns 404 if the task does not exist.

PUT /api/tasks/:id/todos

Requires tasks update permission. Replaces the full set of todos for the task — send the complete list each time. Todos not present in the request are deleted. New todos (without id) are created; existing todos (with id) are upserted.

Body:

{
"todos": [
{
"id": "string (optional — omit for new items)",
"title": "string",
"is_done": false,
"position": 0,
"assignee_id": "string | null",
"due_date": "string | null"
}
]
}

Side effects:

  • Sends todo_assigned notification when a todo is newly assigned to someone other than the current user

Response:

{
"todos": [
{
"id": "string",
"task_id": "string",
"title": "string",
"is_done": 0,
"position": 0,
"assignee_id": "string | null",
"assignee_name": "string | null",
"assignee_picture": "string | null",
"due_date": "string | null"
}
]
}

Returns 404 if the task does not exist.

GET /api/tasks/:id/subtasks

Returns subtasks for the given parent task, sorted by sort_order then created_at.

Response:

{
"subtasks": [
{
"id": "string",
"title": "string",
"assignee_name": "string | null",
"assignee_picture": "string | null",
"status_label": "string | null",
"status_color": "string | null",
"status_value": "string | null"
}
]
}
POST /api/tasks/:id/subtasks

Requires tasks update permission. Creates a new task as a child of the specified parent.

Body:

{
"title": "string (required)",
"assignee_id": "string",
"due_date": "string"
}

Response: 201 with { task }

Returns 404 if the parent task does not exist. Returns 400 if title is empty.

POST /api/tasks/:id/watch

Adds the current user as a watcher. Idempotent — calling again has no effect.

Response: { watching: true }

Returns 404 if the task does not exist.

DELETE /api/tasks/:id/watch

Removes the current user from watchers.

Response: { watching: false }

GET /api/tasks/:id/resources

Returns links/documents attached to a task, sorted by sort_order then created_at.

Response:

{
"resources": [
{
"id": "string",
"task_id": "string",
"type": "link",
"url": "string",
"title": "string",
"description": "string | null",
"icon": "string",
"sort_order": "number | null",
"created_by": "string",
"created_by_name": "string | null",
"created_at": "string"
}
]
}

The icon field is auto-detected from the URL domain (e.g. figma, github, google-docs, slack, notion, linear, loom, youtube, etc.). Falls back to link.

POST /api/tasks/:id/resources

Requires tasks update permission.

Body:

{
"url": "string (required)",
"title": "string (required)",
"description": "string"
}

Response: 201 with { resource }

Returns 400 if url or title is missing.

DELETE /api/tasks/:id/resources/:resourceId

Requires tasks update permission.

Response: { ok: true }

PUT /api/tasks/:id/resources/reorder

Requires tasks update permission.

Body:

{
"order": ["resourceId1", "resourceId2", "resourceId3"]
}

Sets sort_order based on array position.

Response: { ok: true }

Returns 400 if order array is empty.