# Services

### List the services in an environment

`GET /accounts/{accountId}/projects/{projectId}/environments/{environmentId}/services`

**Required role:** reader

**Parameters**

- `environmentId` _(path)_ · `string` · required
- `projectId` _(path)_ · `string` · required
- `accountId` _(path)_ · `string` · required

**Returns**

- `200` · `ServiceDto[]`

**Example request**

```bash
curl https://api.koo.io/accounts/:accountId/projects/:projectId/environments/:environmentId/services \
  -H "Authorization: Bearer kc_your_api_token"
```

**Example response** (`200`)

```json
[
  {
    "id": "svc_01example0000000000000000x",
    "name": "my-app",
    "type": "web",
    "cpu": 1,
    "memory": 1,
    "exposed": true,
    "source": {
      "type": "git",
      "git": {
        "provider": "github",
        "repoUrl": "https://example.com",
        "branch": "example",
        "autoDeploy": true
      },
      "image": {
        "ref": "example"
      }
    },
    "imagePullCredentialsSet": true,
    "runtime": {
      "port": 8080,
      "protocol": "http",
      "healthCheckPath": "example",
      "entrypoint": {
        "command": "example",
        "args": [
          "example"
        ]
      }
    },
    "serverless": {
      "enabled": true
    },
    "cron": {
      "enabled": true,
      "schedule": "example"
    },
    "volume": {
      "id": "svc_01example0000000000000000x",
      "mountPath": "example",
      "sizeGb": 0,
      "name": "my-app"
    },
    "locations": [
      {
        "name": "my-app",
        "replicas": 1
      }
    ],
    "domains": [
      {
        "hostname": "example",
        "mode": "cname",
        "status": "pending",
        "statusReason": "example",
        "dnsRecords": [
          {
            "type": "TXT",
            "name": "my-app",
            "value": "example"
          }
        ]
      }
    ],
    "status": {
      "applied": "pending",
      "build": "pending",
      "health": "online",
      "errors": [
        {
          "source": "applied",
          "code": "dns_propagating",
          "resource": "example",
          "message": "example",
          "at": "2026-01-01T00:00:00.000Z"
        }
      ],
      "warnings": [
        {
          "source": "applied",
          "code": "dns_propagating",
          "resource": "example",
          "message": "example",
          "at": "2026-01-01T00:00:00.000Z"
        }
      ],
      "url": "https://example.com",
      "lastDeployment": {
        "id": "svc_01example0000000000000000x",
        "version": 3
      },
      "appliedAt": "2026-01-01T00:00:00.000Z",
      "statusObservedAt": "2026-01-01T00:00:00.000Z"
    },
    "internalHost": "example",
    "connection": {
      "key": "example"
    }
  }
]
```

### Create a service in an environment

`POST /accounts/{accountId}/projects/{projectId}/environments/{environmentId}/services`

**Required role:** editor

Creates the service. A deployable web service (git/image source) starts rolling out immediately — create is deploy.

**Parameters**

- `environmentId` _(path)_ · `string` · required
- `projectId` _(path)_ · `string` · required
- `accountId` _(path)_ · `string` · required

**Request body**

