# Projects API

Projects. 5 operations under /api/v1. Errors are RFC 9457 problem+json with a stable `code` field. The complete schemas are in https://nebulactrl.com/api/v1/openapi.json.

## GET /projects

List projects

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `cursor` (query, string): Opaque cursor from a previous page's nextCursor
- `limit` (query, integer (int64)): Page size (default 50, max 200)

Response 200: OK (PageProject)
  - `items` (Project[] | null, required)
  - `nextCursor` (string): Present when more items exist
Errors: 401 Unauthorized; 403 Forbidden; 422 Unprocessable Entity; 500 Internal Server Error

## POST /projects

Create a project (with its production environment)

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `Idempotency-Key` (header, string): Client-chosen key (<=255 chars). A retry with the same key replays the original response for 24 hours.

Request body (application/json, required): CreateProjectInputBody
  - `description` (string)
  - `name` (string, required)

Response 201: Created (Project)
  - `buildLocation` (enum: inherit | target | build_server, required)
  - `buildServerId` (string, required)
  - `createdAt` (string (date-time), required)
  - `description` (string, required)
  - `id` (string, required)
  - `name` (string, required)
  - `registryId` (string, required)
  - `slug` (string, required)
  - `system` (boolean, required)
  - `updatedAt` (string (date-time), required)
Errors: 401 Unauthorized; 403 Forbidden; 409 Conflict; 422 Unprocessable Entity; 500 Internal Server Error

## GET /projects/{projectId}

Get a project

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `projectId` (path, string, required)

Response 200: OK (Project)
  - `buildLocation` (enum: inherit | target | build_server, required)
  - `buildServerId` (string, required)
  - `createdAt` (string (date-time), required)
  - `description` (string, required)
  - `id` (string, required)
  - `name` (string, required)
  - `registryId` (string, required)
  - `slug` (string, required)
  - `system` (boolean, required)
  - `updatedAt` (string (date-time), required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## PATCH /projects/{projectId}

Update a project

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `projectId` (path, string, required)

Request body (application/json, required): UpdateProjectInputBody
  - `buildLocation` (enum: inherit | target | build_server): Where this project's git services are built: inherit (team default), target, or build_server. Sets buildServerId and registryId with it (omitted = cleared).
  - `buildServerId` (string): Pin the build server; omitted lets the control plane choose a sticky one. Only with buildLocation.
  - `description` (string)
  - `name` (string)
  - `registryId` (string): Registry the builds are pushed to; omitted inherits the team default. Only with buildLocation.

Response 200: OK (Project)
  - `buildLocation` (enum: inherit | target | build_server, required)
  - `buildServerId` (string, required)
  - `createdAt` (string (date-time), required)
  - `description` (string, required)
  - `id` (string, required)
  - `name` (string, required)
  - `registryId` (string, required)
  - `slug` (string, required)
  - `system` (boolean, required)
  - `updatedAt` (string (date-time), required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## DELETE /projects/{projectId}

Delete an empty project

Fails with 409 `project_not_empty` while the project still has services.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `projectId` (path, string, required)

Response 204: No Content
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict (`project_not_empty`); 422 Unprocessable Entity; 500 Internal Server Error
