Work items and resources
Issues, pages, plans, comments, attachments, exports, search, and realtime events.
Issues
GET /api/issues
Lists issues. Query parameters are project_id (integer), status (string), priority (string), module_id (integer), label (string), workable (boolean), blocked (boolean), created_since (string), created_until (string), updated_since (string), updated_until (string), order_by (string), order (string), limit (integer), and offset (integer). Every query parameter is optional.
order_by accepts sort_order, sequence, created, created_at, updated, updated_at, or priority. order accepts asc or desc. The since bounds are inclusive. The until bounds are exclusive.
It has no request body. Response: an Issue array.
POST /api/issues
Creates an issue. This endpoint has no path or query parameters.
Request body fields are project_id (integer), title (string), description (string, optional, defaults to an empty string), status (string, optional, defaults to backlog), priority (string, optional, defaults to none), module_id (integer or null, optional), start_date (string or null, optional), target_date (string or null, optional), labels (string array, optional), and source (string or null, optional).
Response: an Issue object.
curl -X POST https://your-server.example/api/issues \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"project_id":1,"title":"Document the REST API"}'{
"id": 1,
"project_id": 1,
"sequence": 1,
"identifier": "LIF-1",
"title": "Document the REST API",
"description": "",
"status": "backlog",
"priority": "none",
"module_id": null,
"sort_order": 0,
"start_date": null,
"target_date": null,
"created_at": "2026-07-14 12:00:00",
"updated_at": "2026-07-14 12:00:00",
"labels": []
}GET /api/issues/{id}
Returns an issue with labels and relations. Path parameter: id (integer issue ID). It has no query parameters or request body.
Response: an Issue object.
PUT /api/issues/{id}
Updates supplied issue fields. Path parameter: id (integer issue ID). It has no query parameters.
Request body fields are title (string, optional), description (string, optional), status (string, optional), priority (string, optional), module_id (integer, null, or omitted), sort_order (number, optional), start_date (string or null, optional), target_date (string or null, optional), and labels (string array, optional). An omitted module_id is unchanged. A null module_id clears the assignment. Supplied labels replace the issue label set.
Response: an Issue object.
DELETE /api/issues/{id}
Deletes an issue. Path parameter: id (integer issue ID). It has no query parameters or request body.
Response: the standard delete response.
GET /api/issues/resolve/{identifier}
Resolves an issue identifier such as LIF-1. Path parameter: identifier (string). It has no query parameters or request body.
Response: an Issue object.
curl https://your-server.example/api/issues/resolve/LIF-1 \
-H "Authorization: Bearer <API_KEY>"{
"id": 1,
"identifier": "LIF-1",
"title": "Document the REST API",
"status": "backlog",
"priority": "none",
"labels": [],
"blocks": [],
"blocked_by": [],
"relates_to": [],
"duplicates": [],
"duplicated_by": []
}POST /api/issues/link
Links two issues by identifier. This endpoint has no path or query parameters.
Request body fields are source (string issue identifier), target (string issue identifier), and relation_type (string). relation_type must be blocks, relates_to, or duplicate. Response: { "linked": true }.
POST /api/issues/unlink
Removes the relation between two issues. This endpoint has no path or query parameters.
Request body fields are source (string issue identifier) and target (string issue identifier). Response: { "unlinked": true }.
GET /api/issues/{id}/activity
Returns activity for an issue. Path parameter: id (integer issue ID). Query parameters are limit (integer, optional) and offset (integer, optional).
It has no request body. Response: an activity feed with the shape documented for project activity.
Comments
GET /api/issues/{issue_id}/comments
Lists comments on an issue. Path parameter: issue_id (integer). Query parameters are author (string), order (string, asc or desc), limit (integer), and offset (integer). Every query parameter is optional.
It has no request body. Response: a Comment array.
POST /api/issues/{issue_id}/comments
Creates a comment on an issue. Path parameter: issue_id (integer). It has no query parameters.
Request body field: content (string). Response: a Comment object.
GET /api/pages/{page_id}/comments
Lists comments on a page. Path parameter: page_id (integer). Query parameters are author (string), order (string, asc or desc), limit (integer), and offset (integer). Every query parameter is optional.
It has no request body. Response: a Comment array.
POST /api/pages/{page_id}/comments
Creates a comment on a page. Path parameter: page_id (integer). It has no query parameters.
Request body field: content (string). Response: a Comment object.
PUT /api/comments/{id}
Updates a comment. Path parameter: id (integer comment ID). It has no query parameters. Only the comment author or an administrator can update it.
Request body field: content (string). Response: a Comment object.
DELETE /api/comments/{id}
Deletes a comment. Path parameter: id (integer comment ID). It has no query parameters or request body. Only the comment author or an administrator can delete it.
Response: the standard delete response.
Modules, labels, and folders
GET /api/modules
Lists modules for a project. Query parameter: project_id (integer, required). It has no path parameters or request body.
Response: a Module array.
POST /api/modules
Creates a module. This endpoint has no path or query parameters.
Request body fields are project_id (integer), name (string), description (string, optional, defaults to an empty string), status (string, optional, defaults to active), and emoji (string or null, optional). Response: a Module object.
GET /api/modules/{id}
Returns a module. Path parameter: id (integer module ID). It has no query parameters or request body.
Response: a Module object.
PUT /api/modules/{id}
Updates supplied module fields. Path parameter: id (integer module ID). It has no query parameters.
Request body fields are name (string, optional), description (string, optional), status (string, optional), and emoji (string, null, or omitted). Response: a Module object.
DELETE /api/modules/{id}
Deletes a module. Path parameter: id (integer module ID). It has no query parameters or request body.
Response: the standard delete response.
GET /api/labels
Lists labels for a project. Query parameter: project_id (integer, required). It has no path parameters or request body.
Response: a Label array.
POST /api/labels
Creates a label. This endpoint has no path or query parameters.
Request body fields are project_id (integer), name (string), and color (string, optional, defaults to #6B7280). Response: a Label object.
PUT /api/labels/{id}
Updates supplied label fields. Path parameter: id (integer label ID). It has no query parameters.
Request body fields are name (string, optional) and color (string, optional). Response: a Label object.
DELETE /api/labels/{id}
Deletes a label. Path parameter: id (integer label ID). It has no query parameters or request body.
Response: the standard delete response.
POST /api/labels/{id}/merge
Moves source label associations to a target label in the same project. Path parameter: id (integer source label ID). It has no query parameters.
Request body field: into (integer target label ID). Response: the surviving Label object.
GET /api/folders
Lists folders for a project. Query parameter: project_id (integer, required). It has no path parameters or request body.
Response: a Folder array.
POST /api/folders
Creates a folder. This endpoint has no path or query parameters.
Request body fields are project_id (integer), parent_id (integer or null, optional), and name (string). Response: a Folder object.
DELETE /api/folders/{id}
Deletes a folder. Path parameter: id (integer folder ID). It has no query parameters or request body.
Response: the standard delete response.
Pages
GET /api/pages
Lists pages. Query parameters are project_id (integer), folder_id (integer), label (string), status (string), order_by (string), order (string), limit (integer), and offset (integer). Every query parameter is optional.
When project_id is omitted, this endpoint lists workspace pages. order_by accepts sort_order, title, status, created, created_at, updated, or updated_at. order accepts asc or desc.
It has no request body. Response: a Page array.
POST /api/pages
Creates a page. This endpoint has no path or query parameters.
Request body fields are project_id (integer or null, optional), folder_id (integer or null, optional), title (string), content (string, optional, defaults to an empty string), status (string, optional, defaults to draft), and labels (string array, optional). Response: a Page object.
GET /api/pages/{id}
Returns a page. Path parameter: id (integer page ID). It has no query parameters or request body.
Response: a Page object.
PUT /api/pages/{id}
Updates supplied page fields. Path parameter: id (integer page ID). It has no query parameters.
Request body fields are title (string, optional), content (string, optional), folder_id (integer, null, or omitted), sort_order (number, optional), status (string, optional), pinned (boolean, optional), and labels (string array, optional). Supplied labels replace the page label set.
Response: a Page object.
DELETE /api/pages/{id}
Deletes a page. Path parameter: id (integer page ID). It has no query parameters or request body.
Response: the standard delete response.
GET /api/pages/{id}/activity
Returns activity for a page. Path parameter: id (integer page ID). Query parameters are limit (integer, optional) and offset (integer, optional).
It has no request body. Response: an activity feed with the shape documented for project activity.
Plans
GET /api/plans
Lists plans without their nested step trees. Query parameters are project_id (integer), status (string), limit (integer), offset (integer), order_by (string), and before_id (integer). Every query parameter is optional.
order_by accepts updated or id. before_id requires order_by=id. It has no request body. Response: a plan array. Each list item has id, project_id, sequence, identifier, issue_id, anchor_identifier, title, status, created_at, updated_at, steps (empty array), step_count (integer), and done_count (integer).
POST /api/plans
Creates a plan and an optional nested step tree. This endpoint has no path or query parameters.
Request body fields are project_id (integer), title (string), issue_id (integer or null, optional), and steps (array, optional). Each step has title (string), description (string, optional), issue_id (integer or null, optional), done (boolean, optional), and steps (step array, optional).
Response: a plan object with the nested steps tree. Each step node has id (integer), plan_id (integer), parent_step_id (integer or null), position (integer), title (string), description (string), issue_id (integer or null), issue_identifier (string or null), issue_status (string or null), done (boolean), reopened_via_issue_at (string or null), created_at (string), edited_at (string or null), and children (step array).
GET /api/plans/{id}
Returns a plan with its nested steps. Path parameter: id (integer plan ID). It has no query parameters or request body.
Response: a plan object with the full step tree.
PUT /api/plans/{id}
Updates supplied plan fields. Path parameter: id (integer plan ID). It has no query parameters.
Request body fields are title (string, optional), status (string, optional), and issue_id (integer, null, or omitted). Valid plan statuses are active, done, and archived. Response: a plan object with the full step tree.
DELETE /api/plans/{id}
Deletes a plan. Path parameter: id (integer plan ID). It has no query parameters or request body.
Response: the standard delete response.
GET /api/plans/resolve/{identifier}
Resolves a plan identifier such as LIF-PLAN-1. Path parameter: identifier (string). It has no query parameters or request body.
Response: a plan object with the full step tree.
GET /api/plans/{id}/activity
Returns activity for a plan and its steps. Path parameter: id (integer plan ID). Query parameters are limit (integer, optional) and offset (integer, optional).
It has no request body. Response: an activity feed with the shape documented for project activity.
POST /api/plans/{id}/steps
Adds a step to a plan. Path parameter: id (integer plan ID). It has no query parameters.
Request body fields are parent_step_id (integer or null, optional), title (string), description (string, optional, defaults to an empty string), and issue_id (integer or null, optional). Response: a plan object with the full step tree.
PUT /api/plans/{plan_id}/steps/{step_id}
Updates, moves, links, or completes a plan step. Path parameters are plan_id (integer) and step_id (integer). It has no query parameters.
Request body fields are title (string, optional), description (string, optional), done (boolean, optional), issue_id (integer, null, or omitted), move_parent_step_id (integer, optional), move_to_root (boolean, optional), and move_position (integer, optional).
Response: an object with plan (a plan object with the full step tree). When the request supplies done, the response also includes effect, an object with step_id (integer), done (boolean), issue_identifier (string or null), issue_status_changed (boolean), and issue_new_status (string or null). Otherwise, effect is omitted.
DELETE /api/plans/{plan_id}/steps/{step_id}
Deletes a plan step. Path parameters are plan_id (integer) and step_id (integer). It has no query parameters or request body.
Response: a plan object with the full step tree.
Attachments
GET /api/attachments
Lists attachments linked to one entity. Query parameters are entity_type (string, required) and entity_id (integer, required). entity_type accepts issue, page, or comment.
It has no path parameters or request body. Response: an attachment array. Each attachment has id (integer), filename (string), mime (string), size_bytes (integer), uploader_id (integer or null), and created_at (string).
POST /api/attachments
Uploads a file part in a multipart request. Optional entity_type and entity_id form fields immediately link the attachment to an issue, page, or comment.
Form fields are file (file, required), entity_type (string, optional), and entity_id (integer, optional). Response: an object with id (integer), url (string), filename (string), mime (string), and size (integer).
curl -X POST https://your-server.example/api/attachments \
-H "Authorization: Bearer <API_KEY>" \
-F "file=@diagram.png" \
-F "entity_type=issue" \
-F "entity_id=1"{
"id": 1,
"url": "/api/attachments/1",
"filename": "diagram.png",
"mime": "image/png",
"size": 1024
}GET /api/attachments/{id}
Downloads attachment bytes. Path parameter: id (integer attachment ID). It has no query parameters or request body.
Response: file bytes. The response sets Content-Type to the stored MIME type. It sets Content-Disposition to inline for images and attachment for non-image files.
DELETE /api/attachments/{id}
Deletes an attachment metadata row and its links. Path parameter: id (integer attachment ID). It has no query parameters or request body.
Response: the standard delete response.
Exports
GET /api/export/issues/{identifier}
Exports an issue as a Markdown attachment. Path parameter: identifier (string such as LIF-1). It has no query parameters or request body.
Response: Markdown bytes with Content-Type: text/markdown; charset=utf-8.
GET /api/export/pages/{identifier}
Exports a page as a Markdown attachment. Path parameter: identifier (string such as LIF-DOC-1 or DOC-1). It has no query parameters or request body.
Response: Markdown bytes with Content-Type: text/markdown; charset=utf-8.
GET /api/export/projects/{identifier}
Exports a project. Path parameter: identifier (string such as LIF). Query parameter: format (string, optional). format accepts zip by default or json.
It has no request body. Response: ZIP bytes for format=zip. For format=json, the response is an export bundle JSON object. The bundle structure is defined by the export implementation and is not a REST request schema.
Search, health, and realtime
GET /api/search
Searches indexed issues, pages, and comments. Query parameters are query (string, required), project_id (integer, optional), result_type (string, optional), sort (string, optional), mode (string, optional), limit (integer, optional), and offset (integer, optional).
result_type accepts issue, page, or comment. sort accepts relevance or recent. mode accepts fts or literal. It has no request body.
Response: an array of objects with result_type (string), id (integer), identifier (string or null), title (string), snippet (string), and project_id (integer or null).
GET /api/health
Returns the health marker. This endpoint has no path or query parameters. It has no request body.
Response: plain text ok.
GET /api/events/ws
Upgrades the request to a WebSocket that receives project and issue update events. It has no path or query parameters. It has no request body.
The request requires a lific_token cookie containing a valid session token. Response: a WebSocket connection. Event JSON uses a type field. Event types are resync.required, project.created, project.updated, project.deleted, projects.reordered, issue.created, issue.updated, issue.deleted, issue.linked, and issue.unlinked.
{
"type": "issue.updated",
"project_id": 1,
"issue_id": 1
}