Projects
/api/v1 operations for projects. See HTTP 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.metricKeyanddetails.pointIndexidentify 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.