# Deployments API

Deployments and releases. 13 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 /deployments/{deploymentId}

Get a deployment

`waitingFor` is set while the deployment waits for an offline server.

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

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

Response 200: OK (DeploymentView)
  - `actorLabel` (string, required)
  - `actorType` (string, required)
  - `build` (BuildInfo, required)
  - `buildServerId` (string, required)
  - `buildServerName` (string)
  - `cancelRequestedAt` (string (date-time) | null, required)
  - `cancelRequestedBy` (string, required)
  - `canceledAtStep` (string, required)
  - `commit` (CommitView, required)
  - `createdAt` (string (date-time), required)
  - `finishedAt` (string (date-time) | null, required)
  - `generation` (integer (int64), required)
  - `id` (string, required)
  - `message` (string | null, required)
  - `noCache` (boolean)
  - `release` (ReleaseView, required)
  - `releaseId` (string, required)
  - `rolledBackFrom` (string, required)
  - `serverId` (string, required)
  - `serviceId` (string, required)
  - `startedAt` (string (date-time) | null, required)
  - `status` (string, required)
  - `statusMessage` (string, required)
  - `strategy` (enum: rolling | recreate | route_only, required)
  - `trigger` (enum: manual | redeploy | rollback | staged | reconcile | drain | git_push | preview | cron | deploy_hook, required)
  - `waitingFor` (WaitingFor, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## POST /deployments/{deploymentId}/cancel

Cancel a deployment at any step

Before the agent acknowledged the deployment's command the deployment ends as `canceled` at once. Once the agent runs it, a cancel request is sent to the agent (`cancelRequestedAt` is set) and the deployment ends as `canceled` when the agent reports back, with the step it reached in `canceledAtStep`: a pull or build is stopped (a build's work directory is removed), containers of the new release are removed and the current release keeps serving, during verification the previous routes are restored first, and a recreate deploy that already stopped the previous release starts it again. If the previous release cannot be restored the result is `rollback_failed` with manual recovery steps. Once the new release is live it is too late: the deployment finishes (a still-running post-deploy command is stopped). An offline server receives the request when it reconnects. A finished deployment is 409 `cannot_cancel`.

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

