> For the complete documentation index, see [llms.txt](https://docs.khotan.com/llms.txt). A markdown version of any page is available at its `index.md` URL.

# Projects

`/api/v1` operations for projects. See [HTTP API](/api/) for authentication, the error envelope, and rate limits.

## Operations

### GET /api/v1/projects

List projects

Returns the organization's live projects with their current spec revision and member count. Never includes spec bodies.

Auth: API key required.

Operation ID: `listProjects`

Responses:
- `200` — The organization's projects.
- `401` — Authentication is missing or invalid.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects list`

### POST /api/v1/projects

Create a project

Creates a project and its single root goal node. The slug is derived from the name when omitted and must be unique among the organization's live projects.

Auth: API key required.

Operation ID: `createProject`

Request body (`application/json`, required):
- `name` (string, required)
- `slug` (string, optional) — Derived from the name when omitted; conflicts return 409.
- `description` (string | null, optional)

Responses:
- `201` — The created project.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `409` — Response.
- `422` — Response.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects create`

### GET /api/v1/projects/linear-options

List Linear projects that can be bound

Returns Linear projects visible to this workspace. Use this to pick a project to bind; a new Khotan project mints its own Linear project on first push.

Auth: API key required.

Operation ID: `listLinearProjectOptions`

Query parameters:
- `query` (string, optional)

Responses:
- `200` — Linear projects this workspace can bind to.
- `401` — Authentication is missing or invalid.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects linear options`

### GET /api/v1/projects/{projectId}

Get a project

Returns one project by id or slug. Cross-organization requests are answered as not found.

Auth: API key required.

Operation ID: `getProject`

Path parameters:
- `projectId` (string, required)

Responses:
- `200` — The project.
- `401` — Authentication is missing or invalid.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects get`

### PATCH /api/v1/projects/{projectId}

Update a project

Updates a project's Khotan-owned attributes. An omitted field, including `tags`, stays unchanged; a sent `tags` list replaces the stored list. The slug is immutable, and execution state is never writable here — the tracker owns it.

Auth: API key required.

Operation ID: `updateProject`

Path parameters:
- `projectId` (string, required)

Request body (`application/json`, required):
- `name` (string, optional)
- `description` (string | null, optional)
- `lifecycle` ("active" | "archived", optional)
- `tags` (string[], optional) — Full tag list; it replaces the stored list. Each tag is trimmed, lowercased, and has whitespace runs replaced with "-", and repeats are removed. After that, each tag is 1-32 lowercase letters, digits, or single hyphens, with at most 10 tags. Presets: test, live, uat, dev, demo, deprecated.

Responses:
- `200` — The updated project.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `422` — Response.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects update`

### DELETE /api/v1/projects/{projectId}

Delete a project

Soft-deletes a project and frees its slug for reuse. The linked tracker project is retained.

Auth: API key required.

Operation ID: `deleteProject`

Path parameters:
- `projectId` (string, required)

Responses:
- `204` — The project was deleted.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects delete`

### GET /api/v1/projects/{projectId}/spec

Read a project spec

Returns the project's spec tree limited to the requested altitude: high renders the goal, medium adds phases, low adds items joined with their mirrored tracker issues. Defaults to high.

Auth: API key required.

Operation ID: `getProjectSpec`

Path parameters:
- `projectId` (string, required)

Query parameters:
- `altitude` ("high" | "medium" | "low", optional)

Responses:
- `200` — The rendered spec.
- `401` — Authentication is missing or invalid.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects spec`

### POST /api/v1/projects/{projectId}/spec

Commit a spec revision

Applies edits to one or more spec nodes as a single project spec revision. Each edit carries the document revision it was based on, and the request carries the project revision the author last saw; either being stale returns 409.

Auth: API key required.

Operation ID: `commitProjectSpecRevision`

Path parameters:
- `projectId` (string, required)

Request body (`application/json`, required):
- `edits` (object[], required)
- `changeSummary` (string | null, optional)
- `expectedRevision` (integer, required) — Project spec revision the author last saw; a stale value returns 409 with the current revision.
- `links` (object[], optional)

Responses:
- `201` — The committed revision.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `409` — Response.
- `422` — Response.
- `429` — Too many requests; retry after the indicated delay.

### POST /api/v1/projects/{projectId}/spec/nodes

Create a spec node

Creates a child under an existing spec node. Use this to add a delivery phase under the Milestones section, or an item under a phase. The thirteen section headings are created with the project and cannot be added here.

Auth: API key required.

Operation ID: `createProjectSpecNode`

Path parameters:
- `projectId` (string, required)

Request body (`application/json`, required):
- `parentId` (string, required)
- `title` (string, required)
- `body` (string, optional)
- `position` (integer, optional)

Responses:
- `201` — The created node.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `422` — Response.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects node create`

### PATCH /api/v1/projects/{projectId}/spec/nodes/{nodeId}

Update a spec node

Updates a node's title, sibling position, parent, or milestone plan. A move is applied first so a node that is becoming a phase can then accept dates. Each field is optional; omitted fields are left alone.

Auth: API key required.

Operation ID: `updateProjectSpecNode`

Path parameters:
- `projectId` (string, required)
- `nodeId` (string, required)

Request body (`application/json`, required):
- `title` (string, optional)
- `position` (integer, optional)
- `parentId` (string, optional) — Move the node under this parent before applying the other fields.
- `startDate` (string | null, optional) — Milestone plan start. Only valid on a phase.
- `targetDate` (string | null, optional) — Milestone plan target. Only valid on a phase.
- `dependsOnNodeId` (string | null, optional) — Milestone this phase waits on. Only valid on a phase.

Responses:
- `200` — The updated node.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `422` — Response.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects node update`

### DELETE /api/v1/projects/{projectId}/spec/nodes/{nodeId}

Delete a spec node

Soft-deletes the node and its descendants. The goal node cannot be deleted — delete the project instead.

Auth: API key required.

Operation ID: `deleteProjectSpecNode`

Path parameters:
- `projectId` (string, required)
- `nodeId` (string, required)

Responses:
- `204` — The node was deleted.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `422` — Response.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects node delete`

### GET /api/v1/projects/{projectId}/revisions

List spec revisions

Returns the project's committed spec revisions newest-first, with actor, timestamp, change summary, and the number of documents each revision bundled.

Auth: API key required.

Operation ID: `listProjectSpecRevisions`

Path parameters:
- `projectId` (string, required)

Query parameters:
- `limit` (integer, optional)

Responses:
- `200` — The project's committed spec revisions.
- `401` — Authentication is missing or invalid.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects revisions`

### GET /api/v1/projects/{projectId}/revisions/{revision}

Get a spec revision

Returns one committed spec revision: each bundled document's content alongside the previous revision's content, plus the cause and effect provenance edges attached to it.

Auth: API key required.

Operation ID: `getProjectSpecRevision`

Path parameters:
- `projectId` (string, required)
- `revision` (integer, required)

Responses:
- `200` — The revision with its diff and provenance.
- `401` — Authentication is missing or invalid.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects revision`

### GET /api/v1/projects/{projectId}/members

List project members

Returns the embedded apps, pipelines, and databases attached to this project.

Auth: API key required.

Operation ID: `listProjectMembers`

Path parameters:
- `projectId` (string, required)

Responses:
- `200` — The project's members.
- `401` — Authentication is missing or invalid.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects members`

### POST /api/v1/projects/{projectId}/members

Attach a resource to a project

Attaches an embedded app, pipeline, or database. A resource already attached elsewhere returns 409 until it is detached.

Auth: API key required.

Operation ID: `attachProjectMember`

Path parameters:
- `projectId` (string, required)

Request body (`application/json`, required):
- `resourceKind` ("app" | "pipeline" | "database" | "file", required)
- `resourceId` (string, required)

Responses:
- `201` — The created membership.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `409` — Response.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects attach`

### DELETE /api/v1/projects/{projectId}/members

Detach a resource from a project

Removes the membership. The underlying app, pipeline, or database is untouched.

Auth: API key required.

Operation ID: `detachProjectMember`

Path parameters:
- `projectId` (string, required)

Request body (`application/json`, required):
- `resourceKind` ("app" | "pipeline" | "database" | "file", required)
- `resourceId` (string, required)

Responses:
- `204` — The membership was removed.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects detach`

### GET /api/v1/projects/{projectId}/metrics

List project metrics

Returns the project's metrics with their attributes, curation, and last-reported time, in curated order. Hidden metrics are included only for owner, admin, and developer keys.

Auth: API key required.

Operation ID: `listProjectMetrics`

Path parameters:
- `projectId` (string, required)

Responses:
- `200` — The project's metrics.
- `401` — Authentication is missing or invalid.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects metrics list`

### POST /api/v1/projects/{projectId}/metrics

Report project metrics

Creates or updates metric definitions and sets their points. Idempotent: series and breakdown points are keyed by bucket (and category), scatter points by id, so a retried report never double-counts. The whole report is validated before anything is written. Curation made in Khotan — name, order, hidden, headline — is never overwritten.

Auth: API key required.

Operation ID: `reportProjectMetrics`

Path parameters:
- `projectId` (string, required)

Request body (`application/json`, required):
- `metrics` (object[], required) — At most 50 metrics and 10000 points in total per report.

Responses:
- `200` — The metrics and point counts written.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `409` — A metric exists with a different shape or grain, or the project's metric limit would be exceeded.
- `422` — The report is invalid. `details.metricKey` and `details.pointIndex` identify the offending metric and point.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects metrics report`

### GET /api/v1/projects/{projectId}/metrics/{metricKey}

Get a project metric

Returns one metric's points over a range, with the range value, the preceding range's value, and the change. An empty range has a null value, never 0.

Auth: API key required.

Operation ID: `getProjectMetric`

Path parameters:
- `projectId` (string, required)
- `metricKey` (string, required)

Query parameters:
- `range` ("7d" | "30d" | "90d", optional)

Responses:
- `200` — The metric over the requested range.
- `401` — Authentication is missing or invalid.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects metrics get`

### PATCH /api/v1/projects/{projectId}/metrics/{metricKey}

Update a project metric's curation

Changes a metric's display name, position, hidden flag, or headline flag. Reports never overwrite these. Hiding a headline metric clears headline; a project has at most three headline metrics, and a scatter metric cannot be headline.

Auth: API key required.

Operation ID: `updateProjectMetric`

Path parameters:
- `projectId` (string, required)
- `metricKey` (string, required)

Request body (`application/json`, required):
- `displayName` (string | null, optional) — Curated name. Null clears it, so the reported name shows again.
- `position` (integer, optional) — Sort position among the project's metrics, ascending.
- `hidden` (boolean, optional) — Hides the metric from readers. Hiding also clears headline.
- `headline` (boolean, optional) — Leads the Metrics tab and the project card. At most 3 per project; a fourth returns 409.

Responses:
- `200` — The updated metric.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `409` — The project already has three headline metrics.
- `422` — The patch is invalid, including headline on a hidden or scatter metric.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects metrics update`

### DELETE /api/v1/projects/{projectId}/metrics/{metricKey}

Delete a project metric

Removes the metric, its curation, and all of its points. A later report naming the same key creates a new metric with default curation.

Auth: API key required.

Operation ID: `deleteProjectMetric`

Path parameters:
- `projectId` (string, required)
- `metricKey` (string, required)

Responses:
- `204` — The metric and its points were deleted.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects metrics delete`

### POST /api/v1/projects/{projectId}/changes

File a change as an issue

Creates or updates the issue tracking one OpenSpec change directory under this project. Khotan owns the issue's title and description; its state, assignee, priority, and dates stay with the tracker. Idempotent by change id.

Auth: API key required.

Operation ID: `syncProjectChange`

Path parameters:
- `projectId` (string, required)

Request body (`application/json`, required):
- `changeId` (string, required) — The change directory name. Stable identity for the issue, so renaming the directory files a second one.
- `title` (string, required)
- `body` (string, required)
- `capabilities` (string[], optional) — Capabilities this change amends, applied as labels in one managed group. Labels outside that group are untouched.
- `documents` (object[], optional) — Long parts of the change — design and requirements — attached as documents rather than inlined in the description.
- `source` (object, optional) — Where the change came from. A url is attached to the issue as a link.

Responses:
- `200` — The issue tracking this change.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `422` — Response.
- `429` — Too many requests; retry after the indicated delay.

### POST /api/v1/projects/{projectId}/linear

Bind a project to Linear

Binds this project to an existing Linear project, or clears the binding when linearProjectId is null. Binding adopts that project's milestones read-only. To create a fresh empty Linear project instead, use the mint action.

Auth: API key required.

Operation ID: `bindLinearProject`

Path parameters:
- `projectId` (string, required)

Request body (`application/json`, required):
- `linearProjectId` (string | null, required) — The Linear project to bind. Null clears the binding.

Responses:
- `200` — The Linear project now bound, or null if cleared.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `422` — Response.
- `429` — Too many requests; retry after the indicated delay.

CLI: `khotan projects linear bind`

### GET /api/v1/linear/initiative

Get the workspace's Linear initiative link

Returns the Linear initiative currently linked to this workspace, or null when none is linked.

Auth: API key required.

Operation ID: `getLinearInitiative`

Responses:
- `200` — The linked initiative, or null.
- `401` — Authentication is missing or invalid.
- `429` — Too many requests; retry after the indicated delay.

### POST /api/v1/linear/initiative

Link the workspace's Linear initiative

Links this workspace to an existing Linear initiative by id or URL. Minted Linear projects are created beneath it. Restricted to owner and admin.

Auth: API key required.

Operation ID: `linkLinearInitiative`

Request body (`application/json`, required):
- `reference` (string, required) — The Linear initiative to link, by id or URL. Every Linear project Khotan mints for this workspace is created beneath it.

Responses:
- `200` — The linked initiative.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `404` — The resource was not found.
- `409` — Response.
- `422` — Response.
- `429` — Too many requests; retry after the indicated delay.

### DELETE /api/v1/linear/initiative

Unlink the workspace's Linear initiative

Removes the Linear initiative link from this workspace. Restricted to owner and admin.

Auth: API key required.

Operation ID: `unlinkLinearInitiative`

Responses:
- `204` — The initiative link was removed.
- `401` — Authentication is missing or invalid.
- `403` — The caller is not permitted to perform this action.
- `429` — Too many requests; retry after the indicated delay.

<!-- generated from the /api/v1 OpenAPI spec v1.0.0 (adapter 0.10.0); do not edit by hand -->
