53 endpoints. Send your API key as Authorization: Bearer <key> on each request; see The API and AI assistants for keys, errors, idempotency and the conventions every endpoint shares.
GET /api/projects
List projects with progress and summary counts
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
status | query | Status | No | Only projects with this status. |
q | query | string | No | Case-insensitive search across name, description and owner. |
include | query | string | No | Set to tree to include each project's full phases tree. One of: tree. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Projects, ordered by position then id (array of ProjectListItem) |
| 401 | Missing or invalid API key (Error) |
POST /api/projects
Create a project, optionally with a nested plan
Nested phases[].tasks[].subtasks[] are created in one transaction. If any item is invalid, nothing is created.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required): ProjectCreate.
| Field | Type | Required | Description |
|---|---|---|---|
name | Name | Yes | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
repo_url | RepoUrl | No | Up to 2000 characters. |
task_key | TaskKey | No | The start of every task key in the project (a task is AF-12). Unique in the organisation; a clash is a 400 on task_key. Made from the name's initials when not given on create. |
phases | array of PhaseCreate | No |
Example:
{
"name": "Website relaunch",
"status": "in_progress",
"owner": "Dana",
"due_date": "2026-12-01",
"phases": [
{
"name": "Foundations",
"tasks": [
{
"name": "Repo & CI",
"status": "done"
},
{
"name": "Design system",
"subtasks": [
{
"name": "Colour tokens"
},
{
"name": "Typography"
}
]
}
]
},
{
"name": "Build",
"tasks": [
{
"name": "Home page",
"priority": "high"
}
]
}
]
}
Responses:
| Status | Meaning |
|---|---|
| 201 | Created project with its full tree (Project) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 413 | Request body larger than 2 MB (Error) |
GET /api/projects/{id}
Get a project with its full tree
With view=summary the phases, tasks and subtasks come without their descriptions (the project keeps its own), which for a large project is less than half the size. A task's description is in GET /api/tasks/{id}. With view=list the phases keep their descriptions and only the tasks and subtasks leave theirs out (what the website's project page shows before a task is opened).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
view | query | string | No | summary leaves out the descriptions of the phases, tasks and subtasks; list leaves out only those of the tasks and subtasks. Any other value is refused (400). One of: summary, list. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Project (Project) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
PATCH /api/projects/{id}
Update a project
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required): ProjectUpdate.
| Field | Type | Required | Description |
|---|---|---|---|
name | Name | No | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
repo_url | RepoUrl | No | Up to 2000 characters. |
task_key | TaskKey | No | The start of every task key in the project (a task is AF-12). Unique in the organisation; a clash is a 400 on task_key. Made from the name's initials when not given on create. |
Example:
{
"status": "done"
}
Responses:
| Status | Meaning |
|---|---|
| 200 | Updated project with its full tree (Project) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
DELETE /api/projects/{id}
Delete a project and everything under it
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Responses:
| Status | Meaning |
|---|---|
| 204 | Deleted |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/projects/{id}/phases
List a project's phases (with tasks and subtasks)
With view=summary each phase comes with its progress but without its tasks or description, for a phase picker.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
view | query | string | No | summary leaves out descriptions (and, on a phase list, the tasks). Any other value is refused (400). One of: summary. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Phases, ordered by position then id (array of Phase) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/projects/{id}/phases
Add a phase to a project, optionally with nested tasks
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required): PhaseCreate.
| Field | Type | Required | Description |
|---|---|---|---|
project_id | ParentId | No | From 1 to 2147483647. |
name | Name | Yes | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
kind | string | No | Only build is accepted. Sections are created by moving a task into one, or by creating a task with section. One of: build. |
tasks | array of TaskCreate | No |
Responses:
| Status | Meaning |
|---|---|
| 201 | Created phase (Phase) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/projects/{id}/activity
Change history for one project, newest first
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
limit | query | integer | No | Default 50. From 1 to 500. |
before | query | integer | No | Return entries with an id lower than this (for paging). From 1 to .... |
Responses:
| Status | Meaning |
|---|---|
| 200 | Activity entries (array of Activity) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/phases
Create a phase (parent given in the body)
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
project_id | ParentId | No | From 1 to 2147483647. |
name | Name | Yes | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
kind | string | No | Only build is accepted. Sections are created by moving a task into one, or by creating a task with section. One of: build. |
tasks | array of TaskCreate | No |
Responses:
| Status | Meaning |
|---|---|
| 201 | Created phase (Phase) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/phases/{id}
Get a phase with its tasks and subtasks
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Phase (Phase) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
PATCH /api/phases/{id}
Update a phase (set project_id to move it)
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required): PhaseUpdate.
| Field | Type | Required | Description |
|---|---|---|---|
project_id | ParentId | No | From 1 to 2147483647. |
name | Name | No | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
kind | PhaseKind | No | build for a build phase, otherwise the section kind. One of: build, regular, awaiting_owner, awaiting_response. Default "build". |
Responses:
| Status | Meaning |
|---|---|
| 200 | Updated phase (Phase) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
DELETE /api/phases/{id}
Delete a phase and everything under it
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Responses:
| Status | Meaning |
|---|---|
| 204 | Deleted |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/phases/{id}/tasks
List a phase's tasks (with subtasks)
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Tasks, ordered by position then id (array of Task) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/phases/{id}/tasks
Add a task to a phase, optionally with nested subtasks
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required): TaskCreate.
| Field | Type | Required | Description |
|---|---|---|---|
phase_id | ParentId | No | From 1 to 2147483647. |
name | Name | Yes | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
subtasks | array of SubtaskCreate | No |
Responses:
| Status | Meaning |
|---|---|
| 201 | Created task (Task) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/tasks
Create a task (parent given in the body)
Give either phase_id, or project_id and section to create the task in one of the project's sections (created if needed).
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required)
Example:
{
"phase_id": 3,
"name": "Home page"
}
Responses:
| Status | Meaning |
|---|---|
| 201 | Created task (Task) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/tasks/{id}
Get a task with its subtasks
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Task (Task) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
PATCH /api/tasks/{id}
Update a task (set phase_id to move it)
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required): TaskUpdate.
| Field | Type | Required | Description |
|---|---|---|---|
phase_id | ParentId | No | From 1 to 2147483647. |
name | Name | No | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
Example:
{
"status": "done"
}
Responses:
| Status | Meaning |
|---|---|
| 200 | Updated task (Task) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
DELETE /api/tasks/{id}
Delete a task and its subtasks
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Responses:
| Status | Meaning |
|---|---|
| 204 | Deleted |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/tasks/by-key/{key}
Get a task by its short key
Finds the task with this key in the caller's organisation, for links from branch names and commit messages. A task moved to another project has that project's key and number, so its old key finds nothing.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
key | path | string | Yes | A project's task key, a hyphen and the task's number, for example AF-12. Case does not matter. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Task (Task) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/tasks/{id}/move
Move a task into a section, or to a phase
Send section to move the task into that section of its own project (created the first time), or phase_id to move it to a phase (a section must belong to the task's project). The task keeps its status, subtasks and history; the move is logged like a PATCH of phase_id. Going into a section from a build phase records that phase in origin_phase_id; moving between sections keeps it; going to a build phase clears it. Moving a task to where it already is changes nothing.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required): TaskMove.
| Field | Type | Required | Description |
|---|---|---|---|
section | SectionKind | No | regular (Regular tasks), awaiting_owner (Awaiting owner) or awaiting_response (Awaiting response). One of: regular, awaiting_owner, awaiting_response. |
phase_id | ParentId | No | From 1 to 2147483647. |
Example:
{
"section": "awaiting_owner"
}
Responses:
| Status | Meaning |
|---|---|
| 200 | The moved task (Task) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/tasks/{id}/return
Return a task to its build phase
Moves the task back to the build phase it came from (origin_phase_id), or to the build phase given as phase_id, which must be in the same project. A 400 names phase_id when the task has no origin (it was created in the section, or its phase was deleted) and none was given.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json).
| Field | Type | Required | Description |
|---|---|---|---|
phase_id | ParentId | No | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 200 | The returned task (Task) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/tasks/{id}/comments
Comments on a task (also /api/subtasks/{id}/comments and /api/issues/{id}/comments)
Only when the server has COMMENTS=on; otherwise this route does not exist. Oldest first. body_html is rendered on the server from body: raw HTML is shown as text and only web, mail and same-site links are links.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 200 | The comments (array of Comment) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/tasks/{id}/comments
Add a comment
Written by a signed-in member. A key that is not tied to a person gets a 400. The activity log records that a comment was added, never its text.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
body | string | Yes | Markdown. At least 1 character. Up to 10000 characters. |
mention_user_ids | array of integer | No | Current members of the organisation to mention (anyone else is a 400 and nothing is saved). Each gets an email only when the server has WORK_EMAILS=on and they have not switched these emails off: fixed subject, who mentioned them and a link to the task, never the comment's text. The author is never emailed. Up to 20 items. |
Responses:
| Status | Meaning |
|---|---|
| 201 | The comment (Comment) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
PATCH /api/comments/{id}
Edit a comment (its author only)
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
body | string | Yes | At least 1 character. Up to 10000 characters. |
Responses:
| Status | Meaning |
|---|---|
| 200 | The comment, with edited_at set (Comment) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 403 | Only the author can edit a comment |
| 404 | Item (or its parent) does not exist (Error) |
DELETE /api/comments/{id}
Delete a comment (its author, or an admin)
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 204 | Deleted |
| 401 | Missing or invalid API key (Error) |
| 403 | Only the author or an admin can delete a comment |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/search
Search projects, phases, tasks and subtasks
Items of the caller's organisation whose name or description contains the text, any case (% and _ are plain characters). Name matches come first, then projects before phases before tasks before subtasks.
A query can also name an item by its ID, and that item comes first with matched id: id:123 (or #123) is anything with that ID and project:123, phase:123, task:123 or subtask:123 one kind, each returning only the exact match; P123, T123 and S123 (the badges the app shows for phases, tasks and subtasks) and a task key such as AF-12 return the exact match followed by any text matches.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
q | query | string | Yes | 2 to 100 characters. At least 2 characters. Up to 100 characters. |
limit | query | integer | No | Default 50. From 1 to 100. |
Responses:
| Status | Meaning |
|---|---|
| 200 | The matches |
GET /api/labels
The organisation's labels
Responses:
| Status | Meaning |
|---|---|
| 200 | Labels by name, with how many tasks carry each (array of Label) |
| 401 | Missing or invalid API key (Error) |
POST /api/labels
Add a label
Request body (application/json, required): LabelInput.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Unique in the organisation. At least 1 character. Up to 40 characters. |
color | LabelColor | No | One of: gray, red, orange, yellow, green, teal, blue, purple, pink. |
Example:
{
"name": "Bug",
"color": "red"
}
Responses:
| Status | Meaning |
|---|---|
| 201 | The new label (Label) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
PATCH /api/labels/{id}
Rename or recolour a label
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Request body (application/json, required): LabelInput.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Unique in the organisation. At least 1 character. Up to 40 characters. |
color | LabelColor | No | One of: gray, red, orange, yellow, green, teal, blue, purple, pink. |
Responses:
| Status | Meaning |
|---|---|
| 200 | The label (Label) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
DELETE /api/labels/{id}
Delete a label (admins); it comes off every task
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 204 | Deleted |
| 401 | Missing or invalid API key (Error) |
| 403 | Admins only |
| 404 | Item (or its parent) does not exist (Error) |
PUT /api/tasks/{id}/labels
Replace the labels on a task
The task ends up with exactly these labels (an empty list clears them). The change is logged in the activity. A PATCH of the task ignores labels.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
label_ids | array of integer | Yes | Up to 50 items. |
Example:
{
"label_ids": [
3,
5
]
}
Responses:
| Status | Meaning |
|---|---|
| 200 | The task with its labels (Task) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
PUT /api/tasks/{id}/repeat
Make a task repeat, or change how it repeats
The task needs a due date: the pattern counts from it. When the task is marked done, by any route (a PATCH, a bulk change, the connector), the next one is made in the same transaction: a copy with the same name, description, priority, owner, labels and subtasks (set back to to do), due on the first date of the pattern after both the finished task's due date and today (so finishing late never makes a heap of missed ones), with the start date moved by the same distance. Cancelling a task makes no next one and does not stop the series. No email is ever sent. The date a task is made for is claimed in the database first, so the same date is never made twice. Dates are the UTC date. At most 200 repeating tasks per organisation.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
freq | string | Yes | One of: daily, weekly, monthly. |
every_n | integer | No | Default 1. From 1 to 52. |
weekdays | array of integer | No | Weekly only: 1 Monday to 7 Sunday. Defaults to the weekday of the due date. |
month_day | integer | No | Monthly only: a shorter month takes its last day. Defaults to the day of the due date. From 1 to 31. |
ends_on | string or null (date) | No | No new task is made for a date after this. |
Example:
{
"freq": "weekly",
"weekdays": [
1,
4
],
"ends_on": "2027-01-01"
}
Responses:
| Status | Meaning |
|---|---|
| 200 | The task with its repeat (Task) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
DELETE /api/tasks/{id}/repeat
Stop a task repeating
The tasks already made stay; no new one is made. Nothing happens if the task did not repeat.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Responses:
| Status | Meaning |
|---|---|
| 200 | The task, with repeat null (Task) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/projects/{id}/export.csv
The project's tasks and subtasks as a CSV file
UTF-8 with a byte order mark, CRLF line ends. Columns Key, Type (task or subtask), Phase, Section, Name, Status, Priority, Owner, Start, Due, Labels, Description. A cell starting with = + - @ tab or carriage return gets a single quote in front. At most 50,000 rows.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes |
Responses:
| Status | Meaning |
|---|---|
| 200 | The file |
| 404 | Item (or its parent) does not exist (Error) |
| 413 | More than 50 |
POST /api/projects/{id}/import/preview
Check a CSV file of tasks for import (admins and up); writes nothing
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | |
phase_id | query | integer | Yes | A build phase of the project. |
Request body (text/csv, required)
Responses:
| Status | Meaning |
|---|---|
| 200 | ok, rows, skipped_subtasks, errors (row, column, message; never cell content), total_errors, sample (the first 20 tasks) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 404 | Item (or its parent) does not exist (Error) |
| 413 | The file is over 1 MB |
POST /api/projects/{id}/import
Import the tasks of a CSV file, all or none (admins and up)
The file is read and checked again here. Up to 2,000 rows. One import at a time per organisation and 10 an hour. No email; one activity line; the file is not kept.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | |
phase_id | query | integer | Yes |
Request body (text/csv, required)
Responses:
| Status | Meaning |
|---|---|
| 201 | Imported: { imported: n } |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 404 | Item (or its parent) does not exist (Error) |
| 409 | Another import is running for this organisation |
| 413 | The file is over 1 MB |
| 429 | More than 10 imports in the last hour |
PUT /api/tasks/{id}/blocked-by
Replace the tasks this task waits on
The task ends up waiting on exactly these tasks (an empty list clears them), up to 20, all in the same project. A task cannot wait on itself, and a loop (it would wait on a task that already waits on it, directly or through others) is refused with a 400. The change is logged in the activity. Nothing is blocked by the server: a task that waits on another can still be changed; the web app shows a "Blocked by" tag while any of them is not done or cancelled.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
task_ids | array of integer | Yes | Up to 20 items. |
Example:
{
"task_ids": [
12,
15
]
}
Responses:
| Status | Meaning |
|---|---|
| 200 | The task with its blocked_by (Task) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/my-work
The signed-in member's own tasks in every project
Open tasks whose owner_user_id is the caller, in build phases and sections alike, soonest due first (tasks with no due date last). section is build for a task in a build phase, otherwise its section. Needs a person: an environment key or the shared password is not one and gets a 400.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
include_closed | query | boolean | No | Also return done and cancelled tasks. Default false. |
label_id | query | integer | No | Only tasks carrying this label. From 1 to .... |
Responses:
| Status | Meaning |
|---|---|
| 200 | Tasks with their phase or section and project |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
POST /api/phases/{id}/reorder-tasks
Put a phase's tasks in a new order
task_ids must be exactly the phase's tasks, each once. Their position becomes 0, 1, 2 ... in that order, in one transaction, and the change is logged once on the phase (nothing is logged when the order is the same). Works on sections too. The web board's drag and drop uses it.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
task_ids | array of integer | Yes |
Example:
{
"task_ids": [
5,
3,
4
]
}
Responses:
| Status | Meaning |
|---|---|
| 200 | The phase with its tasks in the new order (Phase) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/tasks/bulk
One change to many tasks at once
Up to 100 distinct tasks, all or nothing in one transaction: each is changed (and logged) exactly as a PATCH of that task would be, and a label can be added or taken off without touching the task's other labels. set takes status, priority and due_date (null clears it); the owner is not part of it, so no assignment email is ever sent from here. An unknown task, a task of another organisation, a project that is switched off or any bad value stops the whole request with nothing changed. The web app's table uses it.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
task_ids | array of integer | Yes | Up to 100 items. |
set | object | No | |
add_label_id | integer | No | From 1 to .... |
remove_label_id | integer | No | From 1 to .... |
Example:
{
"task_ids": [
5,
3,
4
],
"set": {
"status": "done"
}
}
Responses:
| Status | Meaning |
|---|---|
| 200 | How many tasks changed |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/projects/{id}/views
The caller's own saved table views of a project
Private to the signed-in member. A key that is not tied to a person (an environment key or the shared password) gets a 400.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 200 | The views by name (array of SavedView) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/projects/{id}/views
Save a table view
Names are unique per person and project ignoring case; at most 30 per project. A missing config field takes its default; an unknown field or value is a 400 naming it.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | At least 1 character. Up to 60 characters. |
config | SavedViewConfig | Yes |
Example:
{
"name": "Blocked things",
"config": {
"status": "blocked",
"sort": "due",
"dir": "asc",
"group": "phase"
}
}
Responses:
| Status | Meaning |
|---|---|
| 201 | The saved view (SavedView) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
PATCH /api/views/{id}
Rename a saved view and/or replace its settings
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | At least 1 character. Up to 60 characters. |
config | SavedViewConfig | No |
Responses:
| Status | Meaning |
|---|---|
| 200 | The saved view (SavedView) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
DELETE /api/views/{id}
Delete a saved view
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 204 | Deleted |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/sections
Tasks in every project's sections
Tasks in the sections of every project (or one), ordered awaiting owner, awaiting response, regular, then by project and position. Build phases are never included. Open tasks only unless include_closed.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
kind | query | string | No | One section kind, or several separated by commas. |
project_id | query | integer | No | From 1 to .... |
label_id | query | integer | No | Only tasks carrying this label. From 1 to .... |
include_closed | query | boolean | No | Also return done and cancelled tasks. Default false. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Tasks with their section, project and origin phase (array of SectionTask) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
GET /api/tasks/{id}/subtasks
List a task's subtasks
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Subtasks, ordered by position then id (array of Subtask) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/tasks/{id}/subtasks
Add a subtask to a task
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required): SubtaskCreate.
| Field | Type | Required | Description |
|---|---|---|---|
task_id | ParentId | No | From 1 to 2147483647. |
name | Name | Yes | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
Responses:
| Status | Meaning |
|---|---|
| 201 | Created subtask (Subtask) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
POST /api/subtasks
Create a subtask (parent given in the body)
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required).
| Field | Type | Required | Description |
|---|---|---|---|
task_id | ParentId | No | From 1 to 2147483647. |
name | Name | Yes | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
Responses:
| Status | Meaning |
|---|---|
| 201 | Created subtask (Subtask) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/subtasks/{id}
Get a subtask
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Subtask (Subtask) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
PATCH /api/subtasks/{id}
Update a subtask (set task_id to move it)
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Request body (application/json, required): SubtaskUpdate.
| Field | Type | Required | Description |
|---|---|---|---|
task_id | ParentId | No | From 1 to 2147483647. |
name | Name | No | Trimmed; must not be blank. At least 1 character. Up to 300 characters. |
description | Description | No | Up to 20000 characters. |
status | Status | No | One of: todo, in_progress, blocked, done, cancelled. Default "todo". |
priority | Priority | No | One of: low, medium, high, critical. Default "medium". |
owner | Owner | No | Up to 200 characters. |
owner_user_id | OwnerUserId | No | The owner as a member of the organisation. Setting it fills owner with their name unless owner is sent too. |
start_date | DateOrNull | No | |
due_date | DateOrNull | No | |
position | Position | No | Sort order among siblings. New items are added at the end by default. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Updated subtask (Subtask) |
| 400 | The request body is invalid or is not valid JSON (Error) |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
DELETE /api/subtasks/{id}
Delete a subtask
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | integer | Yes | From 1 to 2147483647. |
X-Actor | header | string | No | Name recorded in the activity log (up to 200 characters, URI-encoded if not ASCII). |
Responses:
| Status | Meaning |
|---|---|
| 204 | Deleted |
| 401 | Missing or invalid API key (Error) |
| 404 | Item (or its parent) does not exist (Error) |
GET /api/dashboard
Open, overdue and due-soon task counts for each project
Counts only, for tasks in build phases that are not done or cancelled. Projects with no open task are left out. Overdue means due before today; due soon means due today up to 6 days ahead.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
today | query | string (date) | No | The caller's own date, YYYY-MM-DD. Defaults to the server's UTC date. |
Responses:
| Status | Meaning |
|---|---|
| 200 | Counts per project |
| 400 | today is not a date written YYYY-MM-DD |
| 401 | Missing or invalid API key (Error) |
GET /api/activity
Change history across all projects, newest first
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | integer | No | Default 50. From 1 to 500. |
before | query | integer | No | Return entries with an id lower than this (for paging). From 1 to .... |
project_id | query | integer | No | Only entries for this project. From 1 to .... |
Responses:
| Status | Meaning |
|---|---|
| 200 | Activity entries (array of Activity) |
| 401 | Missing or invalid API key (Error) |