# Volumes

Data disks: create, attach, detach, grow and delete.

Source: https://docs.pantechdynamics.com/api/volumes

A volume is a data disk, separate from an instance's root disk. Attach it to a running instance in the same zone, then format and mount it from the instance. Volumes only grow.

Endpoints:

- `GET /public/v1/instances/{instance_id}/volumes`: List an instance's volumes
- `GET /public/v1/volumes`: List volumes
- `POST /public/v1/volumes`: Create a volume
- `GET /public/v1/volumes/{volume_id}`: Get a volume
- `DELETE /public/v1/volumes/{volume_id}`: Delete a volume
- `POST /public/v1/volumes/{volume_id}/attach`: Attach a volume
- `POST /public/v1/volumes/{volume_id}/detach`: Detach a volume
- `POST /public/v1/volumes/{volume_id}/resize`: Grow a volume

## The volume object



- `id` (string, required): e.g. `vol_...`.
- `name` (string, required): Its name.
- `size_gb` (integer, required): Its size in GB.
- `disk_offering_slug` (string, required): See `GET /public/v1/disk-offerings`.
- `storage_type` (string or null): The storage class, e.g. `shared`; null when not stated.
- `region` (string or null)
- `zone` (string or null)
- `attached_instance_id` (string or null): The instance it is attached to; null when detached.
- `attached_instance_name` (string or null): That instance's name; null when detached.
- `desired_instance_id` (string or null): Where an attach in progress is moving it.
- `mount_point` (string or null): Where it is mounted, when mounted at create; null otherwise.
- `source_snapshot_id` (string or null): Set when restored from a snapshot.
- `monthly_cost` (object or null): What it costs per month at its size; null when it cannot be priced.
  - `currency` (string, required)
  - `amount_minor` (integer, required)
- `desired_state` (string, required): What you asked for: `present`, or `deleted` once you deleted it. One of `present`, `deleted`.
- `observed_state` (string, required): What the platform last observed: `pending` and `provisioning` while it is being created, `active` once ready, `deleting`/`deleted` on the way out, `failed` if it could not be made, `unknown` when it could not be checked. One of `pending`, `provisioning`, `active`, `deleting`, `deleted`, `failed`, `unknown`.
- `encryption` (object): What the platform can truthfully say about this volume's encryption. `status` is `none` for a plain volume, `requested` when you asked for encryption at creation and the disk has not been formatted yet, and `encrypted` once the platform has confirmed the disk was formatted with a key from the volume key service. `encrypted` is set only by the platform, never from a claim.
  - `status` (string, required): One of `none`, `requested`, `encrypted`.
- `created_at` (string or null): When it was created (RFC 3339, UTC).
- `updated_at` (string or null): When it last changed (RFC 3339, UTC).
## List an instance's volumes

```http
GET /public/v1/instances/{instance_id}/volumes
```

The volumes attached to this instance.

Scope: `read`.

Path parameters:

- `instance_id`: The resource's id.

Query parameters:

- `limit` (integer): Items per page, 1 to 100. Defaults to 50. From 1 to 100. Defaults to `50`.
- `cursor` (string): The previous page's `next_cursor`, to get the next page. Leave out for the first page; keep the other parameters the same.
- `q` (string): Only items whose name contains this text (case-insensitive), at most 255 characters. At most 255 characters.
- `desired_state` (string): Only items with this `desired_state`: `present` or `deleted`. One of `present`, `deleted`.
- `observed_state` (string): Only items with this `observed_state`. One of `pending`, `provisioning`, `active`, `deleting`, `deleted`, `failed`, `unknown`.

Example:

```sh
curl "https://api.pantechdynamics.com/public/v1/instances/vm_06gk3x9tq7m2v8r4y1c5d0zp8w/volumes" \
  -H "Authorization: Bearer $PANTECH_API_KEY"
```

Response `200`:

