---
title: "Containers — API reference"
description: "Gyro Containers (ECS), Istanbul tr-ist-1: run Docker images on a node pool dedicated to you (no other customer's containers on your nodes). Services w"
url: https://www.gyrohosting.com/developers/en/api/containers.html
language: en
openapi: https://www.gyrohosting.com/developers/openapi.json
api_version: 2.30.0
---

# Containers

Gyro Containers (ECS), Istanbul tr-ist-1: run Docker images on a node pool dedicated to you (no other customer's containers on your nodes). Services with zero-downtime deploys, revision history and rollback, scaling, environment variables and encrypted secrets, HTTPS address <name>-<code>.gyroapp.net plus your own domains (free certificates), logs and metrics. Order the environment like any product (`/cloud/services` lists it); manage it here.

Base URL: `https://www.gyrohosting.com/api/v1/client` · OpenAPI: https://www.gyrohosting.com/developers/openapi.json


## GET /cloud/containers/{id}/services

**List container services** · operationId `typed_get_containers__id__services` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__services.json

Services of the environment with state (running, deploying, failing, pending, stopped, suspended), ready/desired instances, size and public URL. Each item carries `internal_address` (services with a port); the list carries `namespace` and `cluster_dns`. Each service has `alerts` = {"min_ready": N} at the top level (0 = off; the same value as `spec.alerts`).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container service list 12345
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/services

**Create a container service** · operationId `typed_post_containers__id__services` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__services.json

Starts a new service on your dedicated node pool. Name: lowercase letters, digits and '-', max 40. Fails with 409 `quota_exceeded` when the pool has no room. Secret values are set separately (PUT .../secrets). **Serverless environments:** `cpu_m` / `memory_mi` must be one of the pairs from `GET /cloud/billing/container-sizes` (0.25-4 vCPU); billing is per running copy per hour (`POST /cloud/billing/estimate` with `type: serverless`). Refused with 422 `invalid_replicas` (`details.max` = your plan's limit) when the count is outside 0..max copies per service. `alerts` takes one field, `min_ready` (0 = off); `autoscaling` takes enabled, min, max, cpu_percent, memory_percent. Any other field inside them is refused with 422 `invalid_alerts` / `invalid_autoscaling` (it used to be dropped silently).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `name` (required) | string | Service name |
| `image` (required) | string | Image reference, e.g. ghcr.io/acme/web:1.4 (public registries or your private registry) |
| `port` | integer | Container port (0 = no network port). Containers run as a non-root user: use a port >= 1024 |
| `replicas` | integer | Number of instances (default 1) |
| `cpu_m` | integer | CPU per instance in millicores (250 = 0.25 vCPU) |
| `memory_mi` | integer | Memory per instance in MiB |
| `public` | boolean | HTTPS address <name>-<code>.gyroapp.net (and your domains) |
| `health_path` | string | HTTP health check path (empty = TCP check) |
| `args` | array<string> | Arguments for the image's default command. A JSON array of strings, e.g. ["sh","-c","echo a && npm start"]. A plain string is accepted too and split like a shell: whitespace separates, quotes keep text together ("..." with \" escapes, '...' literal): `sh -c "echo a && npm start"` -> ["sh","-c","echo a && npm start"]. A string starting with [ must be a valid JSON array; an unclosed quote is an error. |
| `command` | array<string> | Override the image entrypoint. A JSON array of strings, e.g. ["sh","-c","echo a && npm start"]. A plain string is accepted too and split like a shell: whitespace separates, quotes keep text together ("..." with \" escapes, '...' literal): `sh -c "echo a && npm start"` -> ["sh","-c","echo a && npm start"]. A string starting with [ must be a valid JSON array; an unclosed quote is an error. |
| `env` | object | Plain environment variables (replaces the whole set on update) |
| `run_as_user` | integer | User id inside the container (default 10001) |
| `autoscaling` | object | Autoscaling (horizontal): replicas follow the average CPU (and optionally memory) use of the reserved size. `enabled:false` = off (fixed replicas). |
| `autoscaling.enabled` | boolean |  |
| `autoscaling.min` | integer |  |
| `autoscaling.max` | integer | <= plan replica limit |
| `autoscaling.cpu_percent` | integer |  |
| `autoscaling.memory_percent` | integer | 0 = off, else 10-95 |
| `alerts` | object | Alert threshold: notify when fewer replicas are ready (0 = off). Channels: PUT /cloud/containers/{id}/notifications. |
| `alerts.min_ready` | integer |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "string", "image": "string", "port": 1, "replicas": 1, "cpu_m": 1, "memory_mi": 1, "public": true, "health_path": "string", "args": ["string"], "command": ["string"], "env": {}, "run_as_user": 1, "autoscaling": {"enabled": true, "min": 1, "max": 1, "cpu_percent": 1, "memory_percent": 1}, "alerts": {"min_ready": 1}, "confirm": true}'
```
```bash
gyro container service create 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services", headers=h, json={"name": "string", "image": "string", "port": 1, "replicas": 1, "cpu_m": 1, "memory_mi": 1, "public": True, "health_path": "string", "args": ["string"], "command": ["string"], "env": {}, "run_as_user": 1, "autoscaling": {"enabled": True, "min": 1, "max": 1, "cpu_percent": 1, "memory_percent": 1}, "alerts": {"min_ready": 1}, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"name": "string", "image": "string", "port": 1, "replicas": 1, "cpu_m": 1, "memory_mi": 1, "public": true, "health_path": "string", "args": ["string"], "command": ["string"], "env": {}, "run_as_user": 1, "autoscaling": {"enabled": true, "min": 1, "max": 1, "cpu_percent": 1, "memory_percent": 1}, "alerts": {"min_ready": 1}, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/services/{name}

**Get a container service** · operationId `typed_get_containers__id__services__name` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__services__name.json

Spec, state, problem code (ImagePullBackOff, CrashLoopBackOff, OOMKilled, Unschedulable ...), instances, revision, domains and the names of secrets (values are never returned). `autoscaling` = settings + live status (current/desired replicas, current CPU/memory %, at_max), `capacity` = {blocked, fits}. The answer carries `namespace` and, for services with a port, `internal` = `{host, short, port: 80, target_port, address, url}`: other services of the same environment reach it at `internal.address` (`<name>.<namespace>.svc.cluster.local:80`) or `<name>:80`. Each service has `alerts` = {"min_ready": N} at the top level (0 = off; the same value as `spec.alerts`).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container service get 12345
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PATCH /cloud/containers/{id}/services/{name}

**Deploy a new version / change settings** · operationId `typed_patch_containers__id__services__name` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_patch_containers__id__services__name.json

Send only the fields to change (e.g. `image`). Every change except `replicas` creates a new revision and rolls out instance by instance without downtime. With autoscaling on, `replicas` is ignored (the autoscaler owns it). `autoscaling` / `alerts` are settings, not revisions. **Serverless environments:** `cpu_m` / `memory_mi` must be one of the pairs from `GET /cloud/billing/container-sizes` (0.25-4 vCPU); billing is per running copy per hour (`POST /cloud/billing/estimate` with `type: serverless`). Refused with 422 `invalid_replicas` (`details.max` = your plan's limit) when the count is outside 0..max copies per service. `alerts` takes one field, `min_ready` (0 = off); `autoscaling` takes enabled, min, max, cpu_percent, memory_percent. Any other field inside them is refused with 422 `invalid_alerts` / `invalid_autoscaling` (it used to be dropped silently).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `image` | string | Image reference, e.g. ghcr.io/acme/web:1.4 (public registries or your private registry) |
| `port` | integer | Container port (0 = no network port). Containers run as a non-root user: use a port >= 1024 |
| `replicas` | integer | Number of instances (default 1) |
| `cpu_m` | integer | CPU per instance in millicores (250 = 0.25 vCPU) |
| `memory_mi` | integer | Memory per instance in MiB |
| `public` | boolean | HTTPS address <name>-<code>.gyroapp.net (and your domains) |
| `health_path` | string | HTTP health check path (empty = TCP check) |
| `args` | array<string> | Arguments for the image's default command. A JSON array of strings, e.g. ["sh","-c","echo a && npm start"]. A plain string is accepted too and split like a shell: whitespace separates, quotes keep text together ("..." with \" escapes, '...' literal): `sh -c "echo a && npm start"` -> ["sh","-c","echo a && npm start"]. A string starting with [ must be a valid JSON array; an unclosed quote is an error. |
| `command` | array<string> | Override the image entrypoint. A JSON array of strings, e.g. ["sh","-c","echo a && npm start"]. A plain string is accepted too and split like a shell: whitespace separates, quotes keep text together ("..." with \" escapes, '...' literal): `sh -c "echo a && npm start"` -> ["sh","-c","echo a && npm start"]. A string starting with [ must be a valid JSON array; an unclosed quote is an error. |
| `env` | object | Plain environment variables (replaces the whole set on update) |
| `run_as_user` | integer | User id inside the container (default 10001) |
| `reason` | string | Free text shown in the revision history (e.g. a git sha) |
| `autoscaling` | object | Autoscaling (horizontal): replicas follow the average CPU (and optionally memory) use of the reserved size. `enabled:false` = off (fixed replicas). |
| `autoscaling.enabled` | boolean |  |
| `autoscaling.min` | integer |  |
| `autoscaling.max` | integer | <= plan replica limit |
| `autoscaling.cpu_percent` | integer |  |
| `autoscaling.memory_percent` | integer | 0 = off, else 10-95 |
| `alerts` | object | Alert threshold: notify when fewer replicas are ready (0 = off). Channels: PUT /cloud/containers/{id}/notifications. |
| `alerts.min_ready` | integer |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PATCH 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"image": "string", "port": 1, "replicas": 1, "cpu_m": 1, "memory_mi": 1, "public": true, "health_path": "string", "args": ["string"], "command": ["string"], "env": {}, "run_as_user": 1, "reason": "string", "autoscaling": {"enabled": true, "min": 1, "max": 1, "cpu_percent": 1, "memory_percent": 1}, "alerts": {"min_ready": 1}, "confirm": true}'
```
```bash
gyro container service update 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.patch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1", headers=h, json={"image": "string", "port": 1, "replicas": 1, "cpu_m": 1, "memory_mi": 1, "public": True, "health_path": "string", "args": ["string"], "command": ["string"], "env": {}, "run_as_user": 1, "reason": "string", "autoscaling": {"enabled": True, "min": 1, "max": 1, "cpu_percent": 1, "memory_percent": 1}, "alerts": {"min_ready": 1}, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1", {
  method: "PATCH",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"image": "string", "port": 1, "replicas": 1, "cpu_m": 1, "memory_mi": 1, "public": true, "health_path": "string", "args": ["string"], "command": ["string"], "env": {}, "run_as_user": 1, "reason": "string", "autoscaling": {"enabled": true, "min": 1, "max": 1, "cpu_percent": 1, "memory_percent": 1}, "alerts": {"min_ready": 1}, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/containers/{id}/services/{name}

**Delete a container service** · operationId `typed_delete_containers__id__services__name` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_delete_containers__id__services__name.json

Deletes the service, its address, secrets, revision history and certificates. Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X DELETE 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container service delete 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/services/{name}/scale

**Scale a service** · operationId `typed_post_containers__id__services__name__scale` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__services__name__scale.json

Sets the number of instances (0 = stop, settings kept). Does not create a revision. Refused with 409 `autoscaling_enabled` while autoscaling is on (change min/max instead). Refused with 422 `invalid_replicas` (`details.max` = your plan's limit) when the count is outside 0..max copies per service.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `replicas` (required) | integer |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/scale' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"replicas": 1, "confirm": true}'
```
```bash
gyro container service scale 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/scale", headers=h, json={"replicas": 1, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/scale", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"replicas": 1, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/services/{name}/restart

**Restart a service** · operationId `typed_post_containers__id__services__name__restart` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__services__name__restart.json

Replaces the instances one by one (rolling restart).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/restart' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container service restart 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/restart", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/restart", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/services/{name}/rollback

**Roll back to an earlier revision** · operationId `typed_post_containers__id__services__name__rollback` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__services__name__rollback.json

Restores image, size, command and variables of `revision` (default: the previous one) as a new revision. Replicas and secrets stay as they are. Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `revision` | integer | Revision number (empty = previous) |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/rollback' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"revision": 1, "confirm": true}'
```
```bash
gyro container service rollback 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/rollback", headers=h, json={"revision": 1, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/rollback", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"revision": 1, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/services/{name}/revisions

**Revision history** · operationId `typed_get_containers__id__services__name__revisions` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__services__name__revisions.json

Last 15 revisions, newest first, with image, size, variable names and reason.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/revisions' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container service revisions 12345
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/revisions", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/revisions", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/services/{name}/env

**Set environment variables** · operationId `typed_put_containers__id__services__name__env` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_put_containers__id__services__name__env.json

`set` merges variables (`{"KEY": "value"}` or KEY=VALUE lines), `unset` removes names, `replace: 1` replaces the whole set. Creates a revision and restarts the service. Only `set`, `unset` and `replace` are accepted: an unknown field (e.g. `env`) or a body that changes nothing (no `set`, no `unset`, no `replace: 1`) is rejected with **422 `invalid_env`** (`details.expected` lists the fields); nothing is saved and the service does not restart.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `set` | object |  |
| `unset` | array<string> |  |
| `replace` | integer (0, 1) |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: invalid_env: unknown field or nothing to change (details.expected lists set / unset / replace)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/env' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"set": {}, "unset": ["string"], "replace": 0, "confirm": true}'
```
```bash
gyro container env set 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/env", headers=h, json={"set": {}, "unset": ["string"], "replace": 0, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/env", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"set": {}, "unset": ["string"], "replace": 0, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/services/{name}/secrets

**Set secret values** · operationId `typed_put_containers__id__services__name__secrets` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_put_containers__id__services__name__secrets.json

Stored encrypted, passed to the container as environment variables, never returned. The service restarts.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `set` (required) | object |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/secrets' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"set": {}, "confirm": true}'
```
```bash
gyro container secret set 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/secrets", headers=h, json={"set": {}, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/secrets", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"set": {}, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/containers/{id}/services/{name}/secrets

**Delete secret values** · operationId `typed_delete_containers__id__services__name__secrets` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_delete_containers__id__services__name__secrets.json

`keys` (comma separated in the query). The service restarts. Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |
| `keys` | query | string | Secret names, comma separated |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X DELETE 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/secrets' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container secret delete 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/secrets", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/secrets", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/services/{name}/logs

**Service logs** · operationId `typed_get_containers__id__services__name__logs` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__services__name__logs.json

Lines `{time, instance, text}` oldest first. `since_minutes` (default 60, max 10080), `filter` (plain text), `limit` (max 2000), `previous=1` = last crashed instance.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |
| `since_minutes` | query | string | Time range in minutes |
| `filter` | query | string | Plain text filter |
| `limit` | query | string | Max lines |
| `previous` | query | string | 1 = previous (crashed) instance |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/logs' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container logs 12345
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/logs", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/logs", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/services/{name}/metrics

**Service CPU / memory** · operationId `typed_get_containers__id__services__name__metrics` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__services__name__metrics.json

`source=history`: series (cpu_m, memory_mi, restarts) for `hours`; `source=current`: current usage only.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |
| `hours` | query | string | History length in hours (default 24) |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/metrics' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container metrics 12345
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/metrics", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/metrics", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/services/{name}/domains

**Domains of a service** · operationId `typed_get_containers__id__services__name__domains` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__services__name__domains.json

Status `active` or `pending` (waiting for DNS).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container domain list 12345
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/services/{name}/domains

**Attach a domain** · operationId `typed_post_containers__id__services__name__domains` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__services__name__domains.json

Point a CNAME to the service host (`<name>-<code>.gyroapp.net`) first; the domain is activated (with a free certificate) once its DNS points to us, checked hourly or with .../domains/verify.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `domain` (required) | string |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domain": "string", "confirm": true}'
```
```bash
gyro container domain add 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains", headers=h, json={"domain": "string", "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"domain": "string", "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/services/{name}/domains/verify

**Re-check pending domains** · operationId `typed_post_containers__id__services__name__domains_verify` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__services__name__domains_verify.json

Checks DNS now and activates domains that point here.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains/verify' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container domain verify 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains/verify", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains/verify", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/containers/{id}/services/{name}/domains/{domain}

**Detach a domain** · operationId `typed_delete_containers__id__services__name__domains__domain` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_delete_containers__id__services__name__domains__domain.json

Removes the domain and its certificate. Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |
| `domain` (required) | path | string | Domain name |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X DELETE 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains/1' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container domain remove 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains/1", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/domains/1", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/services/{name}/autoscaling

**Autoscaling / alert threshold of a service** · operationId `typed_put_containers__id__services__name__autoscaling` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_put_containers__id__services__name__autoscaling.json

Body `{"autoscaling": {"enabled": true, "min": 1, "max": 5, "cpu_percent": 70, "memory_percent": 0}, "alerts": {"min_ready": 2}}`. Replicas are added when the average use exceeds the target (at most double per 30 s) and removed one per minute after 3 calm minutes. Max cannot exceed the plan replica limit; replicas that do not fit in the node pool wait (`warnings: max_exceeds_capacity`, alert `capacity`). Not a revision. Switching off keeps the current replica count (or `replicas`). Refused with 422 `invalid_replicas` (`details.max` = your plan's limit) when the count is outside 0..max copies per service. `alerts` takes one field, `min_ready` (0 = off); `autoscaling` takes enabled, min, max, cpu_percent, memory_percent. Any other field inside them is refused with 422 `invalid_alerts` / `invalid_autoscaling` (it used to be dropped silently).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `name` (required) | path | string | Service name |

### Request body

| Field | Type | Description |
|---|---|---|
| `autoscaling` | object | Autoscaling (horizontal): replicas follow the average CPU (and optionally memory) use of the reserved size. `enabled:false` = off (fixed replicas). |
| `autoscaling.enabled` | boolean |  |
| `autoscaling.min` | integer |  |
| `autoscaling.max` | integer | <= plan replica limit |
| `autoscaling.cpu_percent` | integer |  |
| `autoscaling.memory_percent` | integer | 0 = off, else 10-95 |
| `alerts` | object | Alert threshold: notify when fewer replicas are ready (0 = off). Channels: PUT /cloud/containers/{id}/notifications. |
| `alerts.min_ready` | integer |  |
| `replicas` | integer | Fixed replicas when switching autoscaling off |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/autoscaling' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"autoscaling": {"enabled": true, "min": 1, "max": 1, "cpu_percent": 1, "memory_percent": 1}, "alerts": {"min_ready": 1}, "replicas": 1, "confirm": true}'
```
```bash
gyro container service autoscale 12345 --help    # all flags
```
```hcl
# resource gyro_container_service: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_service
# A web service in your Gyro Containers (ECS) environment (Istanbul).
# service_id is the id of the container environment you ordered (see gyro_services).
variable "db_password" {
  type      = string
  sensitive = true
}

resource "gyro_container_service" "web" {
  service_id  = 123
  name        = "web"
  image       = "ghcr.io/acme/web:1.4"
  port        = 8080
  cpu_m       = 500
  memory_mi   = 512
  public      = true
  health_path = "/healthz"

  env     = { MODE = "prod" }
  secrets = { DB_PASSWORD = var.db_password } # write only, never read back

  # Replicas follow the load: 2..6, target 70 % CPU of cpu_m (replicas is ignored while autoscaling is set).
  autoscaling = {
    min         = 2
    max         = 6
    cpu_percent = 70
  }
  # Alert when fewer than 2 replicas are ready (channels: gyro_container_notifications).
  notification = {
    min_ready = 2
  }

  # Point a CNAME from your domain to gyro_container_service.web.host first.
  domains = ["app.example.com"]
}

output "web_url" {
  value = gyro_container_service.web.url
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/autoscaling", headers=h, json={"autoscaling": {"enabled": True, "min": 1, "max": 1, "cpu_percent": 1, "memory_percent": 1}, "alerts": {"min_ready": 1}, "replicas": 1, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/services/1/autoscaling", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"autoscaling": {"enabled": true, "min": 1, "max": 1, "cpu_percent": 1, "memory_percent": 1}, "alerts": {"min_ready": 1}, "replicas": 1, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/notifications

**Notification settings and current alerts** · operationId `typed_get_containers__id__notifications` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__notifications.json

`settings` (channels, events, cooldown; the webhook secret is never returned), `conditions` = what is wrong right now, `history` = last 30 alerts. Events: down (no ready replica for >3 min), crashloop (keeps restarting / image pull fails / out of memory), replicas_low (below alerts.min_ready), scale_max (autoscaling wants more than max), capacity (replicas wait for room in the node pool), egress (the egress IPs of the environment changed; one message, no resolved). An event is announced after ~2 minutes, at most once per cooldown per service + event (flapping included), with a 'resolved' message.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container notifications get 12345
```
```hcl
# resource gyro_container_notifications: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_notifications
# Alert channels of a Gyro Containers (ECS) environment.
resource "gyro_container_notifications" "alerts" {
  service_id  = 123
  email       = true
  sms         = false
  telegram    = "123456789" # send /start to @gyrouptime_bot, it replies with your chat id
  webhook_url = "https://ops.example.com/gyro-alerts"
  events      = ["down", "crashloop", "replicas_low", "scale_max", "capacity"]
  cooldown    = 30
}

# Verify webhooks: X-Gyro-Signature = "sha256=" + HMAC-SHA256(secret, X-Gyro-Timestamp + "." + body)
output "webhook_secret" {
  value     = gyro_container_notifications.alerts.webhook_secret
  sensitive = true
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/notifications

**Set notification channels** · operationId `typed_put_containers__id__notifications` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_put_containers__id__notifications.json

Send only the fields to change. A webhook secret is generated when a webhook URL is first set (POST .../notifications/webhook-secret returns it). No WhatsApp.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `email` | boolean | Account e-mail (default on) |
| `sms` | boolean | Account phone |
| `telegram` | string | Telegram chat id (send /start to the platform bot, e.g. @gyrouptime_bot); empty = off |
| `webhook_url` | string | https URL of a public server; JSON POST signed with X-Gyro-Signature: sha256=HMAC(secret, X-Gyro-Timestamp + '.' + body) |
| `events` | string | Comma separated: down, crashloop, replicas_low, scale_max, capacity, egress |
| `cooldown` | integer | Minutes between two announcements of the same service + event (5-1440, default 30) |
| `resolved` | boolean | Send a 'resolved' message (default on) |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email": true, "sms": true, "telegram": "string", "webhook_url": "string", "events": "string", "cooldown": 1, "resolved": true, "confirm": true}'
```
```bash
gyro container notifications set 12345 --help    # all flags
```
```hcl
# resource gyro_container_notifications: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_notifications
# Alert channels of a Gyro Containers (ECS) environment.
resource "gyro_container_notifications" "alerts" {
  service_id  = 123
  email       = true
  sms         = false
  telegram    = "123456789" # send /start to @gyrouptime_bot, it replies with your chat id
  webhook_url = "https://ops.example.com/gyro-alerts"
  events      = ["down", "crashloop", "replicas_low", "scale_max", "capacity"]
  cooldown    = 30
}

# Verify webhooks: X-Gyro-Signature = "sha256=" + HMAC-SHA256(secret, X-Gyro-Timestamp + "." + body)
output "webhook_secret" {
  value     = gyro_container_notifications.alerts.webhook_secret
  sensitive = true
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications", headers=h, json={"email": True, "sms": True, "telegram": "string", "webhook_url": "string", "events": "string", "cooldown": 1, "resolved": True, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"email": true, "sms": true, "telegram": "string", "webhook_url": "string", "events": "string", "cooldown": 1, "resolved": true, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/notifications/test

**Send a test notification** · operationId `typed_post_containers__id__notifications_test` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__notifications_test.json

Sends a test message to every enabled channel; `channels` = per channel result. At most 5 per 10 minutes.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications/test' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container notifications test 12345 --help    # all flags
```
```hcl
# resource gyro_container_notifications: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_notifications
# Alert channels of a Gyro Containers (ECS) environment.
resource "gyro_container_notifications" "alerts" {
  service_id  = 123
  email       = true
  sms         = false
  telegram    = "123456789" # send /start to @gyrouptime_bot, it replies with your chat id
  webhook_url = "https://ops.example.com/gyro-alerts"
  events      = ["down", "crashloop", "replicas_low", "scale_max", "capacity"]
  cooldown    = 30
}

# Verify webhooks: X-Gyro-Signature = "sha256=" + HMAC-SHA256(secret, X-Gyro-Timestamp + "." + body)
output "webhook_secret" {
  value     = gyro_container_notifications.alerts.webhook_secret
  sensitive = true
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications/test", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications/test", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/notifications/webhook-secret

**Webhook signing secret** · operationId `typed_post_containers__id__notifications_webhook_secret` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__notifications_webhook_secret.json

Returns the signing secret (`rotate: true` makes a new one; not returned when the request carries an Idempotency-Key).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `rotate` | boolean |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications/webhook-secret' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"rotate": true, "confirm": true}'
```
```bash
gyro container notifications webhook-secret 12345 --help    # all flags
```
```hcl
# resource gyro_container_notifications: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_notifications
# Alert channels of a Gyro Containers (ECS) environment.
resource "gyro_container_notifications" "alerts" {
  service_id  = 123
  email       = true
  sms         = false
  telegram    = "123456789" # send /start to @gyrouptime_bot, it replies with your chat id
  webhook_url = "https://ops.example.com/gyro-alerts"
  events      = ["down", "crashloop", "replicas_low", "scale_max", "capacity"]
  cooldown    = 30
}

# Verify webhooks: X-Gyro-Signature = "sha256=" + HMAC-SHA256(secret, X-Gyro-Timestamp + "." + body)
output "webhook_secret" {
  value     = gyro_container_notifications.alerts.webhook_secret
  sensitive = true
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications/webhook-secret", headers=h, json={"rotate": True, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/notifications/webhook-secret", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"rotate": true, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/nodes

**Node pool** · operationId `typed_get_containers__id__nodes` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__nodes.json

Your dedicated nodes (aliases node-1..n), pool capacity and usage, ingress IPs for apex A records, private registry (user only). Also `namespace`, `cluster_dns` and `egress` (schema ContainerEgress).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/nodes' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container nodes 12345
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/nodes", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/nodes", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/nodes

**Set the extra node count** · operationId `setContainerNodes` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/setContainerNodes.json

Adds or removes extra nodes of the plan's node size (absolute `extra_nodes`, 0 = plan only; at most 20 nodes per environment in total, GyroKota may lower it). An extra node costs the same as a plan node, billed hourly from the prepaid balance. Removing drains first: replicas move to the remaining nodes (services restart one by one, health checked), then the node is deleted; it is not billed while drained. Refused with 409 `quota_in_use` when the remaining nodes cannot hold the running services. Serverless environments have no nodes. Needs `"confirm": true`. Every change of a dedicated environment's node set (plan change, extra node added or removed) is announced BEFORE the old nodes go: as soon as the new nodes' addresses are known (usually under a minute) the owner is notified (Alerts channels, event `egress`) and `egress_change` / `pending_egress_ips` show them. With the approval step on (`PUT /cloud/containers/{id}/egress-approval`, 1-72 hours, 24 recommended) the new nodes take no traffic and the old nodes keep serving until `POST /cloud/containers/{id}/egress-change/approve` or the deadline (then it continues by itself). A removal only never waits.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `extra_nodes` (required) | integer |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/nodes' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"extra_nodes": 1, "confirm": true}'
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/nodes", headers=h, json={"extra_nodes": 1, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/nodes", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"extra_nodes": 1, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}

**Container environment detail (namespace, internal addressing, egress IPs)** · operationId `getContainerEnvironment` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/getContainerEnvironment.json

Environment code, Kubernetes **namespace**, in-cluster addressing (`cluster_dns`), plan limits, nodes, quota, ingress IPs and the current **egress IPs**. Egress IPs of a **dedicated** environment = the public IPs of its own nodes + the shared pool node(s) used while an update rolls out (the new copy starts there for about a minute, then moves back to your node). **Serverless** = the shared sandboxed pool nodes (the pool can grow: a new address can appear). Build machines of Gyro Pipeline are listed separately (`build_ips`). The list changes when the nodes change (plan change, node replacement, pool scaling); the owner is notified (Alerts channels, event `egress`). No IPv6 egress; outbound TCP 25 is closed. A fixed egress address is not offered yet.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```hcl
# data source gyro_container_environment: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/data-sources/container_environment
data "gyro_container_environment" "app" {
  service_id = 12345
}

# Egress IPs: put them on the IP allow list of an external service (database, payment provider, partner API).
# They change when the environment's nodes change; the owner is notified and this list is always current.
output "egress_ips" {
  value = data.gyro_container_environment.app.egress_ips
}

# Other services of the same environment reach a service at <service>.<namespace>.svc.cluster.local:80 (or <service>:80).
output "api_internal_url" {
  value = "http://api.${data.gyro_container_environment.app.service_domain}"
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/containers/{id}

**Delete a container environment (prepaid)** · operationId `deleteContainerEnvironment` · scope `cloud_delete` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/deleteContainerEnvironment.json

Same as the panel's Spending > Delete: hourly charges stop at once and the environment is deleted right away with every service, node, registry image, pipeline and custom domain binding (no retention period). Needs `"confirm": true` (body or query) and an `Idempotency-Key` header; repeating returns 202 `deleted`. Environments billed by invoice answer 409 `not_prepaid`. Terraform: destroying `gyro_container_environment` calls this.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `Idempotency-Key` (required) | header | string |  |
| `confirm` | query | boolean |  |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Deleting
- **400**: idempotency_key_required
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X DELETE 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"confirm": true}'
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
h["Idempotency-Key"] = str(uuid.uuid4())
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/egress-ips

**Egress IPs of a container environment** · operationId `getContainerEgressIps` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/getContainerEgressIps.json

Current public IPv4 addresses the containers reach the internet from - add them to the IP allow list of an external service. Egress IPs of a **dedicated** environment = the public IPs of its own nodes + the shared pool node(s) used while an update rolls out (the new copy starts there for about a minute, then moves back to your node). **Serverless** = the shared sandboxed pool nodes (the pool can grow: a new address can appear). Build machines of Gyro Pipeline are listed separately (`build_ips`). The list changes when the nodes change (plan change, node replacement, pool scaling); the owner is notified (Alerts channels, event `egress`). No IPv6 egress; outbound TCP 25 is closed. A fixed egress address is not offered yet.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/egress-ips' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```hcl
# data source gyro_container_environment: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/data-sources/container_environment
data "gyro_container_environment" "app" {
  service_id = 12345
}

# Egress IPs: put them on the IP allow list of an external service (database, payment provider, partner API).
# They change when the environment's nodes change; the owner is notified and this list is always current.
output "egress_ips" {
  value = data.gyro_container_environment.app.egress_ips
}

# Other services of the same environment reach a service at <service>.<namespace>.svc.cluster.local:80 (or <service>:80).
output "api_internal_url" {
  value = "http://api.${data.gyro_container_environment.app.service_domain}"
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/egress-ips", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/egress-ips", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/events

**Recent warnings** · operationId `typed_get_containers__id__events` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__events.json

Last 50 platform warnings of the environment (pull errors, crashes, capacity).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/events' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container events 12345
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/events", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/events", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/egress-approval

**Approval step for egress changes** · operationId `setContainerEgressApproval` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/setContainerEgressApproval.json

`hours` 1-72 turns the approval step on (24 recommended), 0 turns it off. Every change of a dedicated environment's node set (plan change, extra node added or removed) is announced BEFORE the old nodes go: as soon as the new nodes' addresses are known (usually under a minute) the owner is notified (Alerts channels, event `egress`) and `egress_change` / `pending_egress_ips` show them. With the approval step on (`PUT /cloud/containers/{id}/egress-approval`, 1-72 hours, 24 recommended) the new nodes take no traffic and the old nodes keep serving until `POST /cloud/containers/{id}/egress-change/approve` or the deadline (then it continues by itself). A removal only never waits.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `hours` (required) | integer |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/egress-approval' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"hours": 24, "confirm": true}'
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/egress-approval", headers=h, json={"hours": 24, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/egress-approval", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"hours": 24, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/egress-change/approve

**Approve the pending egress change** · operationId `approveContainerEgressChange` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/approveContainerEgressChange.json

Continue now: the new nodes start taking traffic, the old ones are drained and removed. `id` (optional) must match `egress_change.id` (409 `change_mismatch` when a newer change replaced it). 409 `no_pending_change` / `change_preparing`. Every change of a dedicated environment's node set (plan change, extra node added or removed) is announced BEFORE the old nodes go: as soon as the new nodes' addresses are known (usually under a minute) the owner is notified (Alerts channels, event `egress`) and `egress_change` / `pending_egress_ips` show them. With the approval step on (`PUT /cloud/containers/{id}/egress-approval`, 1-72 hours, 24 recommended) the new nodes take no traffic and the old nodes keep serving until `POST /cloud/containers/{id}/egress-change/approve` or the deadline (then it continues by itself). A removal only never waits.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `id` | string |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/egress-change/approve' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id": "string", "confirm": true}'
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/egress-change/approve", headers=h, json={"id": "string", "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/egress-change/approve", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"id": "string", "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/external-registry

**External private registry credentials** · operationId `typed_put_containers__id__external_registry` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_put_containers__id__external_registry.json

One private registry per environment (e.g. ghcr.io + a pull-only token). The token is stored encrypted and never returned.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `server` (required) | string |  |
| `username` (required) | string |  |
| `password` (required) | string |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/external-registry' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"server": "string", "username": "string", "password": "string", "confirm": true}'
```
```bash
gyro container external-registry set 12345 --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/external-registry", headers=h, json={"server": "string", "username": "string", "password": "string", "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/external-registry", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"server": "string", "username": "string", "password": "string", "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/containers/{id}/external-registry

**Remove external registry credentials** · operationId `typed_delete_containers__id__external_registry` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_delete_containers__id__external_registry.json

Private images can no longer be pulled. Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X DELETE 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/external-registry' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container external-registry delete 12345 --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/external-registry", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/external-registry", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/registry

**Gyro image registry of the environment** · operationId `typed_get_containers__id__registry` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__registry.json

Host, image prefix `<host>/<code>/`, username, usage (`used_gb`, `free_gb` = 2, `billable_gb`, `cap_gb` = 50), `push_allowed` and the clean-up policy. Only this environment can push/pull its repositories. Above 2 GB: €0.05 per GB per month, charged hourly from the prepaid balance. The password is returned only by POST .../registry/credentials.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container registry get 12345
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/registry/credentials

**Registry password** · operationId `typed_post_containers__id__registry_credentials` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__registry_credentials.json

Returns username and password for `docker login` (not returned when the request carries an Idempotency-Key).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/credentials' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container registry credentials 12345 --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/credentials", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/credentials", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/registry/rotate

**Rotate the registry password** · operationId `typed_post_containers__id__registry_rotate` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_post_containers__id__registry_rotate.json

The old password stops working at once; running services are not affected (their pull secret is updated). Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/rotate' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container registry rotate 12345 --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/rotate", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/rotate", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/registry/policy

**Image clean-up policy** · operationId `typed_put_containers__id__registry_policy` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_put_containers__id__registry_policy.json

`enabled` (default true), `keep` 1-50 (images of the last N revisions of every service are kept, default 10), `untagged_days` 1-90 (images used by no revision are deleted after, default 7).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `enabled` | boolean |  |
| `keep` | integer |  |
| `untagged_days` | integer |  |
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/policy' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "keep": 1, "untagged_days": 1, "confirm": true}'
```
```bash
gyro container registry policy 12345 --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/policy", headers=h, json={"enabled": True, "keep": 1, "untagged_days": 1, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/policy", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"enabled": true, "keep": 1, "untagged_days": 1, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/registry/images

**Images in the registry** · operationId `typed_get_containers__id__registry_images` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_get_containers__id__registry_images.json

`images`: repo, tag, digest, bytes, created, in_use; `total_bytes` (unique layers).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Responses

- **200**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/images' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container registry images 12345
```
```hcl
# resource gyro_container_image: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_image
# Copy an image into the environment's private Gyro registry (no docker push), then run it.
resource "gyro_container_image" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  source     = "nginxinc/nginx-unprivileged:1.27-alpine"
  repo       = "web"
  tag        = "1.27"
}

# A private image: the token is used only for the copy (prefer a read-only token).
resource "gyro_container_image" "app" {
  service_id = 12345
  source     = "ghcr.io/gyro-ornek/uygulama:1.0"
  username   = "gyro-ornek"
  password   = var.ghcr_read_token
}

resource "gyro_container_service" "web" {
  service_id = 12345
  name       = "web"
  image      = gyro_container_image.web.image
  port       = 8080
  public     = true
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/images", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/images", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/containers/{id}/registry/images

**Delete an image tag** · operationId `typed_delete_containers__id__registry_images` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/typed_delete_containers__id__registry_images.json

Query `repo` (without the code prefix) and `tag`. Images used by a service are refused (409 image_in_use). Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `repo` | query | string | Repository, e.g. web |
| `tag` | query | string | Tag, e.g. 1.4 |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **202**: Tool data / result
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X DELETE 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/images' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container registry image-delete 12345 --help    # all flags
```
```hcl
# resource gyro_container_image: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_image
# Copy an image into the environment's private Gyro registry (no docker push), then run it.
resource "gyro_container_image" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  source     = "nginxinc/nginx-unprivileged:1.27-alpine"
  repo       = "web"
  tag        = "1.27"
}

# A private image: the token is used only for the copy (prefer a read-only token).
resource "gyro_container_image" "app" {
  service_id = 12345
  source     = "ghcr.io/gyro-ornek/uygulama:1.0"
  username   = "gyro-ornek"
  password   = var.ghcr_read_token
}

resource "gyro_container_service" "web" {
  service_id = 12345
  name       = "web"
  image      = gyro_container_image.web.image
  port       = 8080
  public     = true
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/images", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/images", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/registry/imports

**Image imports (progress and history)** · operationId `listContainerImageImports` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/listContainerImageImports.json

`active` = the running import (or null) with `phase`, `bytes_done`/`bytes_total`, `layers_done`/`layers`; `jobs` = the last 20 imports (newest first) with `state` and, when failed, `error` + `message`; `limits`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Container environment (service) id |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/imports' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container registry imports 12345
```
```hcl
# resource gyro_container_image: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_image
# Copy an image into the environment's private Gyro registry (no docker push), then run it.
resource "gyro_container_image" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  source     = "nginxinc/nginx-unprivileged:1.27-alpine"
  repo       = "web"
  tag        = "1.27"
}

# A private image: the token is used only for the copy (prefer a read-only token).
resource "gyro_container_image" "app" {
  service_id = 12345
  source     = "ghcr.io/gyro-ornek/uygulama:1.0"
  username   = "gyro-ornek"
  password   = var.ghcr_read_token
}

resource "gyro_container_service" "web" {
  service_id = 12345
  name       = "web"
  image      = gyro_container_image.web.image
  port       = 8080
  public     = true
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/imports", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/imports", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/registry/imports

**Import an image from another registry** · operationId `importContainerImage` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/importContainerImage.json

Copies an image (e.g. `nginx:1.27`, `docker.io/library/redis:7`, `ghcr.io/gyro-ornek/uygulama:1.0`) into the environment's Gyro registry without `docker push`. HTTPS registries only; only the linux/amd64 variant of multi-platform images is copied. `repo` (default: last part of the source name) and `tag` (default: source tag) name the copy under `<host>/<code>/`; an existing name:tag needs `overwrite: true`. Private images: `username` + `password` (token) are used only for this copy, never stored or returned, and redacted in the API log. Runs in the background: poll GET .../registry/imports. Limits: 1 running import per environment (409 `import_running`), 10 per hour (429 `import_rate_limited`), 15 minutes, 128 layers, size within the free registry space (50 GB cap, 2 GB free, then the usual €0.05/GB-month). Sources that resolve to private, internal or Gyro infrastructure addresses are refused (`source_blocked`).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Container environment (service) id |

### Request body

| Field | Type | Description |
|---|---|---|
| `source` (required) | string | source image (host/repo:tag or @sha256:...); docker.io when no host |
| `repo` | string | target name under your prefix (lowercase, max 3 levels) |
| `tag` | string | target tag (default: source tag, `latest` for a digest) |
| `username` | string | private source: username (not stored) |
| `password` | string | private source: token / password (not stored, redacted in logs) |
| `overwrite` | boolean | replace an existing name:tag |

### Responses

- **202**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)
- **429**: import_rate_limited

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/imports' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"source": "ghcr.io/gyro-ornek/uygulama:1.0", "repo": "uygulama", "tag": "1.0"}'
```
```bash
gyro container registry import 12345 --help    # all flags
```
```hcl
# resource gyro_container_image: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_image
# Copy an image into the environment's private Gyro registry (no docker push), then run it.
resource "gyro_container_image" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  source     = "nginxinc/nginx-unprivileged:1.27-alpine"
  repo       = "web"
  tag        = "1.27"
}

# A private image: the token is used only for the copy (prefer a read-only token).
resource "gyro_container_image" "app" {
  service_id = 12345
  source     = "ghcr.io/gyro-ornek/uygulama:1.0"
  username   = "gyro-ornek"
  password   = var.ghcr_read_token
}

resource "gyro_container_service" "web" {
  service_id = 12345
  name       = "web"
  image      = gyro_container_image.web.image
  port       = 8080
  public     = true
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/imports", headers=h, json={"source": "ghcr.io/gyro-ornek/uygulama:1.0", "repo": "uygulama", "tag": "1.0"}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/registry/imports", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"source": "ghcr.io/gyro-ornek/uygulama:1.0", "repo": "uygulama", "tag": "1.0"}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/pipelines

**List pipelines** · operationId `pipeline_list` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_list.json

Pipelines of the environment with the last run, plan limits (`concurrent`, `monthly_minutes`, `max_timeout`, `max_cpu_m`, `max_memory_mi`, `max_pipelines`) and this month's build minutes. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container pipeline list 12345
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/pipelines

**Create a pipeline** · operationId `pipeline_create` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_create.json

Connects a repository. The answer contains `webhook_url` and (only here and in GET .../webhook) `webhook_secret`: add both to the repository's webhook (GitHub: application/json + Secret, push event; GitLab: Secret token, push + tag push; Gitea: secret; other: X-Gyro-Signature HMAC). Optional `token` for a private repository.  `repo` is required unless `provider` is `archive` (archive pipelines build only uploaded archives: POST .../uploads, then a run with `upload_id`).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |

### Request body

| Field | Type | Description |
|---|---|---|
| `name` (required) | string | Display name (1-48) |
| `provider` | string (github, gitlab, gitea, git, archive) |  |
| `repo` | string | Required for github / gitlab / gitea / git: https clone URL (no credentials) or SSH address git@host:org/repo.git / ssh://user@host[:port]/path (needs a deploy key). Leave it out for provider `archive` (an archive pipeline has no repository; a value sent with `archive` is ignored and the answer shows `repo` empty). |
| `token` | string | Read-only access token for a private repository (stored encrypted, never returned; "-" on update removes it) |
| `git_user` | string | User name for the token (default: x-access-token / oauth2 / git) |
| `branch` | string | Branches whose pushes build (comma separated globs, empty = none) |
| `tags` | string | Tags that build (globs, e.g. v*) |
| `auto` | boolean | Webhook pushes start runs |
| `repo_file` | boolean | Read gyro-pipeline.yml from the repository (GitHub, GitLab, Gitea) |
| `image` (required) | string | Image name in the environment registry (<registry>/<code>/<image>:<sha7> + :latest) |
| `dockerfile` | string |  |
| `context` | string | Build folder (empty = repository root) |
| `build_target` | string | Stage of a multi-stage Dockerfile to build (`FROM node:20 AS production` -> `production`, same as `docker build --target`). Empty = the last stage. Stage names are case-insensitive (stored lowercase). A stage the Dockerfile does not have fails the run with `build_target_not_found`. gyro-pipeline.yml: `build_target:` overrides it per run. CLI: `--target` (or `--build-target`). |
| `targets` | array<string> | Services that get the new image (empty = build only) |
| `approval` | boolean | Wait for approval before deploying (24 h) |
| `auto_rollback` | boolean | Roll back when the new version is unhealthy |
| `build_image` | string | Image that runs `commands` (e.g. node:22-alpine) |
| `commands` | array<string> | Shell commands run in the repository root before the Dockerfile (tests, npm ci ...); build secrets are environment variables here |
| `build_env` | object | Variables for `commands` |
| `build_args` | object | Dockerfile ARG values (visible in the image history) |
| `cpu_m` | integer | Build CPU (millicores, plan limit) |
| `memory_mi` | integer | Build memory (MiB, plan limit) |
| `timeout_min` | integer | Build time limit (minutes, plan limit) |
| `verify_min` | integer | Health wait after deploy (1-30 minutes) |
| `notify` | array<string (failed, succeeded, approval, rolled_back)> | Notifications (channels: the environment's alert settings) |
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "string", "provider": "github", "repo": "https://github.com/gyro-ornek/uygulama", "token": "string", "git_user": "string", "branch": "string", "tags": "string", "auto": true, "repo_file": true, "image": "string", "dockerfile": "string", "context": "string", "build_target": "production", "targets": ["string"], "approval": true, "auto_rollback": true, "build_image": "string", "commands": ["string"], "build_env": {}, "build_args": {}, "cpu_m": 1, "memory_mi": 1, "timeout_min": 1, "verify_min": 1, "notify": ["failed"], "confirm": true}'
```
```bash
gyro container pipeline create 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines", headers=h, json={"name": "string", "provider": "github", "repo": "https://github.com/gyro-ornek/uygulama", "token": "string", "git_user": "string", "branch": "string", "tags": "string", "auto": True, "repo_file": True, "image": "string", "dockerfile": "string", "context": "string", "build_target": "production", "targets": ["string"], "approval": True, "auto_rollback": True, "build_image": "string", "commands": ["string"], "build_env": {}, "build_args": {}, "cpu_m": 1, "memory_mi": 1, "timeout_min": 1, "verify_min": 1, "notify": ["failed"], "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"name": "string", "provider": "github", "repo": "https://github.com/gyro-ornek/uygulama", "token": "string", "git_user": "string", "branch": "string", "tags": "string", "auto": true, "repo_file": true, "image": "string", "dockerfile": "string", "context": "string", "build_target": "production", "targets": ["string"], "approval": true, "auto_rollback": true, "build_image": "string", "commands": ["string"], "build_env": {}, "build_args": {}, "cpu_m": 1, "memory_mi": 1, "timeout_min": 1, "verify_min": 1, "notify": ["failed"], "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/pipelines/{pipeline}

**Get a pipeline** · operationId `pipeline_get` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_get.json

Settings, webhook address, build secret names (values never returned). 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container pipeline get 12345
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PATCH /cloud/containers/{id}/pipelines/{pipeline}

**Change a pipeline** · operationId `pipeline_update` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_update.json

Send only the fields to change. `token`: new value, or "-" to remove. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `name` | string | Display name (1-48) |
| `provider` | string (github, gitlab, gitea, git, archive) |  |
| `repo` | string | Required for github / gitlab / gitea / git: https clone URL (no credentials) or SSH address git@host:org/repo.git / ssh://user@host[:port]/path (needs a deploy key). Leave it out for provider `archive` (an archive pipeline has no repository; a value sent with `archive` is ignored and the answer shows `repo` empty). |
| `token` | string | Read-only access token for a private repository (stored encrypted, never returned; "-" on update removes it) |
| `git_user` | string | User name for the token (default: x-access-token / oauth2 / git) |
| `branch` | string | Branches whose pushes build (comma separated globs, empty = none) |
| `tags` | string | Tags that build (globs, e.g. v*) |
| `auto` | boolean | Webhook pushes start runs |
| `repo_file` | boolean | Read gyro-pipeline.yml from the repository (GitHub, GitLab, Gitea) |
| `image` | string | Image name in the environment registry (<registry>/<code>/<image>:<sha7> + :latest) |
| `dockerfile` | string |  |
| `context` | string | Build folder (empty = repository root) |
| `build_target` | string | Stage of a multi-stage Dockerfile to build (`FROM node:20 AS production` -> `production`, same as `docker build --target`). Empty = the last stage. Stage names are case-insensitive (stored lowercase). A stage the Dockerfile does not have fails the run with `build_target_not_found`. gyro-pipeline.yml: `build_target:` overrides it per run. CLI: `--target` (or `--build-target`). |
| `targets` | array<string> | Services that get the new image (empty = build only) |
| `approval` | boolean | Wait for approval before deploying (24 h) |
| `auto_rollback` | boolean | Roll back when the new version is unhealthy |
| `build_image` | string | Image that runs `commands` (e.g. node:22-alpine) |
| `commands` | array<string> | Shell commands run in the repository root before the Dockerfile (tests, npm ci ...); build secrets are environment variables here |
| `build_env` | object | Variables for `commands` |
| `build_args` | object | Dockerfile ARG values (visible in the image history) |
| `cpu_m` | integer | Build CPU (millicores, plan limit) |
| `memory_mi` | integer | Build memory (MiB, plan limit) |
| `timeout_min` | integer | Build time limit (minutes, plan limit) |
| `verify_min` | integer | Health wait after deploy (1-30 minutes) |
| `notify` | array<string (failed, succeeded, approval, rolled_back)> | Notifications (channels: the environment's alert settings) |
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PATCH 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "string", "provider": "github", "repo": "https://github.com/gyro-ornek/uygulama", "token": "string", "git_user": "string", "branch": "string", "tags": "string", "auto": true, "repo_file": true, "image": "string", "dockerfile": "string", "context": "string", "build_target": "production", "targets": ["string"], "approval": true, "auto_rollback": true, "build_image": "string", "commands": ["string"], "build_env": {}, "build_args": {}, "cpu_m": 1, "memory_mi": 1, "timeout_min": 1, "verify_min": 1, "notify": ["failed"], "confirm": true}'
```
```bash
gyro container pipeline update 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.patch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1", headers=h, json={"name": "string", "provider": "github", "repo": "https://github.com/gyro-ornek/uygulama", "token": "string", "git_user": "string", "branch": "string", "tags": "string", "auto": True, "repo_file": True, "image": "string", "dockerfile": "string", "context": "string", "build_target": "production", "targets": ["string"], "approval": True, "auto_rollback": True, "build_image": "string", "commands": ["string"], "build_env": {}, "build_args": {}, "cpu_m": 1, "memory_mi": 1, "timeout_min": 1, "verify_min": 1, "notify": ["failed"], "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1", {
  method: "PATCH",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"name": "string", "provider": "github", "repo": "https://github.com/gyro-ornek/uygulama", "token": "string", "git_user": "string", "branch": "string", "tags": "string", "auto": true, "repo_file": true, "image": "string", "dockerfile": "string", "context": "string", "build_target": "production", "targets": ["string"], "approval": true, "auto_rollback": true, "build_image": "string", "commands": ["string"], "build_env": {}, "build_args": {}, "cpu_m": 1, "memory_mi": 1, "timeout_min": 1, "verify_min": 1, "notify": ["failed"], "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/containers/{id}/pipelines/{pipeline}

**Delete a pipeline** · operationId `pipeline_delete` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_delete.json

Stops its running runs and deletes its history, webhook secret, token and build secrets. Images stay in the registry. Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X DELETE 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container pipeline delete 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/pipelines/{pipeline}/webhook

**Webhook address and secret** · operationId `pipeline_webhook` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_webhook.json

Returns `webhook_url` and `webhook_secret`. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/webhook' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container pipeline webhook 12345
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/webhook", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/webhook", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/pipelines/{pipeline}/webhook/rotate

**Rotate the webhook secret** · operationId `pipeline_rotate_secret` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_rotate_secret.json

The old secret stops working at once: update the repository's webhook. Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/webhook/rotate' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container pipeline rotate-secret 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/webhook/rotate", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/webhook/rotate", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/pipelines/{pipeline}/test

**Test repository access** · operationId `pipeline_test` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_test.json

Reads the branches and tags (ls-remote) with the stored token. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/test' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container pipeline test 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/test", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/test", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/pipelines/{pipeline}/secrets

**Set build secrets** · operationId `pipeline_secret_set` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_secret_set.json

`set`: {NAME: value} (A-Z0-9_). Given only to `commands` as environment variables, masked as **** in logs, never returned. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `set` (required) | object |  |
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/secrets' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"set": {}, "confirm": true}'
```
```bash
gyro container pipeline secret set 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/secrets", headers=h, json={"set": {}, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/secrets", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"set": {}, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/containers/{id}/pipelines/{pipeline}/secrets/{secret}

**Delete a build secret** · operationId `pipeline_secret_delete` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_secret_delete.json

Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |
| `secret` (required) | path | string | Build secret name |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X DELETE 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/secrets/1' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container pipeline secret delete 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/secrets/1", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/secrets/1", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/pipelines/{pipeline}/runs

**Runs of a pipeline** · operationId `pipeline_runs` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_runs.json

Last 50 runs, newest first. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/runs' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container pipeline runs 12345
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/runs", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/runs", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/pipelines/{pipeline}/runs

**Start a run** · operationId `pipeline_run` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_run.json

`ref`: branch (`main`), tag (`tag:v1.2.0`) or full ref; `sha`: optional 40 character commit. Without both the first branch of the rule builds. Queued runs start when the environment's parallel build limit allows.  With `upload_id` the uploaded archive is built (gyro-pipeline.yml is not read for archives).


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `ref` | string |  |
| `sha` | string |  |
| `confirm` | boolean |  |
| `upload_id` | string | Build an uploaded archive (from POST .../uploads) instead of a git ref |
| `archive_name` | string | Shown in the run list |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/runs' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ref": "main", "sha": "string", "confirm": true, "upload_id": "u3k9x0abc12de", "archive_name": "site.tar.gz"}'
```
```bash
gyro container pipeline run 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/runs", headers=h, json={"ref": "main", "sha": "string", "confirm": True, "upload_id": "u3k9x0abc12de", "archive_name": "site.tar.gz"}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/runs", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"ref": "main", "sha": "string", "confirm": true, "upload_id": "u3k9x0abc12de", "archive_name": "site.tar.gz"}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/pipeline-runs/{run}

**Get a run** · operationId `pipeline_run_get` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_run_get.json




### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `run` (required) | path | string | Run id |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container pipeline run-get 12345
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/pipeline-runs/{run}/logs

**Run log** · operationId `pipeline_logs` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_logs.json

Live while building (clone / commands / build sections, secrets masked), saved afterwards (max 512 KB). `tail`: last N lines. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `run` (required) | path | string | Run id |
| `tail` | query | integer | last N lines (default 3000) |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/logs' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container pipeline logs 12345
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/logs", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/logs", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/pipeline-runs/{run}/cancel

**Stop a run** · operationId `pipeline_cancel` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_cancel.json




### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `run` (required) | path | string | Run id |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/cancel' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container pipeline cancel 12345 --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/cancel", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/cancel", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/pipeline-runs/{run}/approve

**Approve the deployment** · operationId `pipeline_approve` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_approve.json

Only for a run in `awaiting_approval`. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `run` (required) | path | string | Run id |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/approve' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container pipeline approve 12345 --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/approve", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/approve", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/pipeline-runs/{run}/reject

**Reject the deployment** · operationId `pipeline_reject` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_reject.json




### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `run` (required) | path | string | Run id |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/reject' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container pipeline reject 12345 --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/reject", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/reject", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/pipeline-runs/{run}/redeploy

**Deploy this run's image again (rollback)** · operationId `pipeline_redeploy` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_redeploy.json

Starts a new run that deploys the image of an earlier run to the target services (with health watch). Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `run` (required) | path | string | Run id |

### Request body

| Field | Type | Description |
|---|---|---|
| `targets` | array<string> |  |
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/redeploy' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"targets": ["string"], "confirm": true}'
```
```bash
gyro container pipeline redeploy 12345 --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/redeploy", headers=h, json={"targets": ["string"], "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipeline-runs/1/redeploy", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"targets": ["string"], "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers/{id}/pipelines/{pipeline}/uploads

**Upload URL for an archive build** · operationId `pipeline_upload` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_upload.json

Returns a presigned `url` (valid 15 minutes) for one `PUT` of exactly `size` bytes (tar.gz / tgz / zip, max 200 MB; send `Content-Length: size`, e.g. `curl -T site.tar.gz "<url>"`). Then start the build with `POST .../runs {"upload_id": ...}`. Archives are deleted after 24 hours. Do not send an Idempotency-Key here (the URL would be withheld from the replayed answer). 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `size` (required) | integer | archive size in bytes (1 - 209715200) |
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/uploads' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"size": 1, "confirm": true}'
```
```bash
gyro container pipeline upload 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/uploads", headers=h, json={"size": 1, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/uploads", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"size": 1, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/pipelines/{pipeline}/deploy-key

**Create the SSH deploy key** · operationId `pipeline_deploy_key_create` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_deploy_key_create.json

Creates an ed25519 key pair for an SSH repository and returns only the public key (add it to the repository as a read-only deploy key). The private key is stored encrypted and never returned. A pipeline has one key; `"rotate": true` replaces it (the old key stops working). 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `rotate` | boolean |  |
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/deploy-key' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"rotate": true, "confirm": true}'
```
```bash
gyro container pipeline deploy-key create 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/deploy-key", headers=h, json={"rotate": True, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/deploy-key", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"rotate": true, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/containers/{id}/pipelines/{pipeline}/deploy-key

**Delete the SSH deploy key** · operationId `pipeline_deploy_key_delete` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_deploy_key_delete.json

The pipeline can no longer reach its SSH repository. Needs `"confirm": true`.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X DELETE 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/deploy-key' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```
```bash
gyro container pipeline deploy-key delete 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/deploy-key", headers=h, json={"confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/deploy-key", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/{id}/pipelines/{pipeline}/host-key

**SSH server host key** · operationId `pipeline_host_key_scan` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_host_key_scan.json

Connects to the repository's SSH server (public addresses only) and returns the host key fingerprint. GitHub / GitLab / Bitbucket keys are pinned (`pinned: true`). Other servers: compare the fingerprint with your own record, then confirm it with PUT. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/host-key' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container pipeline host-key scan 12345
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/host-key", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/host-key", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## PUT /cloud/containers/{id}/pipelines/{pipeline}/host-key

**Confirm (or forget) the SSH host key** · operationId `pipeline_host_key_trust` · scope `services_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/pipeline_host_key_trust.json

`fingerprint`: the SHA256 fingerprint you verified (the server is scanned again and must present it); `forget: true` removes the confirmation. Builds stop if the server key changes later. 


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer | Service id |
| `pipeline` (required) | path | string | Pipeline id (p + 8) |

### Request body

| Field | Type | Description |
|---|---|---|
| `fingerprint` | string |  |
| `forget` | boolean |  |
| `confirm` | boolean |  |

### Responses

- **200**: OK
- **404**: Not found — also returned for objects owned by someone else
- **409**: State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)
- **422**: Validation or business rule failure (`confirm_required`, `invalid_image`, `tool_action_failed` ...)

### Examples

```bash
curl -s -X PUT 'https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/host-key' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fingerprint": "SHA256:abc...", "forget": true, "confirm": true}'
```
```bash
gyro container pipeline host-key trust 12345 --help    # all flags
```
```hcl
# resource gyro_container_pipeline: https://registry.terraform.io/providers/gyro-hosting/gyro/latest/docs/resources/container_pipeline
# Build the repository's Dockerfile on every push to main (or a v* tag) and roll the "web" service out to it.
resource "gyro_container_pipeline" "web" {
  service_id = 12345 # container environment (gyro-ecs-1)
  name       = "web"
  repo       = "https://github.com/gyro-ornek/uygulama"
  token      = var.github_read_token # private repository only (read access)
  branch     = "main"
  tags       = "v*"
  image      = "web"
  targets    = ["web"]

  # multi-stage Dockerfile: build the "production" stage (FROM node:20 AS production) instead of the last one
  build_target = "production"
  build_args   = { APP_VERSION = "1.2.0" } # Dockerfile ARG (not for secrets)

  # optional build step before the Dockerfile (tests ...); secrets are environment variables here
  build_image = "node:22-alpine"
  commands    = ["npm ci", "npm test"]
  secrets     = { NPM_TOKEN = var.npm_token }
}

# Add these to the repository: Settings > Webhooks (application/json, push event)
output "webhook_url" { value = gyro_container_pipeline.web.webhook_url }
output "webhook_secret" {
  value     = gyro_container_pipeline.web.webhook_secret
  sensitive = true
}

# Private repository over SSH: Gyro creates a read-only deploy key (the private key never leaves Gyro).
# GitHub / GitLab / Bitbucket host keys are pinned; for your own git server set ssh_host_fingerprint.
resource "gyro_container_pipeline" "api" {
  service_id = 12345
  name       = "api"
  repo       = "git@github.com:gyro-ornek/api.git"
  deploy_key = true
  image      = "api"
  targets    = ["api"]
}

# Add this to the repository: Settings > Deploy keys (read only)
output "deploy_key" { value = gyro_container_pipeline.api.deploy_key_public }

# Archive builds only (no repository): upload with the API (POST .../pipelines/{id}/uploads) and run with the upload id.
resource "gyro_container_pipeline" "site" {
  service_id   = 12345
  name         = "site"
  git_provider = "archive"
  image        = "site"
  targets      = ["web"]
}
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.put("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/host-key", headers=h, json={"fingerprint": "SHA256:abc...", "forget": True, "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/12345/pipelines/1/host-key", {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"fingerprint": "SHA256:abc...", "forget": true, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/containers/plans

**List container environment plans** · operationId `listContainerPlans` · scope `cloud_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/listContainerPlans.json

Orderable Gyro Container (ECS) environment plans with prepaid prices. A plan is ONE environment (a dedicated node pool); `max_services` is the most apps you can define, `max_replicas` the most replicas per service; all of them share the pool.


### Responses

- **200**: OK

### Examples

```bash
curl -s 'https://www.gyrohosting.com/api/v1/client/cloud/containers/plans' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro container plans
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/containers/plans", headers=h, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers/plans", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## POST /cloud/containers

**Create container environments (batch)** · operationId `createContainerEnvironments` · scope `cloud_order` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/createContainerEnvironments.json

Orders 1..N environments of a plan in ONE order (`quantity`, default 1, max 25 per request). Prepaid: the order is 0 and usage is charged hourly from the balance; the balance must cover the first 24 hours of every environment and new accounts are limited in their first days. `name` (with `{n}`) and `tags` label every environment; `project_id` puts them in a project. Send `"dry_run": true` for a quote; a real order needs `"confirm": true` and an `Idempotency-Key` header.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `Idempotency-Key` (required) | header | string | Unique per logical request (e.g. a UUID). Replays within 24 hours return the stored answer. |

### Request body

| Field | Type | Description |
|---|---|---|
| `plan` | string | plan from GET /cloud/containers/plans (mini-1-1, small-1-2, starter, standart, pro, serverless) |
| `product_id` | integer | product id instead of plan |
| `quantity` | integer | environments to create in one order (--count), default 1, max 25 |
| `name` | string | name (nickname); {n} = number in a batch |
| `tags` | array<string> | tags (max 10) |
| `project_id` | integer / null | project (0 = default project) |
| `confirm` | boolean |  |
| `dry_run` | boolean |  |

### Responses

- **200**: Dry-run quote
- **202**: Ordered; environments are being set up
- **403**: Key lacks the scope, IP not allowed, or feature disabled
- **422**: confirm_required | limit_exceeded (new-account limit of the prepaid cloud; details = LimitHint, raise it with a support ticket) | validation errors

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/containers' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"plan": "mini-1-1", "quantity": 3, "name": "gyro-ecs-{n}", "tags": ["gyro-test"], "confirm": true}'
```
```bash
gyro container create --help    # all flags
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
h["Idempotency-Key"] = str(uuid.uuid4())
r = requests.post("https://www.gyrohosting.com/api/v1/client/cloud/containers", headers=h, json={"plan": "mini-1-1", "quantity": 3, "name": "gyro-ecs-{n}", "tags": ["gyro-test"], "confirm": True}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/containers", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({"plan": "mini-1-1", "quantity": 3, "name": "gyro-ecs-{n}", "tags": ["gyro-test"], "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```
