Releases
Per-endpoint reference for the Releases resource. A release is a record bundling tasks within a single project, with a version, RC label, status / QA-status pair, and customizable status settings stored as JSON on the row. Tasks can be attached to and detached from a release via dedicated sub-resources. Each section below shows the exact path, every parameter, a full request example, and the response shape with a worked example.
Quickstart
Base URL
https://api.tasklife.com/v1/
Auth
All endpoints require Authorization: Bearer $API_KEY with an API key bound to your org. The key's user becomes the release's created_by on create and the added_by on task attach.
Envelope
Successful responses are { success: true, data: { ... } }. Errors return { success: false, error: { code, message } } with appropriate HTTP status. Per-endpoint response shapes are documented below.
Endpoints at a glance
| Action | Method & Path | Summary |
|---|---|---|
| List releases | GET /releases | List releases, optionally filtered by project, product, or status. |
| Create release | POST /releases | Create a new release for a project. |
| Get release | GET /releases/{id} | Get a release with its current task list. |
| Update release | PUT /releases/{id} | Update one or more fields on a release. |
| Delete release | DELETE /releases/{id} | Delete a release and detach all its tasks. |
| Attach task | POST /releases/{id}/tasks | Add a task to a release. |
| Detach task | DELETE /releases/{id}/tasks/{taskId} | Remove a task from a release. |
| Search tasks | GET /releases/{id}/tasks/search | Search tasks by title within a release's project. |
GET /releases
List releases for the org. By default, shipped / production / done / released releases are hidden — pass show_done=true to include them.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
project_id | integer | no | Filter to releases of a single project. |
product_id | integer | no | Filter to releases of a single product. |
status | string | no | Filter to a single status (e.g. in_progress). |
show_done | boolean | no | When true, include shipped/production/done/released releases. Defaults to false. |
Request
curl -sS "$BASE_URL/releases?project_id=42" \
-H "Authorization: Bearer $API_KEY"
Response 200 OK
| Field | Type | Description |
|---|---|---|
data.releases[] | array | Matching release records. |
data.releases[].id | integer | Release id. |
data.releases[].org_id | integer | Owning org id. |
data.releases[].project_id | integer | Project id. |
data.releases[].product_id | integer|null | Product id (nullable). |
data.releases[].name | string | Release name. |
data.releases[].version | string|null | Version label (e.g. "2.4"). |
data.releases[].rc_number | string|null | RC iteration label. |
data.releases[].status | string | Release status (free text; default seeded values are not_started/in_progress/passed/failed/shipped). |
data.releases[].qa_status | string | QA status (not_started/in_progress/passed/failed by default). |
data.releases[].comments | string|null | Free-form notes. |
data.releases[].project_name | string|null | Project display name (joined from projects). |
data.releases[].product_name | string|null | Product display name (joined from project_products). |
data.releases[].task_count | integer | Number of tasks attached to the release. |
data.releases[].created_at | string | MySQL datetime the release was created. |
data.releases[].updated_at | string | MySQL datetime the release was last updated. |
{
"success": true,
"data": {
"releases": [
{
"id": 17,
"org_id": 1,
"project_id": 42,
"product_id": 7,
"name": "v2.4 Feature Push",
"version": "2.4",
"rc_number": "1",
"status": "in_progress",
"qa_status": "not_started",
"comments": "RC1 — covers cards through Friday",
"project_name": "Web App",
"product_name": "Tasklife",
"task_count": 12,
"created_at": "2026-07-22 14:08:11",
"updated_at": "2026-07-26 09:31:02"
}
]
}
}
POST /releases
Create a new release. The authenticated user (from the API key) is recorded as created_by.
Body parameters
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
project_id | integer | no | most-recent project in org | Project id. A release is product-scoped; project_id is only used to populate the create-task modal's column-options on the edit page. If omitted, the API falls back to the caller's most-recent project in the org. May be null on a row with no project. |
name | string | yes | — | Human-readable release name. |
product_id | integer | no | null | Product id. Must belong to the org if set. |
version | string | no | null | Version label (e.g. "2.4"). |
rc_number | string | no | null | RC iteration label. Free-form; not auto-incremented. |
status | string | no | not_started | Initial release status. |
qa_status | string | no | not_started | Initial QA status. |
comments | string | no | null | Free-form notes. |
status_settings | object | no | seeded defaults | Override the customizable status settings stored as JSON on the row. Shape: { "statuses": [...], "qa_statuses": [...] }. |
Request
curl -sS -X POST "$BASE_URL/releases" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": 42,
"product_id": 7,
"name": "v2.4 Feature Push",
"version": "2.4",
"rc_number": "1",
"comments": "RC1 — covers cards through Friday"
}'
Response 201 Created
{
"success": true,
"data": {
"id": 17
}
}
Errors
| Status | When | What to do |
|---|---|---|
400 MISSING_FIELDS | name missing / empty. | Include name in the body. |
404 PROJECT_NOT_FOUND | project_id supplied but doesn't exist or isn't in this org. | Drop project_id to use the most-recent fallback, or use GET /projects to find a valid project id. |
404 PRODUCT_NOT_FOUND | product_id set but doesn't belong to this org. | Drop product_id or pick a valid one. |
GET /releases/{id}
Get a single release with its full task list.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | yes | Release id. |
Request
curl -sS "$BASE_URL/releases/17" \
-H "Authorization: Bearer $API_KEY"
Response 200 OK
| Field | Type | Description |
|---|---|---|
data.release | object | The release record (same fields as the list endpoint). |
data.release.tasks[] | array | Tasks attached to this release, newest-first. |
data.release.tasks[].id | integer | Task id. |
data.release.tasks[].title | string | Task title. |
data.release.tasks[].task_status | string | Task's own column-level status (independent of release status). |
data.release.tasks[].project_id | integer | Project the task lives in. |
data.release.tasks[].project_column_id | integer|null | Column the task sits in. |
data.release.tasks[].column_name | string|null | Column display name (e.g. "In Progress"). |
{
"success": true,
"data": {
"release": {
"id": 17,
"org_id": 1,
"project_id": 42,
"product_id": 7,
"name": "v2.4 Feature Push",
"version": "2.4",
"rc_number": "1",
"status": "in_progress",
"qa_status": "in_progress",
"comments": "RC1 — covers cards through Friday",
"status_settings": "{\"statuses\":[\"not_started\",\"in_progress\",\"passed\",\"failed\",\"shipped\"],\"qa_statuses\":[\"not_started\",\"in_progress\",\"passed\",\"failed\"]}",
"created_by": 8,
"created_at": "2026-07-22 14:08:11",
"updated_at": "2026-07-26 09:31:02",
"project_name": "Web App",
"product_name": "Tasklife",
"tasks": [
{
"id": 1284,
"title": "Add OAuth device grant endpoint",
"task_status": "Done",
"project_id": 42,
"project_column_id": 91,
"column_name": "Done"
},
{
"id": 1301,
"title": "Auto-bump APP_VERSION on CSS/JS edits",
"task_status": "In Progress",
"project_id": 42,
"project_column_id": 90,
"column_name": "In Progress"
}
]
}
}
}
Errors
| Status | When |
|---|---|
404 NOT_FOUND | Release id doesn't exist or belongs to a different org. |
PUT /releases/{id}
Update one or more fields on a release. Only the fields you include in the body are changed.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | yes | Release id. |
Body parameters
All optional. At least one field is required.
| Field | Type | Description |
|---|---|---|
name | string | Release name. Cannot be empty. |
product_id | integer | Product id. Pass 0 to clear. |
version | string | Version label. |
rc_number | string | RC iteration label. |
status | string | Release status. |
qa_status | string | QA status. |
comments | string | Free-form notes. |
status_settings | object | Replace the customizable status settings JSON. Shape: { statuses: [...], qa_statuses: [...] }. |
Request
curl -sS -X PUT "$BASE_URL/releases/17" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"rc_number": "2",
"status": "in_progress",
"comments": "RC2 — addressing feedback from RC1"
}'
Response 200 OK
{
"success": true
}
Errors
| Status | When |
|---|---|
400 NO_FIELDS | Body had no updatable fields. |
400 EMPTY_NAME | name sent as empty string. |
404 NOT_FOUND | Release doesn't exist in this org. |
404 PRODUCT_NOT_FOUND | product_id set to a non-org product. |
DELETE /releases/{id}
Delete a release. Cascades the release_tasks join rows and clears the release_id back-reference on every attached task so tasks aren't orphaned to a deleted id.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | yes | Release id. |
Request
curl -sS -X DELETE "$BASE_URL/releases/17" \
-H "Authorization: Bearer $API_KEY"
Response 200 OK
{ "success": true }
Errors
| Status | When |
|---|---|
404 NOT_FOUND | Release doesn't exist in this org. |
POST /releases/{id}/tasks
Attach a task to a release. The task must belong to the same project as the release. The task's own release_id field is set as a back-reference, so the task shows up under the release in the task UI too. Idempotent — re-adding an already-attached task is a no-op.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | yes | Release id. |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
task_id | integer | yes | Task id. Must belong to the release's project. |
Request
curl -sS -X POST "$BASE_URL/releases/17/tasks" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "task_id": 1301 }'
Response 200 OK
{ "success": true }
Errors
| Status | When |
|---|---|
400 MISSING_FIELDS | task_id missing or non-numeric. |
400 TASK_NOT_IN_PROJECT | Task is in a different project than the release. |
404 NOT_FOUND | Release id doesn't exist in this org. |
404 TASK_NOT_FOUND | Task id doesn't exist or isn't in this org. |
DELETE /releases/{id}/tasks/{taskId}
Detach a task from a release. Removes the release_tasks join row and clears the task's own release_id back-reference. Idempotent — no error if the task wasn't attached.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | yes | Release id. |
taskId | integer | yes | Task id to detach. |
Request
curl -sS -X DELETE "$BASE_URL/releases/17/tasks/1301" \
-H "Authorization: Bearer $API_KEY"
Response 200 OK
{ "success": true }
Errors
| Status | When |
|---|---|
404 NOT_FOUND | Release doesn't exist in this org. (Detaching an unattached task from a real release is a 200 no-op.) |
GET /releases/{id}/tasks/search
Search tasks by title within a release's project. Used by the in-app autocomplete picker; returns up to 20 matches, ordered by title ascending. Useful when you have a release id and want to find task ids to attach.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | yes | Release id. The release's project becomes the search scope. |
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
q | string | yes | Search string. Must be at least 2 characters. Substring match against task title. |
Request
curl -sS "$BASE_URL/releases/17/tasks/search?q=oauth" \
-H "Authorization: Bearer $API_KEY"
Response 200 OK
| Field | Type | Description |
|---|---|---|
data.tasks[] | array | Up to 20 matching tasks. |
data.tasks[].id | integer | Task id (use as task_id on attach). |
data.tasks[].title | string | Task title. |
data.tasks[].column_name | string|null | Column display name. |
{
"success": true,
"data": {
"tasks": [
{
"id": 1284,
"title": "Add OAuth device grant endpoint",
"column_name": "Done"
},
{
"id": 1411,
"title": "OAuth token refresh bug on slow clocks",
"column_name": "In Progress"
}
]
}
}
Errors
| Status | When |
|---|---|
400 QUERY_TOO_SHORT | q is empty or shorter than 2 characters. |
404 NOT_FOUND | Release id doesn't exist in this org. |