- `name` · `string` · required — Service name — lowercase letters, digits and dashes (3–16 chars, starts with a letter). The name is the service identity within the environment: renaming creates a new service.
- `type` · `"web" | "postgres" | "redis"` · required — Service kind: `web` runs your own code; `postgres`/`redis` are managed addons. Immutable per name.
- `cpu` · `integer` · required — CPU limit in millicores (250, 500, or whole-vCPU multiples of 1000).
- `memory` · `integer` · required — Memory limit in MiB (256, 512, or whole-GiB multiples of 1024).
- `exposed` · `boolean` · optional — Expose the service publicly with its own URL (web services only).
- `addonCredentials` · `object` · optional — Optional credential overrides for a managed database addon (postgres/redis), applied at create time. Ignored for web services.
- `imageCredentials` · `object` · optional — Registry credentials for a private image source (requires an image source with a ref). Write-only — a read returns only `imagePullCredentialsSet`.
- `variables` · `string[]` · optional — Variables to apply atomically with the service create (same shape as POST …/variables).
- `source` · `object` · optional — Where the deployable artifact comes from.
- `runtime` · `object` · optional — Runtime configuration (port, protocol, health check, entrypoint). Variables are managed separately via the /variables endpoints.
- `serverless` · `object` · optional — Run as a scale-to-zero serverless workload (web only; mutually exclusive with cron/volume).
- `cron` · `object` · optional — Run as a scheduled cron job (web only; mutually exclusive with serverless/volume).
- `volume` · `object` · optional — Attach, create, or detach a persistent volume (web only; mutually exclusive with serverless/cron).
- `locations` · `object[]` · optional — Locations this service runs in, each with its replica count.
- `domains` · `string[]` · optional — The full desired set of custom hostnames routed to this exposed service. Omit to leave unchanged; send `[]` to remove all. Re-read the service to see each hostname’s DNS records + live status.

**Returns**

- `201` · `ServiceDto`

Attributes of `ServiceDto`:

- `id` · `string` · read-only — Unique identifier of the service (read-only).
- `name` · `string` · required — Service name — lowercase letters, digits and dashes (3–16 chars, starts with a letter). The name is the service identity within the environment: renaming creates a new service.
- `type` · `"web" | "postgres" | "redis"` · required — Service kind: `web` runs your own code; `postgres`/`redis` are managed addons.
- `cpu` · `integer` · required — CPU limit in millicores (250, 500, or whole-vCPU multiples of 1000).
- `memory` · `integer` · required — Memory limit in MiB (256, 512, or whole-GiB multiples of 1024).
- `exposed` · `boolean` · required — Whether the service is publicly reachable with its own URL (web services only).
- `source` · `object` · optional — Where the deployable artifact comes from.
- `imagePullCredentialsSet` · `boolean` · read-only — Read-only: true when private-image registry credentials are stored for this service. The credentials themselves are never returned.
- `runtime` · `object` · optional — Runtime configuration (port, protocol, health check, entrypoint). Variables are managed separately.
- `serverless` · `object` · optional — Present when the service runs as a scale-to-zero serverless workload.
- `cron` · `object` · optional — Present when the service runs as a scheduled cron job.
- `volume` · `object` · optional — Present when the service has an attached persistent volume.
- `locations` · `object[]` · required — Locations this service runs in, each with its replica count.
- `domains` · `object[]` · required — Read-only: the custom domains routing to this service, with DNS records + live status.
- `status` · `object` · read-only — Read-only: live status of the service.
- `internalHost` · `string` · read-only — Read-only: the internal DNS host other services reach this one at, for service-to-service wiring.
- `connection` · `Record<string, string>` · read-only — Read-only: non-secret connection details for a managed addon (postgres/redis).

**Example request**

```bash
curl https://api.koo.io/accounts/:accountId/projects/:projectId/environments/:environmentId/services \
  -X POST \
  -H "Authorization: Bearer kc_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
      "name": "my-app",
      "type": "web",
      "cpu": 1,
      "memory": 1
    }'
```

**Example response** (`201`)

