---
title: "Plan changes and add-ons — API reference"
description: "Resize (plan upgrade) and add-on purchase, paid from the account balance (dry_run, confirm, Idempotency-Key, daily limit: default 50 paid operations p"
url: https://www.gyrohosting.com/developers/en/api/plan-changes-and-add-ons.html
language: en
openapi: https://www.gyrohosting.com/developers/openapi.json
api_version: 2.30.0
---

# Plan changes and add-ons

Resize (plan upgrade) and add-on purchase, paid from the account balance (dry_run, confirm, Idempotency-Key, daily limit: default 50 paid operations per account, per-account/reseller override, 429 daily_limit).

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


## GET /cloud/services/{id}/resize-options

**Products this service can be upgraded to, with prices** · operationId `getResizeOptions` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/getResizeOptions.json

Prepaid services answer `billing: prepaid`; each option has `plan`, `current` and one `hourly` price with `monthly_cap`.

**Data safety (Gyro Cloud Istanbul, tr-ist-1)**: a plan change keeps the disk and all its data, the IP addresses, additional disks and snapshots. The root disk is enlarged in place and the file system is extended automatically; a disk never shrinks, so a plan with a smaller disk is refused with 409 `disk_shrink` (also in `dry_run`; resize-options marks such plans `allowed: false`). Bigger CPU/RAM is applied live when the hypervisor allows it, otherwise the server is shut down cleanly and started again (measured: 1-2 -> 2-4 restarted, about 12 seconds without SSH; 2-4 -> 4-8 live, no restart). While the server has snapshots the disk cannot grow: 409 `resize_refused` (delete the snapshots first). The answer carries `data_preserved: true`, `disk: grows_only`, `restart: if_needed`. Only a rebuild (`POST /cloud/servers/{id}/actions/rebuild`, needs `confirm: true`) erases data: the disk, additional disks and snapshots are deleted (IP addresses stay). **`recreate_required`** (422, invoiced products only): the target plan would need the service to be re-created on another platform, which deletes its data; the API never does that and nothing is changed or charged. Pick another plan from resize-options, or create a new server, move the data and delete the old one.


### Parameters

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

### Responses

