# Environments API

Environments. 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.

## PATCH /environments/{environmentId}

Update an environment

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

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

Request body (application/json, required): UpdateEnvironmentInputBody
  - `isProduction` (boolean)
  - `name` (string)

Response 200: OK (Environment)
  - `createdAt` (string (date-time), required)
  - `id` (string, required)
  - `isProduction` (boolean, required)
  - `kind` (enum: standard | preview, required)
  - `name` (string, required)
  - `parentEnvironmentId` (string | null, required)
  - `prNumber` (integer (int64) | null, required)
  - `prTitle` (string, required)
  - `projectId` (string, required)
  - `slug` (string, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## DELETE /environments/{environmentId}

Delete an empty environment

Fails with 409 `environment_not_empty` while the environment still has services.

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

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

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

## POST /environments/{environmentId}/clone

Clone an environment

Creates a new, non-production environment in the same project with a staged copy of every service of the source: configuration, variables, the environment's shared variables, volume definitions, links, repository links and canvas layout. Slugs are kept, so references such as `${{postgres.DATABASE_URL}}` point at the cloned siblings. Secrets are copied sealed and generated values are generated again. Databases are created new and empty: restore a dump into them. Volumes are new and empty. Domains are dropped (default) or cloned with a suffix; 409 `domain_conflict` when a suffixed hostname is taken. Route services and Compose stacks are skipped and listed. Nothing runs until the services are deployed. `dryRun` returns the plan and changes nothing.

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.
- `environmentId` (path, string, required)

Request body (application/json, required): CloneEnvironmentInputBody
  - `domainSuffix` (string): Default: -<new environment slug>
  - `domains` (enum: drop | suffix): drop (default) clones no domain; suffix clones each public domain with domainSuffix added to its first label
  - `dryRun` (boolean): Report what the clone would do and change nothing
  - `name` (string, required)
  - `serverId` (string): Place every cloned service on this server (default: each service keeps its server)

Response 200: OK (CloneResult)
  - `domains` (CloneDomainOutcome[] | null, required)
  - `dryRun` (boolean, required)
  - `environment` (CloneEnvironmentRef)
  - `links` (integer (int64), required)
  - `references` (ReferenceFinding[] | null, required)
  - `service` (ServiceView)
  - `services` (ClonedService[] | null, required)
  - `sharedVariables` (integer (int64), required)
  - `skipped` (CloneSkipped[] | null, required)
  - `warnings` (string[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict (`domain_conflict`); 422 Unprocessable Entity; 500 Internal Server Error

## GET /projects/{projectId}/environments

List a project's environments

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

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

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

## POST /projects/{projectId}/environments

Create an 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.
- `projectId` (path, string, required)

Request body (application/json, required): CreateEnvironmentInputBody
  - `isProduction` (boolean)
  - `name` (string, required)

Response 201: Created (Environment)
  - `createdAt` (string (date-time), required)
  - `id` (string, required)
  - `isProduction` (boolean, required)
  - `kind` (enum: standard | preview, required)
  - `name` (string, required)
  - `parentEnvironmentId` (string | null, required)
  - `prNumber` (integer (int64) | null, required)
  - `prTitle` (string, required)
  - `projectId` (string, required)
  - `slug` (string, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict; 422 Unprocessable Entity; 500 Internal Server Error