```json
{
  "id": "svc_01example0000000000000000x",
  "name": "my-app",
  "type": "web",
  "cpu": 1,
  "memory": 1,
  "exposed": true,
  "source": {
    "type": "git",
    "git": {
      "provider": "github",
      "repoUrl": "https://example.com",
      "branch": "example",
      "autoDeploy": true
    },
    "image": {
      "ref": "example"
    }
  },
  "imagePullCredentialsSet": true,
  "runtime": {
    "port": 8080,
    "protocol": "http",
    "healthCheckPath": "example",
    "entrypoint": {
      "command": "example",
      "args": [
        "example"
      ]
    }
  },
  "serverless": {
    "enabled": true
  },
  "cron": {
    "enabled": true,
    "schedule": "example"
  },
  "volume": {
    "id": "svc_01example0000000000000000x",
    "mountPath": "example",
    "sizeGb": 0,
    "name": "my-app"
  },
  "locations": [
    {
      "name": "my-app",
      "replicas": 1
    }
  ],
  "domains": [
    {
      "hostname": "example",
      "mode": "cname",
      "status": "pending",
      "statusReason": "example",
      "dnsRecords": [
        {
          "type": "TXT",
          "name": "my-app",
          "value": "example"
        }
      ]
    }
  ],
  "status": {
    "applied": "pending",
    "build": "pending",
    "health": "online",
    "errors": [
      {
        "source": "applied",
        "code": "dns_propagating",
        "resource": "example",
        "message": "example",
        "at": "2026-01-01T00:00:00.000Z"
      }
    ],
    "warnings": [
      {
        "source": "applied",
        "code": "dns_propagating",
        "resource": "example",
        "message": "example",
        "at": "2026-01-01T00:00:00.000Z"
      }
    ],
    "url": "https://example.com",
    "lastDeployment": {
      "id": "svc_01example0000000000000000x",
      "version": 3
    },
    "appliedAt": "2026-01-01T00:00:00.000Z",
    "statusObservedAt": "2026-01-01T00:00:00.000Z"
  },
  "internalHost": "example",
  "connection": {
    "key": "example"
  }
}
```

### Get a service

`GET /accounts/{accountId}/projects/{projectId}/environments/{environmentId}/services/{name}`

**Required role:** reader

**Parameters**

- `name` _(path)_ · `string` · required — Service name within the environment.
- `environmentId` _(path)_ · `string` · required
- `projectId` _(path)_ · `string` · required
- `accountId` _(path)_ · `string` · required

**Returns**

- `200` · `ServiceDto`

Attributes of `ServiceDto`:

- `id` · `string` · read-only — Unique identifier of the service (read-only).
- `name` · `string` · required — Service name — lowercase letters, digits and dashes (3–16 chars, starts with a letter). The name is the service identity within the environment: renaming creates a new service.
- `type` · `"web" | "postgres" | "redis"` · required — Service kind: `web` runs your own code; `postgres`/`redis` are managed addons.
- `cpu` · `integer` · required — CPU limit in millicores (250, 500, or whole-vCPU multiples of 1000).
- `memory` · `integer` · required — Memory limit in MiB (256, 512, or whole-GiB multiples of 1024).
- `exposed` · `boolean` · required — Whether the service is publicly reachable with its own URL (web services only).
- `source` · `object` · optional — Where the deployable artifact comes from.
- `imagePullCredentialsSet` · `boolean` · read-only — Read-only: true when private-image registry credentials are stored for this service. The credentials themselves are never returned.
- `runtime` · `object` · optional — Runtime configuration (port, protocol, health check, entrypoint). Variables are managed separately.
- `serverless` · `object` · optional — Present when the service runs as a scale-to-zero serverless workload.
- `cron` · `object` · optional — Present when the service runs as a scheduled cron job.
- `volume` · `object` · optional — Present when the service has an attached persistent volume.
- `locations` · `object[]` · required — Locations this service runs in, each with its replica count.
- `domains` · `object[]` · required — Read-only: the custom domains routing to this service, with DNS records + live status.
- `status` · `object` · read-only — Read-only: live status of the service.
- `internalHost` · `string` · read-only — Read-only: the internal DNS host other services reach this one at, for service-to-service wiring.
- `connection` · `Record<string, string>` · read-only — Read-only: non-secret connection details for a managed addon (postgres/redis).

**Example request**

```bash
curl https://api.koo.io/accounts/:accountId/projects/:projectId/environments/:environmentId/services/:name \
  -H "Authorization: Bearer kc_your_api_token"
```

**Example response** (`200`)