- **200**: Options
- **401**: Missing, invalid or revoked key
- **404**: Not found — also returned for objects owned by someone else
- **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/services/12345/resize-options' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro service resize-option list 12345
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/services/12345/resize-options", 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/services/12345/resize-options", {
  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/services/{id}/resize

**Upgrade the plan (paid from balance)** · operationId `resizeService` · scope `cloud_order` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/resizeService.json

dry_run returns the price; a real change needs `"confirm": true` + Idempotency-Key. Creates the upgrade invoice and pays it from the balance; if the balance does not cover it the answer is `awaiting_payment` with a pay_url. Plan changes that would re-create the server are refused (`recreate_required`).
**Prepaid services** (`billing: prepaid`: Gyro Containers, Gyro Cloud Istanbul): no invoice and no confirm/Idempotency-Key; pick `product_id` (or `plan`, e.g. `standart-4-8`, `ecs-pro`) from resize-options (same product family). The module resizes the server / node pool (Istanbul servers: disks never shrink; containers: nodes of another size are replaced one by one, running services must fit a smaller plan). The new hourly price applies from the next hour; answer `status: completed`, `effective: next_hour`, `invoice: null`. A bigger plan needs 24 hours of the price difference in the balance.

**Data safety (Gyro Cloud Istanbul, tr-ist-1)**: a plan change keeps the disk and all its data, the IP addresses, additional disks and snapshots. The root disk is enlarged in place and the file system is extended automatically; a disk never shrinks, so a plan with a smaller disk is refused with 409 `disk_shrink` (also in `dry_run`; resize-options marks such plans `allowed: false`). Bigger CPU/RAM is applied live when the hypervisor allows it, otherwise the server is shut down cleanly and started again (measured: 1-2 -> 2-4 restarted, about 12 seconds without SSH; 2-4 -> 4-8 live, no restart). While the server has snapshots the disk cannot grow: 409 `resize_refused` (delete the snapshots first). The answer carries `data_preserved: true`, `disk: grows_only`, `restart: if_needed`. Only a rebuild (`POST /cloud/servers/{id}/actions/rebuild`, needs `confirm: true`) erases data: the disk, additional disks and snapshots are deleted (IP addresses stay). **`recreate_required`** (422, invoiced products only): the target plan would need the service to be re-created on another platform, which deletes its data; the API never does that and nothing is changed or charged. Pick another plan from resize-options, or create a new server, move the data and delete the old one.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer |  |
| `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 |
|---|---|---|
| `product_id` | integer | target product from resize-options (or give plan for prepaid services) |
| `price_id` | integer |  |
| `dry_run` | boolean |  |
| `confirm` | boolean |  |
| `plan` | string | prepaid services: plan name from resize-options (alternative to product_id) |

### Responses

- **202**: Accepted / awaiting_payment (200 for dry_run)
- **401**: Missing, invalid or revoked key
- **404**: Not found — also returned for objects owned by someone else
- **422**: recreate_required (invoiced products: would re-create the service - refused, nothing changed) | invalid_product | confirm_required
- **409**: disk_shrink | resize_refused (has snapshots / busy) | not_actionable

### Examples

```bash
curl -s -X POST 'https://www.gyrohosting.com/api/v1/client/cloud/services/12345/resize' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"product_id": 1, "price_id": 1, "dry_run": true, "confirm": true, "plan": "string"}'
```
```bash
gyro service resize 12345 --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/services/12345/resize", headers=h, json={"product_id": 1, "price_id": 1, "dry_run": True, "confirm": True, "plan": "string"}, timeout=60)
r.raise_for_status()
print(r.json()["data"])
```
```js
const res = await fetch("https://www.gyrohosting.com/api/v1/client/cloud/services/12345/resize", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({"product_id": 1, "price_id": 1, "dry_run": true, "confirm": true, "plan": "string"}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## GET /cloud/services/{id}/addons/available

**Add-ons offered for this service (with options and prices)** · operationId `listAvailableAddons` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/listAvailableAddons.json

Prepaid add-on options show `cycle: hourly`, `billing: prepaid` and `price {amount (EUR/hour), monthly_cap}`.


### Parameters

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

### Responses

- **200**: Add-ons
- **401**: Missing, invalid or revoked key
- **404**: Not found — also returned for objects owned by someone else
- **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/services/12345/addons/available' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro service addon available 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/services/12345/addons/available", 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/services/12345/addons/available", {
  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/services/{id}/addons

**Add-ons the service holds** · operationId `listServiceAddons` · scope `services_read` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/listServiceAddons.json

Prepaid add-ons (billing: prepaid) carry the hourly price and removable: true.


### Parameters

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

### Responses

- **200**: Add-ons

### Examples

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

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.get("https://www.gyrohosting.com/api/v1/client/cloud/services/12345/addons", 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/services/12345/addons", {
  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/services/{id}/addons

**Buy an add-on (paid from balance)** · operationId `buyAddon` · scope `cloud_order` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/buyAddon.json

Prepaid services + prepaid add-ons: no invoice, the add-on is active at once (`status: active`, `billing: prepaid`) and billed hourly from the balance.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer |  |
| `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 |
|---|---|---|
| `addon_id` (required) | integer |  |
| `option_id` (required) | integer |  |
| `quantity` | integer |  |
| `dry_run` | boolean |  |
| `confirm` | boolean |  |

### Responses

- **202**: Accepted / awaiting_payment (200 for dry_run)
- **401**: Missing, invalid or revoked key
- **404**: Not found — also returned for objects owned by someone else
- **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/services/12345/addons' \
  -H "Authorization: Bearer $GYRO_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"addon_id": 1, "option_id": 1, "quantity": 1, "dry_run": true, "confirm": true}'
```
```bash
gyro service addon create 12345 --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/services/12345/addons", headers=h, json={"addon_id": 1, "option_id": 1, "quantity": 1, "dry_run": 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/services/12345/addons", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({"addon_id": 1, "option_id": 1, "quantity": 1, "dry_run": true, "confirm": true}),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```


## DELETE /cloud/services/{id}/addons/{aid}

**Remove a prepaid add-on at once** · operationId `removeServiceAddon` · scope `cloud_write` · OpenAPI fragment: https://www.gyrohosting.com/developers/spec/ops/removeServiceAddon.json

Prepaid add-ons only (invoiced add-ons are cancelled in the panel at the end of their term). Billing of the item stops at once.


### Parameters

| Field | in | Type | Description |
|---|---|---|---|
| `id` (required) | path | integer |  |
| `aid` (required) | path | integer | add-on record id (GET .../addons) |
| `confirm` (required) | query | boolean |  |

### Responses

- **200**: Removed
- **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/services/12345/addons/1' \
  -H "Authorization: Bearer $GYRO_TOKEN"
```
```bash
gyro service addon delete 12345
```
```python
import os, uuid, requests

h = {"Authorization": "Bearer " + os.environ["GYRO_TOKEN"]}
r = requests.delete("https://www.gyrohosting.com/api/v1/client/cloud/services/12345/addons/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/services/12345/addons/1", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.GYRO_TOKEN}`,
  },
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
```