Response 200: OK (DeploymentView)
  - `actorLabel` (string, required)
  - `actorType` (string, required)
  - `build` (BuildInfo, required)
  - `buildServerId` (string, required)
  - `buildServerName` (string)
  - `cancelRequestedAt` (string (date-time) | null, required)
  - `cancelRequestedBy` (string, required)
  - `canceledAtStep` (string, required)
  - `commit` (CommitView, required)
  - `createdAt` (string (date-time), required)
  - `finishedAt` (string (date-time) | null, required)
  - `generation` (integer (int64), required)
  - `id` (string, required)
  - `message` (string | null, required)
  - `noCache` (boolean)
  - `release` (ReleaseView, required)
  - `releaseId` (string, required)
  - `rolledBackFrom` (string, required)
  - `serverId` (string, required)
  - `serviceId` (string, required)
  - `startedAt` (string (date-time) | null, required)
  - `status` (string, required)
  - `statusMessage` (string, required)
  - `strategy` (enum: rolling | recreate | route_only, required)
  - `trigger` (enum: manual | redeploy | rollback | staged | reconcile | drain | git_push | preview | cron | deploy_hook, required)
  - `waitingFor` (WaitingFor, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict (`cannot_cancel`); 422 Unprocessable Entity; 500 Internal Server Error

## GET /deployments/{deploymentId}/commands

A deployment's agent commands, redacted

Every command the control plane sent for the deployment (deploy, build_image, cancel_deploy, …): kind, server, delivery state, enqueue/ack/finish times, outcome and error, with the payload and result as persisted. Variable values, tokens, credentials and file contents never appear: those fields are masked. Needs the same access as the deployment's logs.

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

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

Response 200: OK (CommandsOutputBody)
  - `items` (CommandView[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## GET /deployments/{deploymentId}/events

A deployment's step timeline

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

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

Response 200: OK (EventsOutputBody)
  - `items` (EventView[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## GET /deployments/{deploymentId}/release

The release a deployment shipped, for debugging

The release id and generation, the resolved source (image ref and digest, or git commit), how it was built and where it ran, the configuration snapshot with secrets and file contents masked, and the keys of its variables with which are secret, references or provided by the platform. Never a variable value. 404 when the deployment has no release yet.

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

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

Response 200: OK (ReleaseDetailView)
  - `build` (BuildInfo, required)
  - `config` (any, required)
  - `createdAt` (string (date-time), required)
  - `deploymentId` (string, required)
  - `generation` (integer (int64), required)
  - `id` (string, required)
  - `imageRef` (string, required)
  - `placement` (ReleasePlacement, required)
  - `serviceId` (string, required)
  - `source` (ReleaseSource, required)
  - `variables` (ReleaseVariableView[] | null, required)
  - `variablesPruned` (boolean, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## POST /deployments/{deploymentId}/rollback

Roll back to the release of a successful deployment

Application image and configuration only. Databases, volumes and external services are not rolled back. Creates a new deployment of the old release (`rolledBackFrom` is the deployment that was running). A git release whose built image was pruned returns 409 `image_pruned` with `commitSha` in the details: rebuild the commit instead.

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

Response 202: Accepted (RollbackView)
  - `actorLabel` (string, required)
  - `actorType` (string, required)
  - `build` (BuildInfo, required)
  - `buildServerId` (string, required)
  - `buildServerName` (string)
  - `cancelRequestedAt` (string (date-time) | null, required)
  - `cancelRequestedBy` (string, required)
  - `canceledAtStep` (string, required)
  - `commit` (CommitView, required)
  - `createdAt` (string (date-time), required)
  - `disclaimer` (string, required)
  - `finishedAt` (string (date-time) | null, required)
  - `generation` (integer (int64), required)
  - `id` (string, required)
  - `message` (string | null, required)
  - `noCache` (boolean)
  - `release` (ReleaseView, required)
  - `releaseId` (string, required)
  - `rolledBackFrom` (string, required)
  - `serverId` (string, required)
  - `serviceId` (string, required)
  - `startedAt` (string (date-time) | null, required)
  - `status` (string, required)
  - `statusMessage` (string, required)
  - `strategy` (enum: rolling | recreate | route_only, required)
  - `trigger` (enum: manual | redeploy | rollback | staged | reconcile | drain | git_push | preview | cron | deploy_hook, required)
  - `waitingFor` (WaitingFor, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict (`image_pruned`); 422 Unprocessable Entity; 500 Internal Server Error

## POST /environments/{environmentId}/deploy-staged

Deploy every service with staged changes

Services start in reference order: providers of `${{service.KEY}}` references first (otherwise databases first; a reference cycle keeps the input order). A consumer's deployment waits until the providers of the same batch finished (at most 10 minutes). `order` lists the service ids in the order they were started. Services that cannot deploy are skipped unless named in `serviceIds`.

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): DeployStagedInputBody
  - `message` (string): Optional note shown with every deployment of the batch
  - `noCache` (boolean): Build every git service of the batch without cache (--no-cache --pull) for this batch only; other services deploy as usual
  - `serviceIds` (string[] | null): Limit to these services (default: every service with staged changes)

Response 202: Accepted (StagedResult)
  - `items` (DeploymentView[] | null, required)
  - `order` (string[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict; 422 Unprocessable Entity; 500 Internal Server Error

## GET /environments/{environmentId}/deployments

List the deployments of every service of an environment (newest first)

Filter by `status` (comma-separated) and `serviceId`.

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

Parameters:
- `environmentId` (path, string, required)
- `status` (query, string): Comma-separated statuses, e.g. failed,succeeded
- `serviceId` (query, string (uuid)): Only deployments of this service
- `cursor` (query, string): Opaque cursor from a previous page's nextCursor
- `limit` (query, integer (int64)): Page size (default 50, max 200)

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

## GET /environments/{environmentId}/deployments/daily

Deployment outcomes per UTC day for an environment

One entry per day, oldest first, ending today (`days` 1..90, default 30); days without deployments are zeros. `failed` counts failed and rollback_failed, `other` counts canceled, rolled_back and in-flight deployments.

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

Parameters:
- `environmentId` (path, string, required)
- `days` (query, integer (int64)): Window length in UTC days, ending today

Response 200: OK (DailyOutcomes)
  - `days` (DailyOutcome[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## POST /services/{serviceId}/build-plan

Preview how a git service would be built

Asks the agent of the service's server to clone the branch head and report the build method (the repository's Dockerfile, Railpack or a generated starter Dockerfile), why it applies, and for Railpack the detected providers, packages, steps and start command. Nothing is built or deployed. The request waits for the agent (up to 150 s): 409 `server_offline` when the server is offline, 504 `build_plan_timeout` when it does not answer. Requires `deployments:write` because it makes the server clone the repository and run the planner.

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

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

Response 200: OK (BuildPlanResult)
  - `commitSha` (string)
  - `dockerfileFound` (boolean, required)
  - `dockerfilePath` (string, required)
  - `info` (BuildInfo)
  - `message` (string)
  - `messages` (PlanLog[] | null)
  - `outcome` (string, required)
  - `requested` (string, required)
  - `suggestedStarter` (string)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict (`server_offline`); 422 Unprocessable Entity; 500 Internal Server Error; 504 Gateway Timeout (`build_plan_timeout`)

## GET /services/{serviceId}/deployments

List a service's deployments (newest first)

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

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

Response 200: OK (PageDeploymentView)
  - `items` (DeploymentView[] | 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 /services/{serviceId}/deployments

Deploy a service

Returns 202 with the deployment; it runs asynchronously. 409 `deployment_in_progress` while another deployment of the service is active; 422 for blocking problems (no server, revoked server, unresolved variable references).

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

Request body (application/json): DeployInputBody1
  - `commitSha` (string): Git services only: build this full 40-character commit instead of the branch head ("Rebuild commit").
  - `message` (string): Optional note shown with the deployment
  - `noCache` (boolean): Git services only: build this one deployment without any cache (--no-cache --pull, and no reuse of an image a build server already pushed). Later deployments build normally. 422 agent_too_old if the server's agent predates this option.

Response 202: Accepted (DeploymentView)
  - `actorLabel` (string, required)
  - `actorType` (string, required)
  - `build` (BuildInfo, required)
  - `buildServerId` (string, required)
  - `buildServerName` (string)
  - `cancelRequestedAt` (string (date-time) | null, required)
  - `cancelRequestedBy` (string, required)
  - `canceledAtStep` (string, required)
  - `commit` (CommitView, required)
  - `createdAt` (string (date-time), required)
  - `finishedAt` (string (date-time) | null, required)
  - `generation` (integer (int64), required)
  - `id` (string, required)
  - `message` (string | null, required)
  - `noCache` (boolean)
  - `release` (ReleaseView, required)
  - `releaseId` (string, required)
  - `rolledBackFrom` (string, required)
  - `serverId` (string, required)
  - `serviceId` (string, required)
  - `startedAt` (string (date-time) | null, required)
  - `status` (string, required)
  - `statusMessage` (string, required)
  - `strategy` (enum: rolling | recreate | route_only, required)
  - `trigger` (enum: manual | redeploy | rollback | staged | reconcile | drain | git_push | preview | cron | deploy_hook, required)
  - `waitingFor` (WaitingFor, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict (`deployment_in_progress`); 422 Unprocessable Entity; 500 Internal Server Error

## POST /services/{serviceId}/deployments/plan

Plan a deployment (dry run)

Reports the strategy (rolling or recreate, with reasons), warnings, the pending changes, the resolved source and blocking problems. Nothing is started.

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

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

Response 200: OK (Plan)
  - `blocking` (PlanBlocking[] | null, required)
  - `changes` (FieldChange[] | null, required)
  - `mesh` (MeshPlan, required)
  - `placement` (PlacementView, required)
  - `reasons` (string[] | null, required)
  - `server` (ServerRef, required)
  - `source` (PlanSource, required)
  - `strategy` (enum: rolling | recreate | route_only, required)
  - `warnings` (string[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error