```json
{
  "id": "svc_01example0000000000000000x",
  "name": "my-app",
  "type": "web",
  "cpu": 1,
  "memory": 1,
  "exposed": true,
  "source": {
    "type": "git",
    "git": {
      "provider": "github",
      "repoUrl": "https://example.com",
      "branch": "example",
      "autoDeploy": true
    },
    "image": {
      "ref": "example"
    }
  },
  "imagePullCredentialsSet": true,
  "runtime": {
    "port": 8080,
    "protocol": "http",
    "healthCheckPath": "example",
    "entrypoint": {
      "command": "example",
      "args": [
        "example"
      ]
    }
  },
  "serverless": {
    "enabled": true
  },
  "cron": {
    "enabled": true,
    "schedule": "example"
  },
  "volume": {
    "id": "svc_01example0000000000000000x",
    "mountPath": "example",
    "sizeGb": 0,
    "name": "my-app"
  },
  "locations": [
    {
      "name": "my-app",
      "replicas": 1
    }
  ],
  "domains": [
    {
      "hostname": "example",
      "mode": "cname",
      "status": "pending",
      "statusReason": "example",
      "dnsRecords": [
        {
          "type": "TXT",
          "name": "my-app",
          "value": "example"
        }
      ]
    }
  ],
  "status": {
    "applied": "pending",
    "build": "pending",
    "health": "online",
    "errors": [
      {
        "source": "applied",
        "code": "dns_propagating",
        "resource": "example",
        "message": "example",
        "at": "2026-01-01T00:00:00.000Z"
      }
    ],
    "warnings": [
      {
        "source": "applied",
        "code": "dns_propagating",
        "resource": "example",
        "message": "example",
        "at": "2026-01-01T00:00:00.000Z"
      }
    ],
    "url": "https://example.com",
    "lastDeployment": {
      "id": "svc_01example0000000000000000x",
      "version": 3
    },
    "appliedAt": "2026-01-01T00:00:00.000Z",
    "statusObservedAt": "2026-01-01T00:00:00.000Z"
  },
  "internalHost": "example",
  "connection": {
    "key": "example"
  }
}
```

### Update a service spec

`PATCH /accounts/{accountId}/projects/{projectId}/environments/{environmentId}/services/{name}`

**Required role:** editor

A deployable source change rolls out automatically.

**Parameters**

- `name` _(path)_ · `string` · required — Service name within the environment.
- `environmentId` _(path)_ · `string` · required
- `projectId` _(path)_ · `string` · required
- `accountId` _(path)_ · `string` · required

**Request body**

- `cpu` · `integer` · optional — CPU limit in millicores (250, 500, or whole-vCPU multiples of 1000).
- `memory` · `integer` · optional — Memory limit in MiB (256, 512, or whole-GiB multiples of 1024).
- `exposed` · `boolean` · optional — Expose the service publicly with its own URL (web services only).
- `confirmDataLoss` · `boolean` · optional — Acknowledge permanent data loss when a change destroys a persistent volume (e.g. switching a volume service to another mode without first detaching). Required for that specific change.
- `confirmAppImpact` · `boolean` · optional — Acknowledge that unexposing this service degrades an active published app that points at it (the listing loses its Visit URL). Required for that specific change.
- `imageCredentials` · `object | null` · optional — Credentials for pulling a private container image (write-only).
- `source` · `object` · optional — Where the deployable artifact comes from.
- `runtime` · `object` · optional — Runtime configuration (port, protocol, health check, entrypoint). Variables are managed separately via the /variables endpoints.
- `serverless` · `object` · optional — Run as a scale-to-zero serverless workload (web only; mutually exclusive with cron/volume).
- `cron` · `object` · optional — Run as a scheduled cron job (web only; mutually exclusive with serverless/volume).
- `volume` · `object` · optional — Attach, create, or detach a persistent volume (web only; mutually exclusive with serverless/cron).
- `locations` · `object[]` · optional — Locations this service runs in, each with its replica count.
- `domains` · `string[]` · optional — The full desired set of custom hostnames routed to this exposed service. Omit to leave unchanged; send `[]` to remove all. Re-read the service to see each hostname’s DNS records + live status.

**Returns**

- `200` · `ServiceDto`

Attributes of `ServiceDto`:

