# Projects

A **project** is one codebase under hlix's control, with its own Coding Workspace, repositories, roadmap and budget. These operations create projects, start runs against them, and read everything hanging off one.

Generated from contract version `1.0.0` — the same document [`/v1/api/openapi.json`](/api/openapi/) serves.

## Prerequisites

- An API key in `x-api-key`, or a browser session cookie
- `X-Organization-Id` naming the workspace to act in, unless the session already has an active one
- The base URL for your deployment, `https://server.hlix.ai` by default

## Operations

| Method | Path | Operation |
| --- | --- | --- |
| `GET` | `/v1/api/projects` | [List the workspace's projects](#list-the-workspaces-projects) |
| `POST` | `/v1/api/projects` | [Create a project](#create-a-project) |
| `DELETE` | `/v1/api/projects/{id}` | [Delete a project](#delete-a-project) |
| `GET` | `/v1/api/projects/{id}` | [Get one project](#get-one-project) |
| `PATCH` | `/v1/api/projects/{id}` | [Update a project's configuration](#update-a-projects-configuration) |
| `GET` | `/v1/api/projects/{id}/artifacts` | [List a project's stored artifacts](#list-a-projects-stored-artifacts) |
| `GET` | `/v1/api/projects/{id}/artifacts/{filename}` | [Get a signed download URL for one project artifact](#get-a-signed-download-url-for-one-project-artifact) |
| `GET` | `/v1/api/projects/{id}/budget` | [Get the project's delivery budget and spend to date](#get-the-projects-delivery-budget-and-spend-to-date) |
| `GET` | `/v1/api/projects/{id}/cycle-metrics` | [Get a project's flow metrics computed from cycle stage history](#get-a-projects-flow-metrics-computed-from-cycle-stage-history) |
| `GET` | `/v1/api/projects/{id}/desktop` | [Report whether a graphical desktop is reachable](#report-whether-a-graphical-desktop-is-reachable) |
| `POST` | `/v1/api/projects/{id}/desktop` | [Start the desktop and mint an embeddable URL](#start-the-desktop-and-mint-an-embeddable-url) |
| `GET` | `/v1/api/projects/{id}/diagnostics` | [Read why the project's run failed or is parked](#read-why-the-projects-run-failed-or-is-parked) |
| `GET` | `/v1/api/projects/{id}/image-builds` | [List the project's sandbox image builds](#list-the-projects-sandbox-image-builds) |
| `GET` | `/v1/api/projects/{id}/linear-sessions` | [List the Linear conversations delegated into this project](#list-the-linear-conversations-delegated-into-this-project) |
| `GET` | `/v1/api/projects/{id}/messages` | [Read the project's conversation](#read-the-projects-conversation) |
| `GET` | `/v1/api/projects/{id}/repos` | [List a project's repositories](#list-a-projects-repositories) |
| `POST` | `/v1/api/projects/{id}/repos` | [Attach a repository to a project](#attach-a-repository-to-a-project) |
| `DELETE` | `/v1/api/projects/{id}/repos/{repoId}` | [Detach a repository from a project](#detach-a-repository-from-a-project) |
| `PATCH` | `/v1/api/projects/{id}/repos/{repoId}` | [Update a repository's branch or label](#update-a-repositorys-branch-or-label) |
| `POST` | `/v1/api/projects/{id}/repos/{repoId}/primary` | [Make a repository the project's primary](#make-a-repository-the-projects-primary) |
| `POST` | `/v1/api/projects/{id}/repos/connect` | [Connect a repo-less project's existing history to GitHub](#connect-a-repo-less-projects-existing-history-to-github) |
| `GET` | `/v1/api/projects/{id}/repos/connect/preview` | [Inspect repo-less history before connecting a remote](#inspect-repo-less-history-before-connecting-a-remote) |
| `GET` | `/v1/api/projects/{id}/roadmap` | [Get the project's roadmap](#get-the-projects-roadmap) |
| `POST` | `/v1/api/projects/{id}/start` | [Start the project's autonomous run](#start-the-projects-autonomous-run) |
| `POST` | `/v1/api/projects/{id}/stop` | [Stop the project's autonomous run](#stop-the-projects-autonomous-run) |
| `GET` | `/v1/api/projects/{id}/terminal` | [Mint SSH access into the project's Coding Workspace](#mint-ssh-access-into-the-projects-coding-workspace) |
| `GET` | `/v1/api/projects/{id}/threads` | [Get the project's conversation tree](#get-the-projects-conversation-tree) |
| `GET` | `/v1/api/projects/{id}/usage` | [Get the project's cost and token roll-up](#get-the-projects-cost-and-token-roll-up) |

## List the workspace's projects

`GET /v1/api/projects` · operation ID `listProjects`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectSummary[] | Every project, newest first, each with its attached repositories and the counts the projects index renders. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |

## Create a project

`POST /v1/api/projects` · operation ID `createProject`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Request body** — `application/json`, `CreateProject` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `brief` | string | no | min length 1, max length 5000 |
| `claudeMd` | string | no |  |
| `name` | string | no | min length 1, max length 100 |
| `repos` | object[] | no |  |
| `runtime` | object | no |  |
| `stack` | string | no | min length 1, max length 200 |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `201` | Project | The created project and its repositories. |
| `400` | ValidationError \| ApiError | The JSON body failed schema validation (`ValidationError`), or the request named no active organization (`ApiError`). |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `409` | ApiError | One of the named repositories is already attached to another project in this workspace. |
| `500` | ApiError | The project could not be created. |

## Delete a project

`DELETE /v1/api/projects/{id}` · operation ID `deleteProject`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | MessageAck | The project was deleted. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Get one project

`GET /v1/api/projects/{id}` · operation ID `getProject`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectDetail | The project, its repositories, the viewer's steer permission and whether this deployment can publish previews. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Update a project's configuration

`PATCH /v1/api/projects/{id}` · operation ID `updateProject`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Request body** — `application/json`, `UpdateProject` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `claudeMd` | string | no |  |
| `costCapUsd` | number | null | no |  |
| `cycleCap` | integer | null | no |  |
| `intake` | object | no |  |
| `maxRevisions` | integer | null | no |  |
| `name` | string | no | min length 1, max length 100 |
| `requiresApproval` | boolean | no |  |
| `runtime` | object | no |  |
| `stack` | string | no | min length 1, max length 200 |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | Project | The updated project. Repository membership is never changed here — the collection is echoed back unchanged. |
| `400` | ValidationError \| ApiError | The JSON body failed schema validation (`ValidationError`), or the request named no active organization (`ApiError`). |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## List a project's stored artifacts

`GET /v1/api/projects/{id}/artifacts` · operation ID `listProjectArtifacts`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | StoredArtifact[] | The stored objects. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Get a signed download URL for one project artifact

`GET /v1/api/projects/{id}/artifacts/{filename}` · operation ID `getProjectArtifactUrl`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `filename` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | SignedArtifactUrl | A short-lived signed URL. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project, or no such artifact under it. |

## Get the project's delivery budget and spend to date

`GET /v1/api/projects/{id}/budget` · operation ID `getProjectBudget`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectBudget | The project's own cap overrides (null means inherited), the caps actually enforced, the platform defaults behind them, and spend to date in USD. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Get a project's flow metrics computed from cycle stage history

`GET /v1/api/projects/{id}/cycle-metrics` · operation ID `getProjectCycleMetrics`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | CycleMetrics | Throughput, cycle-time percentiles, per-stage medians, WIP, aging WIP, and stage-automation quality — computed purely from each cycle's stageHistory. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace. |

## Report whether a graphical desktop is reachable

`GET /v1/api/projects/{id}/desktop` · operation ID `getProjectDesktop`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectDesktopAccess | Desktop status. Reports only — never starts the VNC stack, and never starts a stopped sandbox. `available: true` with no `desktopUrl` means the sandbox is running but the desktop has not been started. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Start the desktop and mint an embeddable URL

`POST /v1/api/projects/{id}/desktop` · operation ID `startProjectDesktop`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectDesktopAccess | The noVNC client URL, or `available: false` with the reason. POST because it spawns processes and resets the sandbox's idle auto-stop — which is why opening the page never does it. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Read why the project's run failed or is parked

`GET /v1/api/projects/{id}/diagnostics` · operation ID `getProjectRunDiagnostics`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | RunDiagnostics | The run's own account of itself: the failure it recorded, its un-collapsed engine status, and every step it is parked on with the labels a resume may address. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project, or it has no run to diagnose. |

## List the project's sandbox image builds

`GET /v1/api/projects/{id}/image-builds` · operation ID `listProjectImageBuilds`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectImageBuild[] | The build rows, newest first. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## List the Linear conversations delegated into this project

`GET /v1/api/projects/{id}/linear-sessions` · operation ID `listProjectLinearSessions`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectLinearSessions | Newest first. Each row carries the Linear issue identifier and deep link, the session's status, and the cycle the ticket became (null while the Orchestrator has yet to adopt it). |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Read the project's conversation

`GET /v1/api/projects/{id}/messages` · operation ID `listProjectMessages`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `agentId` | query | no | string | Read this agent's project thread instead of the conductor's. |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | WireMessage[] | The transcript, oldest first. A project or agent thread that cannot be resolved reads as an EMPTY list, not a 404. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## List a project's repositories

`GET /v1/api/projects/{id}/repos` · operation ID `listProjectRepos`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectRepo[] | The repositories, primary first then oldest first. An EMPTY array is the repo-less signal — there is no placeholder row. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Attach a repository to a project

`POST /v1/api/projects/{id}/repos` · operation ID `attachProjectRepo`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Request body** — `application/json`, `AttachProjectRepo` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `cloneUrl` | string (uri) | **yes** |  |
| `defaultBranch` | string | no | min length 1, max length 200 |
| `githubInstallationId` | string | null | no |  |
| `githubRepoId` | integer | null | no |  |
| `isPrimary` | boolean | no |  |
| `label` | string | null | no |  |
| `name` | string | **yes** | min length 1, max length 200 |
| `owner` | string | **yes** | min length 1, max length 100 |
| `provider` | string | no |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `201` | ProjectRepo | The attached repository. |
| `400` | ValidationError \| ApiError | The JSON body failed schema validation (`ValidationError`), or the request named no active organization (`ApiError`). |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |
| `409` | ApiError | That repository is already attached to a project in this workspace. |
| `500` | ApiError | The repository could not be attached. |

## Detach a repository from a project

`DELETE /v1/api/projects/{id}/repos/{repoId}` · operation ID `detachProjectRepo`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `repoId` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | MessageAck | The repository was detached. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project, or that repository does not belong to it. |
| `409` | ApiError | Tasks have already run against that repository, so it cannot be detached. |

## Update a repository's branch or label

`PATCH /v1/api/projects/{id}/repos/{repoId}` · operation ID `updateProjectRepo`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `repoId` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Request body** — `application/json`, `UpdateProjectRepo` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `defaultBranch` | string | no | min length 1, max length 200 |
| `label` | string | null | no |  |
| `setupCommand` | string | null | no |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectRepo | The updated repository. |
| `400` | ValidationError \| ApiError | The JSON body failed schema validation (`ValidationError`), or the request named no active organization (`ApiError`). |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project, or that repository does not belong to it. |

## Make a repository the project's primary

`POST /v1/api/projects/{id}/repos/{repoId}/primary` · operation ID `setPrimaryProjectRepo`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `repoId` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectRepo | The promoted repository. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project, or that repository does not belong to it. |

## Connect a repo-less project's existing history to GitHub

`POST /v1/api/projects/{id}/repos/connect` · operation ID `connectProjectRepo`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Request body** — `application/json`, `ConnectProjectRepo` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `force` | boolean | no |  |
| `githubInstallationId` | string | null | no |  |
| `githubRepoId` | integer | null | no |  |
| `label` | string | null | no |  |
| `name` | string | **yes** | min length 1, max length 200 |
| `owner` | string | **yes** | min length 1, max length 100 |
| `provider` | string | no |  |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ConnectProjectRepoResult | The repository was already connected by an earlier completed request. |
| `201` | ConnectProjectRepoResult | The existing project history was pushed and the repository was connected. |
| `400` | ValidationError \| ApiError | The JSON body failed schema validation (`ValidationError`), or the request named no active organization (`ApiError`). |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |
| `409` | ApiError | The repository identity is unavailable, the project gained a repository, or the remote contains conflicting refs without force acknowledgement. |
| `502` | ApiError | The push stopped before every local ref was confirmed on the remote; retry is safe. |
| `503` | WorkspaceBudgetError | This backend instance is at its Coding Workspace budget. Nothing was changed and the request is safe to retry; `Retry-After` carries the hint. |

## Inspect repo-less history before connecting a remote

`GET /v1/api/projects/{id}/repos/connect/preview` · operation ID `previewProjectRepoConnection`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | RepoHistorySummary | The local branch, tag and commit counts. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |
| `409` | ApiError | The project already has a repository and therefore has no repo-less history to connect. |
| `503` | WorkspaceBudgetError | This backend instance is at its Coding Workspace budget. Nothing was changed and the request is safe to retry; `Retry-After` carries the hint. |

## Get the project's roadmap

`GET /v1/api/projects/{id}/roadmap` · operation ID `getProjectRoadmap`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectRoadmap | The merged roadmap — persisted cycles, the orchestrator's plan and any un-adopted proposals. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Start the project's autonomous run

`POST /v1/api/projects/{id}/start` · operation ID `startProject`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Request body** — `application/json`, `StartProject` (required)

| Property | Type | Required | Notes |
| --- | --- | --- | --- |
| `brief` | string | **yes** | min length 1 |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectStarted | The run was handed to the engine. It starts asynchronously — watch it over the event or stream endpoints. |
| `400` | ValidationError \| ApiError | The JSON body failed schema validation (`ValidationError`), or the request named no active organization (`ApiError`). |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |
| `409` | ApiError | A run for this project is already running or suspended. |
| `502` | ApiError | The dispatch could not be queued — the run was NOT started. Phase 2.9 commits the state change and the outbox row together, so nothing is half-written; retry is safe. |

## Stop the project's autonomous run

`POST /v1/api/projects/{id}/stop` · operation ID `stopProject`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectStopped | The run was cancelled. Cancellation is checked BETWEEN steps: the step in flight completes and nothing after it starts. A coding agent already running in the sandbox is NOT interrupted — it runs to completion and its result is still recorded. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |
| `409` | ApiError | There is no running or suspended run to stop; the body names the status that was actually found. |

## Mint SSH access into the project's Coding Workspace

`GET /v1/api/projects/{id}/terminal` · operation ID `getProjectTerminalAccess`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectTerminalAccess | A ready-to-paste `ssh` command, or `available: false` with the reason. Never starts a stopped sandbox. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Get the project's conversation tree

`GET /v1/api/projects/{id}/threads` · operation ID `getProjectThreadDirectory`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | ProjectThreadDirectory | The thread directory. It always states whether it KNOWS: a degraded read answers an explicitly non-authoritative directory rather than an empty one. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## Get the project's cost and token roll-up

`GET /v1/api/projects/{id}/usage` · operation ID `getProjectUsage`

**Parameters**

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `id` | path | **yes** | string |  |
| `X-Organization-Id` | header | no | string | The workspace (organization) to act in. Omitted, the session's active organization is used. |

**Responses**

| Status | Body | Description |
| --- | --- | --- |
| `200` | AgentUsageRollup | Totals plus two breakdowns of the same runs: by execution lane (`byRole`) and by worker persona (`byPersona`). A lane that never ran is ABSENT rather than reported as a zero row; `byPersona` instead keeps a `null` bucket for runs whose dispatch knew no persona, so both breakdowns sum to `totals`. |
| `400` | ApiError | The request named no active organization. |
| `401` | ApiError | No valid session or API key. |
| `403` | ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
| `404` | ApiError | No such project in this workspace — or a client collaborator it was never shared with (existence is deliberately not revealed). |

## If a request fails

These apply to every operation on this page.

- `400` — the body failed schema validation, or the request named no active organization. A `ValidationError` body carries the failing fields; an `ApiError` body carries only `error`.
- `401` — no valid session or API key.
- `403` — authenticated, but not a member of the named organization, or a client collaborator without the required access.
- `404` — no such resource **in this workspace**. Absent and not-visible are deliberately the same answer; do not infer cross-tenant existence from it.
- `409` — the expected revision or state is stale, or another operation currently owns the transition.
- `500` — the operation failed server-side. Retry only where an idempotency key makes that safe.

## Next steps

[API reference](/api/reference/)
  [API & OpenAPI](/api/openapi/)
  [TypeScript SDK](/sdk/typescript/)
  [CLI reference](/reference/cli/)