```json
{
  "data": [
    {
      "id": "vol_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "name": "data",
      "size_gb": 100,
      "disk_offering_slug": "medium-20gb",
      "storage_type": "shared",
      "region": "af-abj",
      "zone": "af-abj-1",
      "attached_instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "attached_instance_name": "web-1",
      "desired_instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "mount_point": "/data",
      "source_snapshot_id": "snap_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "monthly_cost": {
        "currency": "NGN",
        "amount_minor": 1059200
      },
      "desired_state": "present",
      "observed_state": "active",
      "encryption": {
        "status": "none"
      },
      "created_at": "2026-10-03T09:30:00Z",
      "updated_at": "2026-10-03T09:30:00Z"
    }
  ],
  "next_cursor": "eyJhdCI6IjIwMjYtMTAtMDNUMDk6MzA6MDBaIiwiaWQiOiJ2bV8wNmdrIn0"
}
```

Errors:

- `400`: `INVALID_PAGE`: `limit` is not a whole number from 1 to 100. Send a value in that range, or leave it out for 50.
`INVALID_CURSOR`: `cursor` does not belong to this list with these filters. Start again without a cursor.
`INVALID_FILTER`: `q` is longer than 255 characters, or `desired_state` or `observed_state` is not one of its listed values. Fix the parameter named in `detail`.
- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `404`: `RESOURCE_NOT_FOUND`: no instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
## List volumes

```http
GET /public/v1/volumes
```

Your volumes, newest first, with what each costs per month. Filter by name with `q`, or by state.

Scope: `read`.

Query parameters:

- `limit` (integer): Items per page, 1 to 100. Defaults to 50. From 1 to 100. Defaults to `50`.
- `cursor` (string): The previous page's `next_cursor`, to get the next page. Leave out for the first page; keep the other parameters the same.
- `q` (string): Only items whose name contains this text (case-insensitive), at most 255 characters. At most 255 characters.
- `desired_state` (string): Only items with this `desired_state`: `present` or `deleted`. One of `present`, `deleted`.
- `observed_state` (string): Only items with this `observed_state`. One of `pending`, `provisioning`, `active`, `deleting`, `deleted`, `failed`, `unknown`.

Example:

```sh
curl "https://api.pantechdynamics.com/public/v1/volumes" \
  -H "Authorization: Bearer $PANTECH_API_KEY"
```

Response `200`:

```json
{
  "data": [
    {
      "id": "vol_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "name": "data",
      "size_gb": 100,
      "disk_offering_slug": "medium-20gb",
      "storage_type": "shared",
      "region": "af-abj",
      "zone": "af-abj-1",
      "attached_instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "attached_instance_name": "web-1",
      "desired_instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "mount_point": "/data",
      "source_snapshot_id": "snap_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "monthly_cost": {
        "currency": "NGN",
        "amount_minor": 1059200
      },
      "desired_state": "present",
      "observed_state": "active",
      "encryption": {
        "status": "none"
      },
      "created_at": "2026-10-03T09:30:00Z",
      "updated_at": "2026-10-03T09:30:00Z"
    }
  ],
  "next_cursor": "eyJhdCI6IjIwMjYtMTAtMDNUMDk6MzA6MDBaIiwiaWQiOiJ2bV8wNmdrIn0"
}
```

Errors:

- `400`: `INVALID_PAGE`: `limit` is not a whole number from 1 to 100. Send a value in that range, or leave it out for 50.
`INVALID_CURSOR`: `cursor` does not belong to this list with these filters. Start again without a cursor.
`INVALID_FILTER`: `q` is longer than 255 characters, or `desired_state` or `observed_state` is not one of its listed values. Fix the parameter named in `detail`.
- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
## Create a volume

```http
POST /public/v1/volumes
```

Creates a data disk of `disk_offering_slug` (see `GET /public/v1/disk-offerings`; send `size_gb` for a custom-size offering). Send `instance_id` to attach it at once to a running instance in the same zone, and `mount_point` to have it mounted there.

