Projects

Per-endpoint reference for the Projects resource. Covers project CRUD plus the kanban-style columns (statuses) inside each project. 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)

Every request must include a bearer token in the Authorization header. The token is an API key bound to a specific organization — endpoints return only data scoped to that org.

Authorization: Bearer <yo...ode>
Content-Type: application/json

Response envelope

All successful responses share a common envelope. Error responses are described in Errors below.

{
  "success": true,
  "data":   { /* endpoint-specific payload */ }
}

Note: Projects endpoints nest results under data.project.<field> for single-project responses (get/create/update) and under data.projects[0].<field> for list. Column endpoints nest under data.column.<field> or data.columns[...]. Each endpoint below shows its actual response shape — trust that.

Errors

Failed responses always have success: false and an error object with a stable code and a human-readable message:

{
  "success": false,
  "error": {
    "code":    "API_ERROR",
    "message": "Task prefix must be 2-4 uppercase letters (A-Z)"
  }
}
HTTP statusWhenWhat to do
400Request body failed validation (missing required field, wrong type, empty name, invalid task_prefix, etc.).Read error.message; fix the offending field.
403API key is valid but the user lacks permission (e.g. only org admins may set agent_team_active).Confirm the user has org-admin role and the API key's org matches the resource's org.
404Project or column doesn't exist, or isn't visible to this org.Verify the ID and that the resource hasn't been deleted.
409task_prefix is already in use by another project in the same org.Read error.message and choose a different task_prefix.

Projects

Kanban-style projects. A project has a name, a unique short task_prefix used in task IDs (optional), a description (optional), a list of priority labels, and a set of named columns that tasks move through. New projects are seeded with three default columns: To Do, In Progress, Done.

Endpoints at a glance

ActionMethod + PathSummary
List projectsGET /projectsList all projects for the API key's org, sorted by name.
Create a projectPOST /projectsCreate a project and seed it with three default columns. Returns 201 with the full project + columns.
Get a projectGET /projects/{id}Fetch a single project including its columns array.
Update a projectPUT /projects/{id} or PATCH /projects/{id}Update one or more fields on a project. agent_team_active is admin-gated.
List columnsGET /projects/{id}/columnsList the columns (statuses) for a project, ordered by sort_order.
Create a columnPOST /projects/{id}/columnsAdd a column to a project. Returns 201 with the created column.
Update a columnPUT /projects/{id}/columns/{columnId} or PATCH ...Rename, reorder, or update lifecycle config on a column.
Delete a columnDELETE /projects/{id}/columns/{columnId}Delete a column. Returns 400 if the column still has tasks.

Heads up: there is no DELETE /v1/projects/{id} endpoint — projects themselves cannot be deleted via the API (only their columns can). The routing block at external-api/v1/index.php returns 404 for any unhandled projects path.

GET /projects

List every project that belongs to the API key's org. Results are sorted alphabetically by name. The returned projects do not include a columns array — call GET /projects/{id} to fetch a project with its columns.

Request example

curl -sS https://api.tasklife.com/v1/projects \
  -H "Authorization: Bearer $API_KEY"

Response 200 OK

The array is nested under data.projects[]:

FieldTypeDescription
data.projects[].idintegerProject id. Use this for follow-up get/update/columns calls.
data.projects[].namestringProject display name.
data.projects[].task_prefixstring | null2–4 uppercase letters used to prefix task IDs (e.g. WEB-12). null if not set.
data.projects[].descriptionstringFree-form description. Present only on installations that have the description column; older installs omit it.
data.projects[].agent_team_activeinteger (0 or 1)Whether the AI agent team is active for this project. 1 = on, 0 = off. Present only when the column exists; defaults to 1 when missing.
data.projects[].task_prioritiesarray<string>Available priority labels in order. Defaults to ["High", "Medium", "Low"] when task_priorities_json column is absent.
data.projects[].default_task_prioritystringPriority applied to new tasks by default. Must be one of task_priorities.
data.projects[].created_atstring (ISO 8601 datetime)Project creation timestamp.
data.projects[].updated_atstring (ISO 8601 datetime)Last update timestamp.
{
  "success": true,
  "data": {
    "projects": [
      {
        "id":                    42,
        "name":                  "Website Relaunch",
        "task_prefix":           "WEB",
        "description":           "Q2 marketing site redesign & launch.",
        "agent_team_active":     1,
        "task_priorities":       ["Critical", "High", "Medium", "Low"],
        "default_task_priority": "Medium",
        "created_at":            "2026-04-12T18:21:04Z",
        "updated_at":            "2026-06-29T14:08:51Z"
      },
      {
        "id":                    87,
        "name":                  "Mobile App",
        "task_prefix":           "MOB",
        "description":           "iOS + Android client work.",
        "agent_team_active":     0,
        "task_priorities":       ["High", "Medium", "Low"],
        "default_task_priority": "High",
        "created_at":            "2026-05-02T09:11:32Z",
        "updated_at":            "2026-06-15T22:00:09Z"
      }
    ]
  }
}

POST /projects

Create a new project. The service seeds the project with three default columns (To Do / In Progress / Done) and returns the full project including those columns.

Body parameters

FieldTypeRequiredDefaultDescription
name string yes — Project display name. Cannot be empty.
description string no "" Free-form description. Ignored on installations that lack the description column.
task_prefix string no null 2–4 uppercase letters (A–Z). Must be unique within the org — duplicates return 409.
task_priorities array<string> no ["High","Medium","Low"] Available priority labels, in display order. Duplicates and blank entries are stripped. Stored as JSON; ignored if task_priorities_json column is absent.
default_task_priority string no first entry of task_priorities Priority applied to new tasks by default. Must be one of task_priorities; otherwise it falls back to the first entry.

Request example

curl -sS -X POST https://api.tasklife.com/v1/projects \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":                 "Website Relaunch",
    "description":          "Q2 marketing site redesign & launch.",
    "task_prefix":          "WEB",
    "task_priorities":      ["Critical", "High", "Medium", "Low"],
    "default_task_priority": "Medium"
  }'

Response 201 Created

The created project (with its seeded columns) is nested under data.project:

FieldTypeDescription
data.project.idintegerNew project id.
data.project.namestringEcho of the name you sent.
data.project.task_prefixstring | nullEcho of the prefix (uppercased) or null.
data.project.descriptionstringEcho of the description (may be absent on older installs).
data.project.agent_team_activeinteger (0 or 1)Agent team toggle (defaults to 1; field may be absent on older installs).
data.project.task_prioritiesarray<string>Stored priority labels as a clean array.
data.project.default_task_prioritystringResolved default priority.
data.project.created_atstring (ISO 8601 datetime)Creation timestamp.
data.project.updated_atstring (ISO 8601 datetime)Update timestamp (== created_at on create).
data.project.columnsarrayDefault columns seeded in order: To Do, In Progress, Done. Each entry follows the column shape documented under List columns.
{
  "success": true,
  "data": {
    "project": {
      "id":                    142,
      "name":                  "Website Relaunch",
      "task_prefix":           "WEB",
      "description":           "Q2 marketing site redesign & launch.",
      "agent_team_active":     1,
      "task_priorities":       ["Critical", "High", "Medium", "Low"],
      "default_task_priority": "Medium",
      "created_at":            "2026-07-03T19:48:02Z",
      "updated_at":            "2026-07-03T19:48:02Z",
      "columns": [
        { "id": 501, "project_id": 142, "name": "To Do",       "sort_order": 1, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" },
        { "id": 502, "project_id": 142, "name": "In Progress", "sort_order": 2, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" },
        { "id": 503, "project_id": 142, "name": "Done",        "sort_order": 3, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" }
      ]
    }
  }
}

GET /projects/{id}

Fetch a single project by id, including its columns array ordered by sort_order. Returns 404 if the project doesn't exist or belongs to a different org.

Path parameters

FieldTypeRequiredDescription
idintegeryesProject id.

Request example

curl -sS https://api.tasklife.com/v1/projects/142 \
  -H "Authorization: Bearer $API_KEY"

Response 200 OK

The project is nested under data.project:

FieldTypeDescription
data.project.idintegerProject id.
data.project.namestringProject display name.
data.project.task_prefixstring | null2–4 letter prefix or null.
data.project.descriptionstringDescription (omitted on installs missing the column).
data.project.agent_team_activeinteger (0 or 1)Agent team toggle.
data.project.task_prioritiesarray<string>Available priority labels.
data.project.default_task_prioritystringDefault priority for new tasks.
data.project.created_atstring (ISO 8601 datetime)Creation timestamp.
data.project.updated_atstring (ISO 8601 datetime)Last update timestamp.
data.project.columnsarrayColumns in sort_order order. Each entry: { id, project_id, name, sort_order, lifecycle_enabled, lifecycle_start_target_column_id, lifecycle_blocked_target_column_id, lifecycle_complete_target_column_id, lifecycle_has_issues_target_column_id, lifecycle_get_prompt, lifecycle_put_prompt }. Lifecycle fields are present only when the lifecycle_* columns exist in project_columns.
{
  "success": true,
  "data": {
    "project": {
      "id":                    142,
      "name":                  "Website Relaunch",
      "task_prefix":           "WEB",
      "description":           "Q2 marketing site redesign & launch.",
      "agent_team_active":     1,
      "task_priorities":       ["Critical", "High", "Medium", "Low"],
      "default_task_priority": "Medium",
      "created_at":            "2026-07-03T19:48:02Z",
      "updated_at":            "2026-07-03T19:48:02Z",
      "columns": [
        { "id": 501, "project_id": 142, "name": "To Do",       "sort_order": 1, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" },
        { "id": 502, "project_id": 142, "name": "In Progress", "sort_order": 2, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" },
        { "id": 503, "project_id": 142, "name": "Done",        "sort_order": 3, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" }
      ]
    }
  }
}

PUT /projects/{id} (PATCH is also accepted)

Update one or more fields on a project. Omitted fields are unchanged. The request returns the updated project (same envelope as GET /projects/{id}).

Path parameters

FieldTypeRequiredDescription
idintegeryesProject id.

Body parameters

FieldTypeRequiredDefaultDescription
name string no — New project name. Trimmed; cannot be empty (returns 400 otherwise).
description string no — New description. Ignored on installs that lack the column.
task_prefix string no — 2–4 uppercase letters. Must be unique within the org (excluding this project); returns 409 on conflict.
task_priorities array<string> no — Replace the priority label set. Stored as a cleaned JSON array.
default_task_priority string no — New default priority. Must match one of the (current) task_priorities; otherwise falls back to the first entry.
agent_team_active integer (0 or 1) no — Toggle the AI agent team for this project. Admin-only: callers without org-admin role receive 403. Boolean true/false and the strings "1"/"0"/"true"/"yes"/"on"/"active" are also accepted.

Request example

curl -sS -X PUT https://api.tasklife.com/v1/projects/142 \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":          "Website Relaunch (Q2)",
    "task_prefix":   "WEB",
    "agent_team_active": 1
  }'

Response 200 OK

Returns the updated project nested under data.project — identical envelope shape to GET /projects/{id}:

FieldTypeDescription
data.project.<field>mixedSame fields as the get response. updated_at is bumped; columns reflects the current column list (unchanged).
{
  "success": true,
  "data": {
    "project": {
      "id":                    142,
      "name":                  "Website Relaunch (Q2)",
      "task_prefix":           "WEB",
      "description":           "Q2 marketing site redesign & launch.",
      "agent_team_active":     1,
      "task_priorities":       ["Critical", "High", "Medium", "Low"],
      "default_task_priority": "Medium",
      "created_at":            "2026-07-03T19:48:02Z",
      "updated_at":            "2026-07-03T20:15:11Z",
      "columns": [
        { "id": 501, "project_id": 142, "name": "To Do",       "sort_order": 1, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" },
        { "id": 502, "project_id": 142, "name": "In Progress", "sort_order": 2, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" },
        { "id": 503, "project_id": 142, "name": "Done",        "sort_order": 3, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" }
      ]
    }
  }
}

GET /projects/{id}/columns

List the columns (statuses) for a project, ordered by sort_order (then id as a tiebreaker).

Path parameters

FieldTypeRequiredDescription
idintegeryesProject id.

Request example

curl -sS https://api.tasklife.com/v1/projects/142/columns \
  -H "Authorization: Bearer $API_KEY"

Response 200 OK

The array is nested under data.columns[]:

FieldTypeDescription
data.columns[].idintegerColumn id.
data.columns[].project_idintegerOwning project id.
data.columns[].namestringColumn display name.
data.columns[].sort_orderintegerDisplay order (1-based). Tasks move left-to-right.
data.columns[].lifecycle_enabledinteger (0 or 1)Whether AI agent lifecycle automation is enabled for this column. Present only when the project_columns table has the lifecycle columns; defaults to 0 on older installs.
data.columns[].lifecycle_start_target_column_idinteger | nullColumn id to move tasks into when an agent "starts" work.
data.columns[].lifecycle_blocked_target_column_idinteger | nullColumn id to move tasks into when an agent reports blocked.
data.columns[].lifecycle_complete_target_column_idinteger | nullColumn id to move tasks into when an agent completes work.
data.columns[].lifecycle_has_issues_target_column_idinteger | nullColumn id to move tasks into when an agent reports issues.
data.columns[].lifecycle_get_promptstringPrompt template used by the agent when fetching work from this column.
data.columns[].lifecycle_put_promptstringPrompt template used by the agent when reporting work back from this column.
{
  "success": true,
  "data": {
    "columns": [
      { "id": 501, "project_id": 142, "name": "To Do",       "sort_order": 1, "lifecycle_enabled": 1, "lifecycle_start_target_column_id": 502,   "lifecycle_blocked_target_column_id": 504, "lifecycle_complete_target_column_id": 503, "lifecycle_has_issues_target_column_id": 504, "lifecycle_get_prompt": "Read the task and start.",    "lifecycle_put_prompt": "Summarize what you started." },
      { "id": 502, "project_id": 142, "name": "In Progress", "sort_order": 2, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" },
      { "id": 503, "project_id": 142, "name": "Done",        "sort_order": 3, "lifecycle_enabled": 0, "lifecycle_start_target_column_id": null, "lifecycle_blocked_target_column_id": null, "lifecycle_complete_target_column_id": null, "lifecycle_has_issues_target_column_id": null, "lifecycle_get_prompt": "", "lifecycle_put_prompt": "" }
    ]
  }
}

POST /projects/{id}/columns

Add a new column to a project. Required: name. If sort_order is omitted, the new column is appended at MAX(sort_order) + 1.

Path parameters

FieldTypeRequiredDescription
idintegeryesProject id.

Body parameters

FieldTypeRequiredDefaultDescription
name string yes — Column display name. Trimmed; cannot be empty (returns 400).
sort_order integer no MAX(sort_order) + 1 Display order. Non-numeric values return 400.
lifecycle_enabled integer (0 or 1) no 0 Toggle AI lifecycle automation for this column. Stored only when the lifecycle columns exist.
lifecycle_start_target_column_id integer | null no null Column id to move tasks into on start. Must belong to this project.
lifecycle_blocked_target_column_id integer | null no null Column id to move tasks into when blocked. Must belong to this project.
lifecycle_complete_target_column_id integer | null no null Column id to move tasks into on complete. Must belong to this project.
lifecycle_has_issues_target_column_id integer | null no null Column id to move tasks into on has issues. Must belong to this project.
lifecycle_get_prompt string no "" Prompt template used by the agent when fetching work from this column.
lifecycle_put_prompt string no "" Prompt template used by the agent when reporting work back from this column.

Request example

curl -sS -X POST https://api.tasklife.com/v1/projects/142/columns \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":                 "In Review",
    "sort_order":           4,
    "lifecycle_enabled":    1,
    "lifecycle_complete_target_column_id": 503,
    "lifecycle_has_issues_target_column_id": 504,
    "lifecycle_get_prompt": "Fetch the task assigned to you.",
    "lifecycle_put_prompt": "Report progress or completion."
  }'

Response 201 Created

The created column is nested under data.column:

FieldTypeDescription
data.column.idintegerNew column id.
data.column.project_idintegerOwning project id.
data.column.namestringEcho of the column name.
data.column.sort_orderintegerResolved sort order.
data.column.<lifecycle_*>mixedEcho of the lifecycle fields (present only on installs with the lifecycle columns).
{
  "success": true,
  "data": {
    "column": {
      "id":                                 504,
      "project_id":                         142,
      "name":                               "In Review",
      "sort_order":                         4,
      "lifecycle_enabled":                  1,
      "lifecycle_start_target_column_id":   null,
      "lifecycle_blocked_target_column_id": null,
      "lifecycle_complete_target_column_id": 503,
      "lifecycle_has_issues_target_column_id": 504,
      "lifecycle_get_prompt":               "Fetch the task assigned to you.",
      "lifecycle_put_prompt":               "Report progress or completion."
    }
  }
}

PUT /projects/{id}/columns/{columnId} (PATCH is also accepted)

Update fields on a column. Omitted fields are unchanged. Same lifecycle fields as POST. Returns the updated column nested under data.column.

Path parameters

FieldTypeRequiredDescription
idintegeryesProject id.
columnIdintegeryesColumn id.

Body parameters

FieldTypeRequiredDefaultDescription
name string no — Renamed column. Trimmed; cannot be empty (returns 400).
sort_order integer no — New display order. Non-numeric values return 400.
lifecycle_enabled integer (0 or 1) no — Toggle AI lifecycle automation.
lifecycle_start_target_column_id integer | null no — Column id for start transitions. Must belong to this project; sending null clears the field.
lifecycle_blocked_target_column_id integer | null no — Column id for blocked transitions.
lifecycle_complete_target_column_id integer | null no — Column id for complete transitions.
lifecycle_has_issues_target_column_id integer | null no — Column id for has issues transitions.
lifecycle_get_prompt string no — Prompt template used by the agent when fetching work from this column.
lifecycle_put_prompt string no — Prompt template used by the agent when reporting work back from this column.

Request example

curl -sS -X PUT https://api.tasklife.com/v1/projects/142/columns/504 \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":              "Code Review",
    "lifecycle_enabled": 0
  }'

Response 200 OK

FieldTypeDescription
data.column.idintegerColumn id (unchanged).
data.column.project_idintegerProject id (unchanged).
data.column.<field>mixedReflects the merged state (changed fields + preserved fields).
{
  "success": true,
  "data": {
    "column": {
      "id":                                 504,
      "project_id":                         142,
      "name":                               "Code Review",
      "sort_order":                         4,
      "lifecycle_enabled":                  0,
      "lifecycle_start_target_column_id":   null,
      "lifecycle_blocked_target_column_id": null,
      "lifecycle_complete_target_column_id": 503,
      "lifecycle_has_issues_target_column_id": 504,
      "lifecycle_get_prompt":               "Fetch the task assigned to you.",
      "lifecycle_put_prompt":               "Report progress or completion."
    }
  }
}

DELETE /projects/{id}/columns/{columnId}

Permanently delete a column. Returns 400 when the column still has tasks — move or delete the tasks first. Returns 404 if the column doesn't exist or isn't in this project.

Path parameters

FieldTypeRequiredDescription
idintegeryesProject id.
columnIdintegeryesColumn id.

Request example

curl -sS -X DELETE https://api.tasklife.com/v1/projects/142/columns/504 \
  -H "Authorization: Bearer $API_KEY"

Response 200 OK

FieldTypeDescription
data.deletedbooleanAlways true on success.
{
  "success": true,
  "data": { "deleted": true }
}