Hosting, git, and project management
These routes require a platform JWT or PAT. They are not part of the client SDKs. Use KataflaxAdminClient (hosting, git, lenast) or the console.
Hosting (HOSTING_ENABLED)
Section titled “Hosting (HOSTING_ENABLED)”Prefix: /api/projects/:projectId/backend. These routes are mounted only when hosting is enabled.
Create a container app
Section titled “Create a container app”POST /api/projects/:projectId/backend/apps
Auth is a platform JWT or PAT. Required keys are name and slug. Optional keys: environment (default "production"), gitRepositoryId, gitDeployMode (default ""), gitDeployBranch (default ""), gitAutoDeployBranch (default ""), gitDeploySubdirectory (default "").
slug must match ^[a-z][a-z0-9\-]{1,62}$. 400 "name and slug are required" or "slug must be lowercase alphanumeric with hyphens, 2-63 chars".
{ "name": "web", "slug": "web", "environment": "production"}201. data is the app object. GET one app wraps the same object as data.app.
{ "success": true, "data": { "id": "<uuid>", "projectId": "<project uuid>", "name": "web", "slug": "web", "appType": "container", "environment": "production", "defaultDomain": "<appSlug>-<project slug>-<last project-id segment>.<hosting domain>", "status": "active", "minReplicas": 1, "maxReplicas": 3, "cpuMillis": 500, "memoryMb": 512, "activeDeploymentId": null, "activeStaticDeploymentId": null, "gitRepositoryId": null, "gitDeployBranch": "", "gitAutoDeployBranch": "", "gitDeploySubdirectory": "", "gitDeployMode": "", "createdAt": "<ISO-8601>", "updatedAt": "<ISO-8601>" }}defaultDomain is {slug}-{project name lowercased, with every character outside [a-z0-9] replaced by "-"}-{last "-" segment of the project id}.{hosting domain}. The host is the configured hosting domain.
minReplicas, maxReplicas, cpuMillis, and memoryMb follow the account plan. The numbers above are the fallback when plan limits are absent; the account plan clamps them.
appType is container on this route. POST /api/projects/:projectId/backend/static-apps reads the same required keys and returns the same fields with appType static. The container quota key is max_apps. The static quota key is max_static_sites.
409 "App slug already in use". 403 "Not allowed for this project". 404 "Project not found". 402 and 429 use the same error body when the billing gate or the app limit blocks the create. That body is only:
{ "success": false, "message": "..." }401 still comes from platform auth before the handler. Missing or non-Bearer auth is "Authentication required". A bad token is "Invalid or expired token".
Containers: GET/POST /apps, GET/DELETE /apps/:appId, suspend / unsuspend, PUT .../scaling, PUT .../git-source, source-uploads, POST .../deployments, POST .../deployments/from-git, list deployments, get deployment, rollback, runtime and build logs, secrets (list/upsert/import/delete), hostnames (bind/verify/unbind; domains is an alias), metrics, timeline, usage.
Static: POST /static-apps, get/delete, git-source, source-uploads, deployments and from-git, hostnames.
See Hosting.
Git (GIT_HOSTING_ENABLED)
Section titled “Git (GIT_HOSTING_ENABLED)”Prefix: /api/projects/:projectId/git.
Repos CRUD; branches (delete uses ?name= so slashes work); tags; commits and commit diff; tree; blob; merge requests (create, get, patch, merge, close, reopen, merge-check, diff); comments, reactions, suggestions; reviews; assignees/reviewers/labels; repo labels; members; deploy keys.
Smart HTTP clone:
| Method | Path |
|---|---|
| GET | /git/:projectId/:repoSlug/info/refs |
| POST | /git/:projectId/:repoSlug/git-upload-pack |
| POST | /git/:projectId/:repoSlug/git-receive-pack |
See Git.
CI runner
Section titled “CI runner”Console, under /api/projects/:projectId/git: GET/POST /repos/:repoId/ci/runners, DELETE /repos/:repoId/ci/runners/:runnerId, GET /repos/:repoId/ci/runs, GET /repos/:repoId/ci/runs/:runId, GET /repos/:repoId/merge-requests/:mrId/ci-run.
The runner process calls /api/ci/runner/ with a kfxr_… token. Request and response fields are on CI. The pipeline file and runner process are on CI runner.
/api/projects/:projectId/dns/providers, zones, records, gateway-instructions. See DNS.
Project management
Section titled “Project management”All under /api/projects/:projectId/lenast/.... Permissions lenast.read / lenast.write / lenast.admin. Nested projects use :lpId.
Create a work item
Section titled “Create a work item”POST /api/projects/:projectId/lenast/projects/:lpId/work-items
:projectId is the Tefily project. :lpId is the project-management project inside it. Auth is a platform JWT or PAT. The permission key is lenast.write.
| Key | Handling |
|---|---|
name |
Required string. 400 "name is required". Blank after trim is the same message. |
description |
String, default "". |
descriptionJson |
String, default "". |
stateId |
Optional. Invalid for this project: 400 "stateId is invalid for this project". Omitted: the default state, otherwise the first backlog state. If none: 400 "Project has no states configured". |
priority |
String, default "none". The server stores the string. The console uses urgent, high, medium, low, and none. |
startDate, targetDate |
Optional. Non-empty strings go through date parsing. Anything else becomes null. Response dates are ISO-8601 and omitted when null. |
estimatePointId, parentId, issueTypeId |
Optional strings. |
sortOrder |
Optional number. Default is sequence * 10000. |
isDraft |
Bool, default false. |
assigneeIds, labelIds |
Arrays of strings, default []. |
A bad issueTypeId is 400 "issueTypeId is invalid for this project". A bad parentId is 400 "parentId is invalid for this project" or "parentId would create a cycle".
{ "name": "Ship the billing page", "description": "", "priority": "none", "assigneeIds": [], "labelIds": []}201. data is the work item. key is {identifier}-{sequence} (for example ENG-42). Create includes it because the project identifier is passed in. version defaults to 1.
{ "success": true, "data": { "id": "<uuid>", "lenastProjectId": "<lpId>", "sequence": 1, "sortOrder": 10000, "version": 1, "key": "ENG-1", "name": "Ship the billing page", "description": "", "descriptionJson": "", "stateId": "<state uuid>", "priority": "none", "isDraft": false, "createdBy": "<user uuid>", "createdAt": "<ISO-8601>", "updatedAt": "<ISO-8601>", "assigneeIds": [], "labelIds": [], "cycleIds": [], "moduleIds": [], "subscriberIds": [], "links": [], "relations": [], "comments": [], "activities": [], "attachments": [], "subIssues": [] }}Omitted when null: startDate, targetDate, completedAt, estimatePointId, parentId, issueTypeId, updatedBy, archivedAt.
A bare create inserts a created activity in activities (left unexpanded in the sample). Activity keys are id, workItemId, actorId, verb, createdAt, plus optional actorName, actorEmail, field, oldValue, newValue. links, relations, comments, and attachments are [] on this create.
404 "Project not found" without lenast.write. 404 "Lenast project not found" when :lpId is not in that Tefily project. Missing or non-Bearer auth is 401 "Authentication required". A bad token is 401 "Invalid or expired token".
Workspace: projects, members, notifications, activity, stickies, drafts, recents, browse by key (ENG-42), search, workspace views, active cycles, favorites, exports, webhooks, analytics.
Per project: archive/restore, members, automations, states, labels, issue types, estimates, work items (including bulk patch), comments, links/relations/subscribers, attachments, cycles (burndown), modules, views, intake, pages (content + versions).
MCP implements a subset. Prefer the console or admin SDK rather than copying every path.
UI builder and assistant
Section titled “UI builder and assistant”| Area | Prefix |
|---|---|
| UI builder | /api/projects/:projectId/strala |
| Assistant sessions | /api/projects/:projectId/assistant/sessions |
See UI builder and Assistant.