Returns `202 Accepted` with an `operation_id` (also in the `Location` header). The change happens in the background: poll `GET /public/v1/operations/{operation_id}`, or stream `/events`, until `status` is `succeeded` or `failed`; a failed operation carries a stable `failure.code` and a readable `failure.reason`. The volume's id is the operation's `resource_id`.

Send an `Idempotency-Key`. A retry with the same key returns the original operation instead of making the change twice.

Scope: `write`. Send an `Idempotency-Key` header.

Body:

- `name` (string, required): 1 to 255 characters. At most 255 characters.
- `disk_offering_slug` (string, required): The volume type, from `GET /public/v1/disk-offerings`.
- `size_gb` (integer): Size in GB. Required for an offering with `custom_size: true`; ignored otherwise.
- `region` (string): A region from `GET /public/v1/regions`. Defaults to the platform's region.
- `instance_id` (string): Attach the new volume at once to this running instance in the same zone.
- `mount_point` (string): With `instance_id`: an absolute path to mount the volume at, e.g. `/data`.
- `encrypted` (boolean): Optional. `true` asks for an **encrypted data volume**. It is a request, not encryption yet: the volume reports `encryption.status` `requested` until its disk has been formatted with a key from Pantech, and `encrypted` only once the platform has confirmed that. The format happens inside the instance, by the Pantech volume agent (`pantech-vkey`): attach the volume to an instance that runs the agent and it formats the new, empty disk for you as soon as it appears, so you only create a filesystem on `/dev/mapper/pvk-...` and mount it. Creating or attaching alone encrypts nothing, an instance without the agent cannot encrypt it, and a disk that already holds data is never formatted. Its key is released only to the instance of your organization the volume is attached to, so the volume can be used with any instance that runs the agent, an encrypted VM or a plain one. Refused with `ENCRYPTION_NOT_SUPPORTED` when encrypted volumes are not enabled for your organization, and `ENCRYPTED_VOLUMES_UNAVAILABLE` while the service is off. `false` or omitted creates a plain volume, as before.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/volumes" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "data",
    "disk_offering_slug": "medium-20gb",
    "size_gb": 100,
    "instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w"
  }'
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vol_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "status": "submitting"
}
```

Errors:

- `400`: `IDEMPOTENCY_KEY_REQUIRED`: the `Idempotency-Key` header is missing. Send a new unique value, such as a UUID, with every write.
`INVALID_IDEMPOTENCY_KEY`: the `Idempotency-Key` header is longer than 255 characters. Use a shorter value.
`INVALID_REQUEST_BODY`: the body is not a single valid JSON object. Send one JSON object with the fields documented for this endpoint.
- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `409`: `ACCOUNT_SUSPENDED`: an invoice is overdue, or the organization was suspended for non-payment. Pay the outstanding invoice in the console, then retry.
`PROVIDER_ACCOUNT_NOT_READY`: your organization's cloud account is still being set up, which takes a few minutes after sign-up. Retry shortly.
`CONCURRENT_UPDATE`: the resource changed while this request was being processed. Retry the request; if it repeats, read the resource first.
- `413`: `REQUEST_TOO_LARGE`: the body is larger than 64 KiB. Send a smaller request.
- `422`: `VALIDATION_FAILED`: a field is invalid. Each entry of `errors` names the `field` and its `code`:
`name` or `disk_offering_slug` `REQUIRED`: send both.
`name` `NAME_TOO_LONG`: at most 255 characters.
`disk_offering_slug` `DISK_OFFERING_NOT_FOUND`: no active disk offering has this slug. Pick one from `GET /public/v1/disk-offerings`.
`size_gb` `SIZE_REQUIRED`: a custom-size offering needs `size_gb`.
`instance_id` `INSTANCE_NOT_FOUND`: not one of your instances.
`instance_id` `INSTANCE_NOT_RUNNING`: the instance must be running to attach a volume at create. Start it, or attach the volume later.
`instance_id` `INSTANCE_WRONG_ZONE`: the instance is in another zone than the volume.
`mount_point` `INVALID_MOUNT_POINT`: send an absolute path such as `/data`.
`region` `REGION_NOT_AVAILABLE`: pick a region from `GET /public/v1/regions`.
`encrypted` `ENCRYPTION_NOT_SUPPORTED`: encrypted volumes are not enabled for your organization yet. Create a plain volume, or send `encrypted` as `false`.
`UNKNOWN_FIELD`: the body has a field this endpoint does not take (named in `field`). Remove it or fix its spelling.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`ENCRYPTED_VOLUMES_UNAVAILABLE`: you asked for an encrypted volume but the encrypted volume service is not available right now. Retry later, or create a plain volume.
`PROVISIONING_UNAVAILABLE`: the provisioning service is temporarily unavailable. Wait a few seconds, then retry with the same `Idempotency-Key`.
## Get a volume

```http
GET /public/v1/volumes/{volume_id}
```

One volume, with where it is attached and what it costs.

Scope: `read`.

Path parameters:

- `volume_id`: The resource's id.

Example:

```sh
curl "https://api.pantechdynamics.com/public/v1/volumes/vol_06gk3x9tq7m2v8r4y1c5d0zp8w" \
  -H "Authorization: Bearer $PANTECH_API_KEY"
