# Deployments

> Long-lived application instances ZopDay runs in your Kubernetes cluster, built from a connected git repo and managed as a Helm release.

Source: https://zop.dev/developer-docs/deployments/deployments

---

A deployment is a long-lived application instance ZopDay runs in your Kubernetes cluster. It lives inside an [environment](https://zop.dev/docs/deployments/projects), is built from a connected git repository, and is rolled out through a Helm release that ZopDay manages on your behalf.

**Info**

- **Deployment**: a long-lived app instance in one environment (e.g. `checkout` in `prod-us-east`).
- **Revision**: an immutable image + config snapshot. Each push, config change, or rollback creates a new revision.
- **Rollback**: revert to a previous revision; the rollback itself is recorded as a new revision so history is append-only.

## Lifecycle

1. Client calls `POST /deployments`. ZopDay records the deployment and queues its first build.
2. The repository is cloned, the stack is detected, and a [GitHub Actions build](https://zop.dev/docs/deployments/builds) produces the image.
3. When the build completes, ZopDay applies a Helm release to the target namespace on the deployment's [deployment space](https://zop.dev/docs/deployments/deployment-spaces), using short-lived cluster credentials minted from the connected cloud account.
4. Every stage boundary appends an event to the deployment's [events timeline](#events-timeline), and the deployment's status always reflects the live state.
5. Subsequent pushes (auto-deploy) or config changes create new revisions, rolled out as rolling updates.
6. `DELETE /deployments/{id}` uninstalls the release and cleans up its revisions and events, then removes the deployment. Repeating the delete is safe.

## Imported (Helm) Services

Besides git- and image-sourced deployments, the cluster import flow can bring **existing Helm releases** into a project as deployments with `sourceType: "helm-import"`. These are catalog entries for workloads whose lifecycle stays with Helm on the cluster. ZopDay did not build or roll them out, so the UI treats them as **view-only**: the canvas card carries an *Imported · Read-only* badge and is not clickable, edit / redeploy / delete are hidden wherever the service is inspected, and no revision history exists (`currentRevisionId` stays empty). Manage or remove the underlying release with Helm (or re-run the import): the deployment row is a mirror, not a control surface.

## Create a Deployment

Two equivalent forms: a flat path for integrations and CI scripts, and an env-nested form for the canvas (which already has the environment in context).

### POST /deployments

Create a deployment and queue its first build.

```bash title="Request"
curl -X POST https://api.zop.dev/deployments \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "checkout",
    "envId": "env_abc",
    "sourceType": "git",
    "repoUrl": "https://github.com/acme/checkout",
    "branch": "main",
    "integrationId": "int_github_001",
    "autoDeploy": true,
    "configs": {
      "port": 8080,
      "envVars": { "DB_HOST": "checkout-db.zopnight.internal", "LOG_LEVEL": "info" },
      "secretRefs": ["secret://checkout-db-password"]
    }
  }'
```

### POST /environments/{envID}/deployments

Env-nested create. Same payload, used by the canvas.

## List & Get

### GET /deployments

List all deployments visible to the caller across every environment.

### GET /environments/{envID}/deployments

List deployments in one environment.

### GET /deployments/{id}

Get a deployment, hydrated with current revision, configs, and the env

```json title="Response"
{
  "data": {
    "id": "dep_xyz",
    "name": "checkout",
    "orgId": "org_acme",
    "envId": "env_abc",
    "sourceType": "git",
    "repoUrl": "https://github.com/acme/checkout",
    "branch": "main",
    "integrationId": "int_github_001",
    "autoDeploy": true,
    "spaceId": "spc_abc",
    "namespace": "checkout-prod",
    "registryUrl": "123456789012.dkr.ecr.us-east-1.amazonaws.com/checkout",
    "currentRevisionId": "rev_005",
    "status": "active",
    "configs": "{\"port\":8080,\"envVars\":{\"LOG_LEVEL\":\"info\"}}",
    "canvasPosition": { "x": 320, "y": 180 },
    "lastDeployAt": "2026-04-29T11:30:00Z",
    "createdAt": "2026-04-15T08:00:00Z",
    "updatedAt": "2026-04-29T11:30:00Z"
  }
}
```

### DELETE /deployments/{id}

Tear down a deployment: uninstall its Helm release, clean up revisions and events, then remove the deployment. Safe to repeat.

## Update Configs

Env-var, secret, port, replica, and resource changes go through `PATCH /deployments/{id}/configs`. The new configs take effect on the next revision and are applied as a rolling update. Configs are stored as JSON; the recognised fields are `port`, `replicas`, `liveness`, `resources`, `expose` and `rootPath`.

### PATCH /deployments/{id}/configs

Patch deployment configs.

```bash title="Request"
curl -X PATCH https://api.zop.dev/deployments/dep_xyz/configs \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "configs": {
      "envVars": { "LOG_LEVEL": "debug" },
      "port": 8080
    }
  }'
```

## Canvas Position

### PATCH /deployments/{id}/canvas-position

Persist the (x, y) of a deployment card on the canvas. Body: { x, y }, both finite numbers.

## Analyze a Deployment

### POST /deployments/{id}/analyze

Re-run deploy-readiness checks against the current source + configs. Returns warnings (missing health probe, missing resource requests, etc.) without creating a revision.

## Revisions

Every image or config change is captured as a revision. Revisions are immutable. A redelivered GitHub push does not create a second revision; see [Webhooks](https://zop.dev/docs/deployments/webhooks).

### POST /deployments/{id}/revisions

Create a new revision (typically from CI after a build completes).

```bash title="Request"
curl -X POST https://api.zop.dev/deployments/dep_xyz/revisions \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "imageRef": "123456789012.dkr.ecr.us-east-1.amazonaws.com/checkout:v1.4.3"
  }'
```

`imageRef` is required for image-sourced deployments. Other fields: `dockerfile`, `envVars`,
`deployedBy`, `configOnly` (`true` to roll out a config change without a new image) and `values`
(for imported Helm services).

### GET /deployments/{id}/revisions

List revisions for a deployment.

### GET /deployments/{id}/revisions/{revID}

Get a specific revision.

## Rollback

### POST /deployments/{id}/rollback

Roll back to a prior revision. The target revision is cloned into a new revision and applied.

```bash title="Request"
curl -X POST https://api.zop.dev/deployments/dep_xyz/rollback \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "revisionId": "<revision id>" }'
```

## Events Timeline

### GET /deployments/{id}/events

Append-only chronological list of deploy stage events. Used by the canvas drawer

```json title="Response"
{
  "data": [
    { "id": "ev_1", "stage": "building",    "status": "started",   "createdAt": "2026-04-29T11:00:00Z" },
    { "id": "ev_2", "stage": "building",    "status": "succeeded", "meta": { "imageRef": "..." },           "createdAt": "2026-04-29T11:04:30Z" },
    { "id": "ev_3", "stage": "deploying",   "status": "started",                                            "createdAt": "2026-04-29T11:04:35Z" },
    { "id": "ev_4", "stage": "rolling_out", "status": "succeeded", "meta": { "ready": 3, "desired": 3 },    "createdAt": "2026-04-29T11:05:10Z" },
    { "id": "ev_5", "stage": "active",      "status": "succeeded", "meta": { "address": "a-1234.elb..." }, "createdAt": "2026-04-29T11:06:50Z" }
  ]
}
```

## LoadBalancer Status

For deployments that expose ports as `http` or `tcp`, ZopDay provisions a cloud LoadBalancer through the cluster's ingress class. The endpoint below is what the UI polls during the post-deploy "Waiting for public address…" state.

### GET /deployments/{id}/loadbalancer

Get LoadBalancer status (assigned address, listeners, TLS certificate).

```json title="Response"
{
  "data": {
    "status": "ready",
    "address": "a-12345.elb.us-east-1.amazonaws.com",
    "ports": [
      { "port": 80,  "protocol": "HTTP" },
      { "port": 443, "protocol": "HTTPS" }
    ],
    "tls": { "issuer": "letsencrypt-prod", "expiresAt": "2026-07-29T00:00:00Z" }
  }
}
```

See [Builds](https://zop.dev/docs/deployments/builds) for how images are produced, [Projects & Environments](https://zop.dev/docs/deployments/projects) for how deployments are grouped, and [Deployment Spaces](https://zop.dev/docs/deployments/deployment-spaces) for the cluster they target.
