Skip to content

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.

Prefix: /api/projects/:projectId/backend. These routes are mounted only when hosting is enabled.

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.

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.

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.

All under /api/projects/:projectId/lenast/.... Permissions lenast.read / lenast.write / lenast.admin. Nested projects use :lpId.

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.

Area Prefix
UI builder /api/projects/:projectId/strala
Assistant sessions /api/projects/:projectId/assistant/sessions

See UI builder and Assistant.