```

Response `200`:

```json
{
  "id": "vol_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "name": "data",
  "size_gb": 100,
  "disk_offering_slug": "medium-20gb",
  "storage_type": "shared",
  "region": "af-abj",
  "zone": "af-abj-1",
  "attached_instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "attached_instance_name": "web-1",
  "desired_instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "mount_point": "/data",
  "source_snapshot_id": "snap_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "monthly_cost": {
    "currency": "NGN",
    "amount_minor": 1059200
  },
  "desired_state": "present",
  "observed_state": "active",
  "encryption": {
    "status": "none"
  },
  "created_at": "2026-10-03T09:30:00Z",
  "updated_at": "2026-10-03T09:30:00Z"
}
```

Errors:

- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `404`: `RESOURCE_NOT_FOUND`: no volume with this id in your organization, or the id is not a volume id (`vol_…`). Check the id.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
## Delete a volume

```http
DELETE /public/v1/volumes/{volume_id}
```

Deletes a volume and its data. Detach it first. Its snapshots are kept and stay billed until you delete them.

Returns `202 Accepted` with an `operation_id` (also in the `Location` header). The change happens in the background: poll `GET /public/v1/operations/{operation_id}`, or stream `/events`, until `status` is `succeeded` or `failed`; a failed operation carries a stable `failure.code` and a readable `failure.reason`.

Send an `Idempotency-Key`. A retry with the same key returns the original operation instead of making the change twice.

**Scope:** `write`. Destructive: an AI app's key also needs `delete`.

Scope: `write`. Send an `Idempotency-Key` header.

Path parameters:

- `volume_id`: The resource's id.

Example:

```sh
curl -X DELETE "https://api.pantechdynamics.com/public/v1/volumes/vol_06gk3x9tq7m2v8r4y1c5d0zp8w" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vol_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "status": "submitting"
}
```

Errors:

- `400`: `IDEMPOTENCY_KEY_REQUIRED`: the `Idempotency-Key` header is missing. Send a new unique value, such as a UUID, with every write.
`INVALID_IDEMPOTENCY_KEY`: the `Idempotency-Key` header is longer than 255 characters. Use a shorter value.
- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `404`: `RESOURCE_NOT_FOUND`: no volume with this id in your organization, or the id is not a volume id (`vol_…`). Check the id.
- `409`: `INVALID_RESOURCE_STATE`: the volume is still attached, or is already being deleted. Detach it first.
`CONCURRENT_UPDATE`: the resource changed while this request was being processed. Retry the request; if it repeats, read the resource first.
- `422`: `VALIDATION_FAILED` with no `errors` entry: the platform refused a value, for example a malformed CIDR or a name longer than 255 characters. Check the request against this page.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`PROVISIONING_UNAVAILABLE`: the provisioning service is temporarily unavailable. Wait a few seconds, then retry with the same `Idempotency-Key`.
## Attach a volume

```http
POST /public/v1/volumes/{volume_id}/attach
```

Attaches a detached volume to a running instance in the same zone. Mount it inside the instance afterwards.

Returns `202 Accepted` with an `operation_id` (also in the `Location` header). The change happens in the background: poll `GET /public/v1/operations/{operation_id}`, or stream `/events`, until `status` is `succeeded` or `failed`; a failed operation carries a stable `failure.code` and a readable `failure.reason`.

Send an `Idempotency-Key`. A retry with the same key returns the original operation instead of making the change twice.

Scope: `write`. Send an `Idempotency-Key` header.

Path parameters:

- `volume_id`: The resource's id.

Body:

- `instance_id` (string, required): A running instance in the volume's zone, e.g. `vm_…`.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/volumes/vol_06gk3x9tq7m2v8r4y1c5d0zp8w/attach" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w"
  }'
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vol_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "status": "submitting"
}
```