- `id` · `string` · read-only — Unique identifier of the service (read-only).
- `name` · `string` · required — Service name — lowercase letters, digits and dashes (3–16 chars, starts with a letter). The name is the service identity within the environment: renaming creates a new service.
- `type` · `"web" | "postgres" | "redis"` · required — Service kind: `web` runs your own code; `postgres`/`redis` are managed addons.
- `cpu` · `integer` · required — CPU limit in millicores (250, 500, or whole-vCPU multiples of 1000).
- `memory` · `integer` · required — Memory limit in MiB (256, 512, or whole-GiB multiples of 1024).
- `exposed` · `boolean` · required — Whether the service is publicly reachable with its own URL (web services only).
- `source` · `object` · optional — Where the deployable artifact comes from.
- `imagePullCredentialsSet` · `boolean` · read-only — Read-only: true when private-image registry credentials are stored for this service. The credentials themselves are never returned.
- `runtime` · `object` · optional — Runtime configuration (port, protocol, health check, entrypoint). Variables are managed separately.
- `serverless` · `object` · optional — Present when the service runs as a scale-to-zero serverless workload.
- `cron` · `object` · optional — Present when the service runs as a scheduled cron job.
- `volume` · `object` · optional — Present when the service has an attached persistent volume.
- `locations` · `object[]` · required — Locations this service runs in, each with its replica count.
- `domains` · `object[]` · required — Read-only: the custom domains routing to this service, with DNS records + live status.
- `status` · `object` · read-only — Read-only: live status of the service.
- `internalHost` · `string` · read-only — Read-only: the internal DNS host other services reach this one at, for service-to-service wiring.
- `connection` · `Record<string, string>` · read-only — Read-only: non-secret connection details for a managed addon (postgres/redis).

**Example request**

```bash
curl https://api.koo.io/accounts/:accountId/projects/:projectId/environments/:environmentId/services/:name \
  -X PATCH \
  -H "Authorization: Bearer kc_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**Example response** (`200`)

```json
{
  "id": "svc_01example0000000000000000x",
  "name": "my-app",
  "type": "web",
  "cpu": 1,
  "memory": 1,
  "exposed": true,
  "source": {
    "type": "git",
    "git": {
      "provider": "github",
      "repoUrl": "https://example.com",
      "branch": "example",
      "autoDeploy": true
    },
    "image": {
      "ref": "example"
    }
  },
  "imagePullCredentialsSet": true,
  "runtime": {
    "port": 8080,
    "protocol": "http",
    "healthCheckPath": "example",
    "entrypoint": {
      "command": "example",
      "args": [
        "example"
      ]
    }
  },
  "serverless": {
    "enabled": true
  },
  "cron": {
    "enabled": true,
    "schedule": "example"
  },
  "volume": {
    "id": "svc_01example0000000000000000x",
    "mountPath": "example",
    "sizeGb": 0,
    "name": "my-app"
  },
  "locations": [
    {
      "name": "my-app",
      "replicas": 1
    }
  ],
  "domains": [
    {
      "hostname": "example",
      "mode": "cname",
      "status": "pending",
      "statusReason": "example",
      "dnsRecords": [
        {
          "type": "TXT",
          "name": "my-app",
          "value": "example"
        }
      ]
    }
  ],
  "status": {
    "applied": "pending",
    "build": "pending",
    "health": "online",
    "errors": [
      {
        "source": "applied",
        "code": "dns_propagating",
        "resource": "example",
        "message": "example",
        "at": "2026-01-01T00:00:00.000Z"
      }
    ],
    "warnings": [
      {
        "source": "applied",
        "code": "dns_propagating",
        "resource": "example",
        "message": "example",
        "at": "2026-01-01T00:00:00.000Z"
      }
    ],
    "url": "https://example.com",
    "lastDeployment": {
      "id": "svc_01example0000000000000000x",
      "version": 3
    },
    "appliedAt": "2026-01-01T00:00:00.000Z",
    "statusObservedAt": "2026-01-01T00:00:00.000Z"
  },
  "internalHost": "example",
  "connection": {
    "key": "example"
  }
}
```

### Delete a service

`DELETE /accounts/{accountId}/projects/{projectId}/environments/{environmentId}/services/{name}`

**Required role:** editor

Deleting a service that backs an active published app degrades its listing; pass confirmAppImpact to proceed (412 otherwise).

**Parameters**

- `name` _(path)_ · `string` · required — Service name within the environment.
- `environmentId` _(path)_ · `string` · required
- `projectId` _(path)_ · `string` · required
- `accountId` _(path)_ · `string` · required

**Request body**

- `confirmAppImpact` · `boolean` · optional — Must be true to proceed when this service backs an active published app: deleting it leaves the listing degraded.

**Returns**

- `204`

**Example request**

```bash
curl https://api.koo.io/accounts/:accountId/projects/:projectId/environments/:environmentId/services/:name \
  -X DELETE \
  -H "Authorization: Bearer kc_your_api_token" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### Reveal a managed service’s connection details (incl. password)

