REST API
Base URL, authentication, common response formats, and shared data shapes.
Base URL
All REST paths are relative to the Lific server URL.
https://your-server.example/apiThe examples use https://your-server.example and the project identifier LIF.
Authentication
Most endpoints require an Authorization header with the Bearer scheme.
curl https://your-server.example/api/projects \
-H "Authorization: Bearer <API_KEY>"API keys use the lific_sk prefix. Session tokens use the lific_sess_ prefix. OAuth access tokens use the lific_at_ prefix. All three are accepted in a Bearer header by the REST authentication middleware.
GET /api/health, GET /api/instance, POST /api/auth/signup, POST /api/auth/login, and POST /api/auth/auto-login do not pass through the Bearer middleware. POST /api/auth/logout passes through the middleware and requires a valid Bearer credential. A session token deletes that session; any valid credential clears the browser session cookie and returns a successful logout response.
GET /api/events/ws is not Bearer authenticated. It requires a valid lific_token cookie containing a session token. GET /api/attachments/{id} also accepts that session cookie for browser downloads. Other attachment requests require a Bearer header.
Authorization by endpoint family
When authz_enforced is enabled, REST handlers use the following minimum roles. Administrators bypass project membership checks.
| Operation family | Minimum access |
|---|---|
| Project content reads, issue/page/plan reads, search, and activity | viewer on the relevant project |
| Issue/page/plan/comment content mutations, relations, plan-step links, modules, labels, and folders | maintainer on the relevant project; cross-project operations require it in both projects |
| Project settings, membership/role changes, and project deletion | lead on the project |
| Workspace-level page reads/mutations | Workspace rules; mutations require an administrator |
| Attachment upload | Any authenticated user; the resulting attachment is private until linked or otherwise authorized |
| Linked attachment list/download | viewer on an owning project; an unlinked attachment is readable only by its uploader or an administrator |
| Attachment deletion | The uploader, a project maintainer on an owning project, or an administrator |
Saved views are additionally caller-owned: a member can list or modify only their own views, and every saved-view request requires an authenticated user.
This matrix applies to project-scoped requests made by users and bot identities. Instance administrators and operator-trusted credentials intentionally bypass project membership checks; auth-disabled requests are operator-equivalent, and instance-scoped endpoints have their own authentication rules.
Content types
JSON request bodies use Content-Type: application/json.
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"}'POST /api/attachments uses multipart/form-data. Export and attachment download endpoints return file bytes instead of JSON.
Errors
Handlers return JSON errors with an error string.
{
"error": "issue 999 not found"
}The common error statuses are 400 for invalid requests, 403 for forbidden requests, 404 for missing resources, 409 for conflicts, and 500 for internal failures. Authentication middleware can return 401 as plain text with a WWW-Authenticate header.
An endpoint that requires an authenticated user, such as saved views or project groups, answers a request that carries no user identity with 403 and this body:
{
"error": "authentication required"
}Pagination and lists
Most collection endpoints return JSON arrays. Activity endpoints return an object with items and has_more; board, insights, and count endpoints return objects with their documented fields. Array responses do not include total counts or cursor metadata.
issues, plans, and search default to limit=50, limit=50, and limit=20 respectively. Their limits are clamped to 1..=500. Activity feeds default to limit=50 and clamp to 1..=200; they return has_more. Comment lists default to limit=50 and clamp to 1..=500. Page lists apply pagination when limit or offset is supplied, with a maximum limit of 500.
Every offset is floored at 0. A comment list array is a page, not a thread: it carries no total and no has_more, so a full page can be the end of the thread or the start of the next one. Request the page after it to find out.
Comment lists also accept an optional keyset cursor, before_created_at plus before_id, which pages backwards from a comment the client has already seen without the drift an offset suffers when the thread is written to mid-read. See comment endpoints for its rules.
GET /api/plans supports keyset paging only when order_by=id. Pass before_id with that ordering. Other paginated endpoints use limit and offset.
Shared response shapes
Project
Project fields are id (integer), name (string), identifier (string), description (string), emoji (string or null), lead_user_id (integer or null), sort_order (integer), created_at (string), and updated_at (string).
{
"id": 1,
"name": "Lific",
"identifier": "LIF",
"description": "",
"emoji": null,
"lead_user_id": 1,
"sort_order": 0,
"created_at": "2026-07-14 12:00:00",
"updated_at": "2026-07-14 12:00:00"
}Issue
Issue fields are id (integer), project_id (integer), sequence (integer), identifier (string), title (string), description (string), status (string), priority (string), module_id (integer or null), sort_order (number), start_date (string or null), target_date (string or null), created_at (string), updated_at (string), and labels (string array). When set, source appears on create, update, and single-issue responses. Issue list responses omit source. GET /api/issues/{id} and GET /api/issues/resolve/{identifier} populate relation arrays: blocks, blocked_by, relates_to, duplicates, and duplicated_by.
{
"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": []
}Page and comment
Page fields are id (integer), project_id (integer or null), sequence (integer or null), identifier (string), folder_id (integer or null), title (string), content (string), sort_order (number), status (string), pinned (boolean), created_at (string), updated_at (string), and labels (string array).
Comment fields are id (integer), issue_id (integer or null), page_id (integer or null), user_id (integer), author (string), author_display_name (string), content (string), created_at (string), and updated_at (string).
Project resources
Module fields are id (integer), project_id (integer), name (string), description (string), status (string), emoji (string or null), created_at (string), and updated_at (string).
Label fields are id (integer), project_id (integer), name (string), and color (string).
Folder fields are id (integer), project_id (integer), parent_id (integer or null), name (string), and sort_order (number).
Delete responses
Endpoints that delete a resource return this JSON shape unless the endpoint reference states otherwise.
{
"deleted": true
}