Errors:

- `400`: `IDEMPOTENCY_KEY_REQUIRED`: the `Idempotency-Key` header is missing. Send a new unique value, such as a UUID, with every write.
`INVALID_IDEMPOTENCY_KEY`: the `Idempotency-Key` header is longer than 255 characters. Use a shorter value.
`INVALID_REQUEST_BODY`: the body is not a single valid JSON object. Send one JSON object with the fields documented for this endpoint.
- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `404`: `RESOURCE_NOT_FOUND`: no volume with this id in your organization, or the id is not a volume id (`vol_…`). Check the id.
- `409`: `INVALID_RESOURCE_STATE`: the volume is already attached or changing, or the instance is not running. Detach it first, or start the instance.
`CONCURRENT_UPDATE`: the resource changed while this request was being processed. Retry the request; if it repeats, read the resource first.
- `413`: `REQUEST_TOO_LARGE`: the body is larger than 64 KiB. Send a smaller request.
- `422`: `VALIDATION_FAILED`: a field is invalid. Each entry of `errors` names the `field` and its `code`:
`instance_id` `REQUIRED` or `INSTANCE_NOT_FOUND`: send one of your instances.
`instance_id` `INSTANCE_NOT_RUNNING`: the instance must be running.
`instance_id` `INSTANCE_WRONG_ZONE`: the instance is in another zone than the volume. Volumes attach only within their zone.
`UNKNOWN_FIELD`: the body has a field this endpoint does not take (named in `field`). Remove it or fix its spelling.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`PROVISIONING_UNAVAILABLE`: the provisioning service is temporarily unavailable. Wait a few seconds, then retry with the same `Idempotency-Key`.
## Detach a volume

```http
POST /public/v1/volumes/{volume_id}/detach
```

Detaches a volume from its instance. Unmount it inside the instance first, or data being written may be lost.

Returns `202 Accepted` with an `operation_id` (also in the `Location` header). The change happens in the background: poll `GET /public/v1/operations/{operation_id}`, or stream `/events`, until `status` is `succeeded` or `failed`; a failed operation carries a stable `failure.code` and a readable `failure.reason`.

Send an `Idempotency-Key`. A retry with the same key returns the original operation instead of making the change twice.

Scope: `write`. Send an `Idempotency-Key` header.

Path parameters:

- `volume_id`: The resource's id.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/volumes/vol_06gk3x9tq7m2v8r4y1c5d0zp8w/detach" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vol_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "status": "submitting"
}
```

Errors:

- `400`: `IDEMPOTENCY_KEY_REQUIRED`: the `Idempotency-Key` header is missing. Send a new unique value, such as a UUID, with every write.
`INVALID_IDEMPOTENCY_KEY`: the `Idempotency-Key` header is longer than 255 characters. Use a shorter value.
- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `404`: `RESOURCE_NOT_FOUND`: no volume with this id in your organization, or the id is not a volume id (`vol_…`). Check the id.
- `409`: `INVALID_RESOURCE_STATE`: the volume is not attached, or is changing. Wait for its current operation to finish.
`CONCURRENT_UPDATE`: the resource changed while this request was being processed. Retry the request; if it repeats, read the resource first.
- `422`: `VALIDATION_FAILED` with no `errors` entry: the platform refused the request, for example because the resource has already been deleted. Read the resource to check its state.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`PROVISIONING_UNAVAILABLE`: the provisioning service is temporarily unavailable. Wait a few seconds, then retry with the same `Idempotency-Key`.
## Grow a volume

```http
POST /public/v1/volumes/{volume_id}/resize
```

Grows a volume to a bigger offering, or to a bigger `size_gb` of a custom-size offering. Volumes only grow. Extend the file system from inside the instance afterwards. No upfront charge: the larger size is billed hourly.

Returns `202 Accepted` with an `operation_id` (also in the `Location` header). The change happens in the background: poll `GET /public/v1/operations/{operation_id}`, or stream `/events`, until `status` is `succeeded` or `failed`; a failed operation carries a stable `failure.code` and a readable `failure.reason`.

Send an `Idempotency-Key`. A retry with the same key returns the original operation instead of making the change twice.

Scope: `write`. Send an `Idempotency-Key` header.

Path parameters:

- `volume_id`: The resource's id.

Body:

- `disk_offering_slug` (string, required): The volume type after the resize, from `GET /public/v1/disk-offerings`.
- `size_gb` (integer): The new size in GB, for a custom-size offering: larger than the current size. Volumes only grow.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/volumes/vol_06gk3x9tq7m2v8r4y1c5d0zp8w/resize" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "disk_offering_slug": "medium-20gb",
    "size_gb": 20
  }'
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vol_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "status": "submitting"
}
```

Errors:

- `400`: `IDEMPOTENCY_KEY_REQUIRED`: the `Idempotency-Key` header is missing. Send a new unique value, such as a UUID, with every write.
`INVALID_IDEMPOTENCY_KEY`: the `Idempotency-Key` header is longer than 255 characters. Use a shorter value.
`INVALID_REQUEST_BODY`: the body is not a single valid JSON object. Send one JSON object with the fields documented for this endpoint.
- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `404`: `RESOURCE_NOT_FOUND`: no volume with this id in your organization, or the id is not a volume id (`vol_…`). Check the id.
- `409`: `ACCOUNT_SUSPENDED`: an invoice is overdue, or the organization was suspended for non-payment. Pay the outstanding invoice in the console, then retry.
`INVALID_RESOURCE_STATE`: the new size is not larger than the current one, or the volume is changing. Send a larger size, or wait.
`CONCURRENT_UPDATE`: the resource changed while this request was being processed. Retry the request; if it repeats, read the resource first.
- `413`: `REQUEST_TOO_LARGE`: the body is larger than 64 KiB. Send a smaller request.
- `422`: `VALIDATION_FAILED`: a field is invalid. Each entry of `errors` names the `field` and its `code`:
`disk_offering_slug` `REQUIRED` or `DISK_OFFERING_NOT_FOUND`: send an active offering's slug.
`size_gb` `SIZE_REQUIRED`: a custom-size offering needs `size_gb`.
`UNKNOWN_FIELD`: the body has a field this endpoint does not take (named in `field`). Remove it or fix its spelling.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`PROVISIONING_UNAVAILABLE`: the provisioning service is temporarily unavailable. Wait a few seconds, then retry with the same `Idempotency-Key`.