`GET /accounts/{accountId}/projects/{projectId}/environments/{environmentId}/services/{name}/connection`

**Required role:** editor

The host/port/user/db + the connection URL and password for a postgres/redis service. Editor+ and audited; web services have no connection details (422).

**Parameters**

- `name` _(path)_ · `string` · required — Service name within the environment.
- `environmentId` _(path)_ · `string` · required
- `projectId` _(path)_ · `string` · required
- `accountId` _(path)_ · `string` · required

**Returns**

- `200` · `ServiceConnectionDto`

**Example request**

```bash
curl https://api.koo.io/accounts/:accountId/projects/:projectId/environments/:environmentId/services/:name/connection \
  -H "Authorization: Bearer kc_your_api_token"
```

**Example response** (`200`)

```json
{
  "key": "example"
}
```

### Get the data to open an interactive shell into a running replica

`GET /accounts/{accountId}/projects/{projectId}/environments/{environmentId}/services/{name}/exec-connection`

**Required role:** editor

Returns the target location + container choices for the PROXIED interactive shell. The koo API bridges the replica exec WebSocket server-side (the Control Plane cluster endpoint + the exec-scoped token stay server-side); list replicas via exec-replicas and connect via the exec WebSocket. Editor+ and audited.

**Parameters**

- `name` _(path)_ · `string` · required — Service name within the environment.
- `environmentId` _(path)_ · `string` · required
- `projectId` _(path)_ · `string` · required
- `accountId` _(path)_ · `string` · required

**Returns**

- `200` · `ExecConnectionDto`

Attributes of `ExecConnectionDto`:

- `location` · `string` · read-only — The location whose replicas the shell targets.
- `containers` · `string[]` · read-only — Container names in the workload (the shell target choices).

**Example request**

```bash
curl https://api.koo.io/accounts/:accountId/projects/:projectId/environments/:environmentId/services/:name/exec-connection \
  -H "Authorization: Bearer kc_your_api_token"
```

**Example response** (`200`)

```json
{
  "location": "example",
  "containers": [
    "example"
  ]
}
```

### List running replicas for an interactive shell

`GET /accounts/{accountId}/projects/{projectId}/environments/{environmentId}/services/{name}/exec-replicas`

**Required role:** editor

Proxies the Control Plane replica listing server-side (the browser cannot reach the cluster endpoint — CORS). Editor+ and audited.

**Parameters**

- `name` _(path)_ · `string` · required — Service name within the environment.
- `environmentId` _(path)_ · `string` · required
- `projectId` _(path)_ · `string` · required
- `accountId` _(path)_ · `string` · required

**Returns**

- `200` · `ExecReplicasDto`

Attributes of `ExecReplicasDto`:

- `items` · `string[]` · read-only — Running replica (pod) names available to open a shell into.

**Example request**

```bash
curl https://api.koo.io/accounts/:accountId/projects/:projectId/environments/:environmentId/services/:name/exec-replicas \
  -H "Authorization: Bearer kc_your_api_token"
```

**Example response** (`200`)

```json
{
  "items": [
    "example"
  ]
}
```
