# Instances

Create, power, change and delete virtual machines (VPS).

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

An instance is a virtual machine. A **standard** instance gets its own public IPv4 address and a security group. An instance created with a `subnet_id` lives in a [VPC](https://docs.pantechdynamics.com/api/networks) instead: it has a private address, and is reached through a [public IP](https://docs.pantechdynamics.com/api/public-ips). A standard instance can also be given a second interface on its zone's private database network, to reach [managed databases](https://docs.pantechdynamics.com/databases/standard-vps).

Every change is asynchronous. Creating one returns an [order](https://docs.pantechdynamics.com/api/instance-orders); every other change returns an [operation](https://docs.pantechdynamics.com/api/operations) to follow.

Endpoints:

- `GET /public/v1/instances`: List instances
- `POST /public/v1/instances`: Create an instance
- `GET /public/v1/instances/{instance_id}`: Get an instance
- `POST /public/v1/instances/{instance_id}/start`: Start an instance
- `POST /public/v1/instances/{instance_id}/stop`: Stop an instance
- `POST /public/v1/instances/{instance_id}/reboot`: Reboot an instance
- `POST /public/v1/instances/{instance_id}/rename`: Rename an instance
- `POST /public/v1/instances/{instance_id}/resize`: Resize an instance
- `POST /public/v1/instances/{instance_id}/rebuild`: Rebuild an instance
- `POST /public/v1/instances/{instance_id}/restore`: Restore an instance from a snapshot
- `POST /public/v1/instances/{instance_id}/change-ssh-key`: Change an instance's SSH key
- `PUT /public/v1/instances/{instance_id}/security-group`: Change an instance's security group
- `POST /public/v1/instances/{instance_id}/console`: Open the browser console
- `POST /public/v1/instances/{instance_id}/volume-key-credential`: Issue a volume key credential
- `POST /public/v1/instances/{instance_id}/ssh-access`: Grant temporary SSH access
- `GET /public/v1/instances/{instance_id}/ssh-access/{grant_id}`: Get an SSH access grant
- `DELETE /public/v1/instances/{instance_id}/ssh-access/{grant_id}`: Revoke SSH access
- `DELETE /public/v1/instances/{instance_id}`: Delete an instance
- `GET /public/v1/instances/{instance_id}/metrics`: Get instance metrics
- `POST /public/v1/instances/{instance_id}/private-network`: Add a private database network interface to an instance
- `DELETE /public/v1/instances/{instance_id}/private-network`: Remove an instance's private database network interface

## The instance object



- `id` (string, required): e.g. `vm_06gf...`.
- `name` (string, required): Its name, unique among your instances.
- `plan_id` (string, required): The plan's id.
- `plan_slug` (string or null): The plan's slug, as `plan_slug` takes it (e.g. `starter`); null if the plan has since been retired.
- `image_id` (string, required): The image's id.
- `image_slug` (string or null): The image's slug, as `image_slug` takes it (e.g. `ubuntu-24-04`); null if the image has since been retired.
- `encrypted` (boolean): True when the instance runs an encrypted image: its root disk is encrypted on our storage, with its own key.
- `region` (string or null): e.g. `af-abj`.
- `zone` (string or null): e.g. `af-abj-1`.
- `network_id` (string or null): The VPC, for an instance in one.
- `subnet_id` (string or null): Its VPC subnet; null for a standard instance.
- `security_group_id` (string or null): The security group, for a standard instance.
- `public_ipv4` (string or null): The instance's own public address (standard instances). A VPC instance is reached through a public IP: see `GET /public/v1/public-ips`.
- `private_ipv4` (string or null): Its private address: in its VPC subnet, or on the zone's guest network.
- `private_network_state` (string): Its interface on the zone's private database network: `none`, `attaching`, `attached` or `detaching`. See *Add a private database network interface*. One of `none`, `attaching`, `attached`, `detaching`.
- `deletion_protection` (boolean): Platform VM deletion is refused while true.
- `private_network_ip` (string or null): That interface's address once attached (kept while it is being removed); null otherwise. Allow it on a managed database as a `/32` access rule.
- `desired_state` (string, required): What you asked for: `running`, `stopped` or `deleted`. One of `running`, `stopped`, `deleted`.
- `observed_state` (string, required): What the platform last observed. A change is complete when it matches `desired_state`; `failed` means the last change could not be made (see the failure fields), and `unknown` means the platform could not check. One of `pending`, `provisioning`, `running`, `stopping`, `stopped`, `deleting`, `deleted`, `failed`, `unknown`.
- `spec` (object or null): The capacity this instance was created with.
  - `vcpu` (integer, required)
  - `memory_mb` (integer, required)
  - `disk_gb` (integer, required)
- `tags` (object, required): Your own labels, as sent at create.
- `failure` (Failure): Why the last change failed, when it did; otherwise null.
  - `code` (string, required): A stable failure code to branch on, e.g. `INSTANCE_BUSY`. Provisioning failures use `PROVISIONING_CAPACITY_UNAVAILABLE` (no capacity in the zone right now: try another plan or zone, or try later), `PROVISIONING_LIMIT_EXCEEDED` (an account limit was reached: delete unused resources or contact support), `PROVISIONING_ADDRESSES_EXHAUSTED` (no free addresses in the zone), `PROVISIONING_IMAGE_UNAVAILABLE` (the image is not ready), `PROVISIONING_RESOURCE_BUSY` (the resource was busy: try again once it settles), `PROVISIONING_RESOURCE_CONFLICT`, `PROVISIONING_RESOURCE_NOT_FOUND`, `PROVISIONING_INVALID_REQUEST`, `PROVISIONING_UNAVAILABLE` (temporarily unavailable: retry), `PROVISIONING_INTERNAL_ERROR` (a fault on our side: our team is alerted, and retrying will not help) and `PROVISIONING_FAILED` (anything else).
  - `reason` (string, required): A readable sentence saying what went wrong and what to do; safe to show to your users. Its wording may change: branch on `code`.
- `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 instances

```http
GET /public/v1/instances
```

Your instances, newest first, with addresses and state. 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`: `running`, `stopped` or `deleted`. One of `running`, `stopped`, `deleted`.
- `observed_state` (string): Only items with this `observed_state`. One of `pending`, `provisioning`, `running`, `stopping`, `stopped`, `deleting`, `deleted`, `failed`, `unknown`.

Example:

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

Response `200`:

```json
{
  "data": [
    {
      "id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "name": "web-1",
      "plan_id": "plan_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "plan_slug": "starter",
      "image_id": "img_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "image_slug": "ubuntu-24-04",
      "encrypted": true,
      "region": "af-abj",
      "zone": "af-abj-1",
      "network_id": null,
      "subnet_id": null,
      "security_group_id": "sg_06gk3x9tq7m2v8r4y1c5d0zp8w",
      "public_ipv4": "102.211.122.80",
      "private_ipv4": "10.0.1.12",
      "private_network_state": "attached",
      "deletion_protection": true,
      "private_network_ip": "10.250.4.23",
      "desired_state": "running",
      "observed_state": "running",
      "spec": {
        "vcpu": 2,
        "memory_mb": 4096,
        "disk_gb": 80
      },
      "tags": {
        "env": "production"
      },
      "failure": null,
      "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 an instance

```http
POST /public/v1/instances
```

Orders a new instance (VPS). Choose a plan (`GET /public/v1/plans`), an image (`GET /public/v1/images`) and an SSH key (`GET /public/v1/ssh-keys`), which is required: images have no password login.

**Placement.** Leave out `subnet_id` for a standard instance with its own public IPv4, protected by a security group (your organization's default group unless you send `security_group_id`). Send `subnet_id` (and optionally its `network_id`) to place it in a VPC subnet, with a private address only; reach it through a public IP.

**Paid first.** Returns `202 Accepted` with an order (`order_id`, also the `Location` header). The initial payment is taken from credit, or else from the organization's default card, and then the instance is provisioned. Follow `GET /public/v1/instance-orders/{order_id}` until `status` is `provisioned`, then read the instance; `payment_failed` and `failed` are final, and any payment taken is returned to your credit.

**Idempotency.** Send an `Idempotency-Key`. A retry with the same key returns the original order instead of ordering a second instance.

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

Body:

- `name` (string, required): 1 to 255 characters of lowercase letters a-z, digits 0-9 and hyphens (-), starting and ending with a letter or digit, unique among your instances. Anything else is refused with 422 INVALID_INSTANCE_NAME. The guest hostname is derived from it (at most 63 characters). At most 255 characters.
- `plan_slug` (string, required): The size, from `GET /public/v1/plans`.
- `image_slug` (string, required): The operating system, from `GET /public/v1/images`. It must run in the instance's zone.
- `encrypted` (boolean): Optional. `true` creates the encrypted version of `image_slug`: the disk is encrypted on first boot with a key held by Pantech's key servers, and the instance needs outbound access to them at every boot (the platform keeps that open). Refused with `ENCRYPTION_NOT_SUPPORTED` when the image has no encrypted version available to your organization. `false` or omitted creates the image as named. Naming an encrypted image's own slug already creates an encrypted instance; sending `false` with that slug is refused as `ENCRYPTION_CONFLICT`.
- `ssh_key_id` (string, required): An SSH key from `GET /public/v1/ssh-keys`, installed for the default user. Required: images have no password login, so this key is how you log in.
- `region` (string): A region from `GET /public/v1/regions`, e.g. `af-abj`. Defaults to the platform's region.
- `subnet_id` (string): Place the instance in this VPC subnet, with a private address only. Leave out for a standard instance with its own public IPv4.
- `network_id` (string): The subnet's VPC. Optional when `subnet_id` is set; if sent, the subnet must belong to it.
- `security_group_id` (string): Standard instances only: one of your security groups. Defaults to the organization's default group, which starts with one rule: ICMP (ping) from anywhere.
- `tags` (object): Your own labels, as string keys and values. Not interpreted by the platform.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/instances" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "web-1",
    "plan_slug": "starter",
    "image_slug": "ubuntu-24-04",
    "ssh_key_id": "sshk_06gk3x9tq7m2v8r4y1c5d0zp8w"
  }'
```

Response `202`:

```json
{
  "order_id": "ord_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "status": "awaiting_payment",
  "operation_id": null,
  "failure_code": null,
  "amount_minor": 1059200,
  "currency": "NGN"
}
```

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.
- `402`: `INSUFFICIENT_CREDIT`: the initial payment is more than your available credit and the organization has no default card; `detail` gives both amounts. Buy credit (`POST /public/v1/billing/credit/purchases`) or add a card in the console, then retry.
- `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`: `ssh_key_id` is not one of your SSH keys. Check it with `GET /public/v1/ssh-keys`.
- `409`: `INSTANCE_NAME_TAKEN`: you already have an instance with this name. Choose a different name.
`ACCOUNT_SUSPENDED`: an invoice is overdue, or the organization was suspended for non-payment. Pay the outstanding invoice in the console, then retry.
`ACCOUNT_NOT_ACTIVE`: the organization's billing account is not active yet. Finish billing setup in the console (verify the organization and add a payment method), 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.
`SECURITY_GROUPS_NOT_SUPPORTED_IN_ZONE`: a VPC instance uses its subnet's firewall rules, not a security group. Leave out `security_group_id`.
`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` `REQUIRED` or `NAME_TOO_LONG`: send a name of 1 to 255 characters.
`ssh_key_id` `REQUIRED`: no SSH key was sent. Send the id of one of your SSH keys (`GET /public/v1/ssh-keys`): images have no password login.
`plan_slug` `PLAN_NOT_FOUND`: no active plan has this slug. Pick one from `GET /public/v1/plans`.
`image_slug` `IMAGE_NOT_FOUND`: no active image has this slug. Pick one from `GET /public/v1/images`.
`image_slug` `IMAGE_NOT_AVAILABLE_IN_ZONE`: the image does not run in the zone this instance goes to. Pick an image whose `zones` include it.
`encrypted` `ENCRYPTION_NOT_SUPPORTED`: `encrypted` is `true` but this image has no encrypted version available to your organization. Choose an image that offers one, or send `false`.
`encrypted` `ENCRYPTION_CONFLICT`: `encrypted` is `false` but `image_slug` is an encrypted image. Send `true`, or choose the plain image.
`security_group_id` `SECURITY_GROUP_NOT_FOUND`: not one of your security groups. Check it with `GET /public/v1/security-groups`.
`subnet_id` `SUBNET_REQUIRED`: `network_id` was sent without a subnet. Send the subnet too.
`subnet_id` `SUBNET_NOT_IN_NETWORK`: the subnet is not in `network_id`. Send a subnet of that VPC, or leave out `network_id`.
`subnet_id` `SUBNET_NOT_FOUND`: no subnet with this id in your organization.
`subnet_id` `SUBNET_NOT_USABLE`: the subnet is being deleted or has failed. Use another subnet.
`region` `REGION_NOT_AVAILABLE`: this region cannot take this instance. Pick one from `GET /public/v1/regions`.
`UNKNOWN_FIELD`: the body has a field this endpoint does not take (named in `field`). Remove it or fix its spelling.
`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.
`BILLING_UNAVAILABLE`: billing is not answering right now. 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`.
## Get an instance

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

One instance: its plan, image, addresses (including `private_network_ip` once a private database network interface is attached), and current state. A change is complete when `observed_state` matches `desired_state`.

Scope: `read`.

Path parameters:

- `instance_id`: The resource's id.

Example:

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

Response `200`:

```json
{
  "id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "name": "web-1",
  "plan_id": "plan_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "plan_slug": "starter",
  "image_id": "img_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "image_slug": "ubuntu-24-04",
  "encrypted": true,
  "region": "af-abj",
  "zone": "af-abj-1",
  "network_id": null,
  "subnet_id": null,
  "security_group_id": "sg_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "public_ipv4": "102.211.122.80",
  "private_ipv4": "10.0.1.12",
  "private_network_state": "attached",
  "deletion_protection": true,
  "private_network_ip": "10.250.4.23",
  "desired_state": "running",
  "observed_state": "running",
  "spec": {
    "vcpu": 2,
    "memory_mb": 4096,
    "disk_gb": 80
  },
  "tags": {
    "env": "production"
  },
  "failure": null,
  "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 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.
## Start an instance

```http
POST /public/v1/instances/{instance_id}/start
```

Starts a stopped instance. A running instance is billed for its plan again.

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:

- `instance_id`: The resource's id.

Example:

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

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). 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 instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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 instance has been deleted.
- `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`.
## Stop an instance

```http
POST /public/v1/instances/{instance_id}/stop
```

Shuts the instance down. A stopped instance keeps its disks and addresses and is billed for storage only.

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.

For the first 5 minutes after a new instance is created, this returns `409` with code `INSTANCE_POWER_LOCKED` while first boot finishes. Wait the `Retry-After` header's seconds, then try again. Pending, provisioning, or uncertain instances also return this 409 before the first run is recorded, without `Retry-After`; wait for the instance to reach running first.

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

Path parameters:

- `instance_id`: The resource's id.

Example:

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

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
- `409`: `INVALID_RESOURCE_STATE`: the instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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 instance has been deleted.
- `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`.
## Reboot an instance

```http
POST /public/v1/instances/{instance_id}/reboot
```

Restarts the instance (a clean reboot of the operating system). It must be running.

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.

For the first 5 minutes after a new instance is created, this returns `409` with code `INSTANCE_POWER_LOCKED` while first boot finishes. Wait the `Retry-After` header's seconds, then try again. Pending, provisioning, or uncertain instances also return this 409 before the first run is recorded, without `Retry-After`; wait for the instance to reach running first.

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

Path parameters:

- `instance_id`: The resource's id.

Example:

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

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). 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 instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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 instance has been deleted.
- `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`.
## Rename an instance

```http
POST /public/v1/instances/{instance_id}/rename
```

Changes the instance's name (1 to 255 characters, unique among your instances).

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:

- `instance_id`: The resource's id.

Body:

- `name` (string, required): The new name: 1 to 255 characters of lowercase letters a-z, digits 0-9 and hyphens (-), starting and ending with a letter or digit, unique among your instances. Anything else is refused with 422 INVALID_INSTANCE_NAME. At most 255 characters.

Example:

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

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
- `409`: `INSTANCE_NAME_TAKEN`: you already have an instance with this name. Choose a different name.
`ACCOUNT_NOT_ACTIVE`: the organization's billing account is not active yet. Finish billing setup in the console (verify the organization and add a payment method), then retry.
`INVALID_RESOURCE_STATE`: the instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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` `REQUIRED` or `NAME_TOO_LONG`: send a name of 1 to 255 characters.
`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`.
## Resize an instance

```http
POST /public/v1/instances/{instance_id}/resize
```

Moves the instance to a bigger plan. The instance is stopped, resized and started again. Instances only grow: the new plan must be bigger than the current one. No upfront charge: the bigger plan is billed hourly from the change.

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:

- `instance_id`: The resource's id.

Body:

- `plan_slug` (string, required): A bigger plan than the instance's current one, from `GET /public/v1/plans`. Instances only grow.

Example:

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

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). 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 instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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`:
`plan_slug` `PLAN_NOT_FOUND`: no active plan has this slug. Pick one from `GET /public/v1/plans`.
`plan_slug` `PLAN_NOT_BIGGER`: the plan is not bigger than the instance's current one. Choose a bigger plan.
`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`.
## Rebuild an instance

```http
POST /public/v1/instances/{instance_id}/rebuild
```

Reinstalls the operating system from `image_slug`, or from the instance's current image when it is left out. **Everything on the root disk is erased**; attached volumes, addresses and the plan are kept. Take a snapshot first if you may need the data.

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:

- `instance_id`: The resource's id.

Body:

- `image_slug` (string): The image to install, from `GET /public/v1/images`. Leave out to reinstall the current image.

Example:

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

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). 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.
`ACCOUNT_NOT_ACTIVE`: the organization's billing account is not active yet. Finish billing setup in the console (verify the organization and add a payment method), then retry.
`INVALID_RESOURCE_STATE`: the instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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`:
`image_slug` `IMAGE_NOT_FOUND`: no active image has this slug. Pick one from `GET /public/v1/images`.
`image_slug` `IMAGE_NOT_AVAILABLE_IN_ZONE`: the image does not run in this instance's zone. Pick an image whose `zones` include it.
`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`.
## Restore an instance from a snapshot

```http
POST /public/v1/instances/{instance_id}/restore
```

Rolls the instance's root disk back to one of its own snapshots and reboots it. **Everything written to the root disk since the snapshot is lost**, so `confirm_name` must repeat the instance's current name. Attached volumes are not changed.

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:

- `instance_id`: The resource's id.

Body:

- `snapshot_id` (string, required): A snapshot of this instance's root disk.
- `confirm_name` (string, required): The instance's current name, exactly, to confirm: everything written since the snapshot is lost.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/instances/vm_06gk3x9tq7m2v8r4y1c5d0zp8w/restore" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "snapshot_id": "snap_06gk3x9tq7m2v8r4y1c5d0zp8w",
    "confirm_name": "web-1"
  }'
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
`RESOURCE_NOT_FOUND` can also mean `snapshot_id` is not a snapshot of this instance.
- `409`: `ACCOUNT_SUSPENDED`: an invoice is overdue, or the organization was suspended for non-payment. Pay the outstanding invoice in the console, then retry.
`ACCOUNT_NOT_ACTIVE`: the organization's billing account is not active yet. Finish billing setup in the console (verify the organization and add a payment method), then retry.
`INVALID_RESOURCE_STATE`: the instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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`:
`snapshot_id` `REQUIRED`: send a snapshot of this instance.
`confirm_name` `REQUIRED` or `CONFIRM_NAME_MISMATCH`: repeat the instance's current name exactly.
`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`.
## Change an instance's SSH key

```http
POST /public/v1/instances/{instance_id}/change-ssh-key
```

Replaces the SSH key you log in to the instance with by another key on your account, for example after losing the private key. The instance must be running: it is stopped, given the new key and started again, which takes about two minutes. The previous key stops working once the instance is back. Keys you added to the server yourself are left as they are.

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:

- `instance_id`: The resource's id.

Body:

- `ssh_key_id` (string, required): The SSH key the instance should accept from now on, from `GET /public/v1/ssh-keys`. It replaces the instance's current key.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/instances/vm_06gk3x9tq7m2v8r4y1c5d0zp8w/change-ssh-key" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "ssh_key_id": "sshk_06gk3x9tq7m2v8r4y1c5d0zp8w"
  }'
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). 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 instance cannot do this in its current state: it is not running, or another change such as a resize is in progress. Start it, or wait for its current operation to finish, then retry.
`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`:
`ssh_key_id` `REQUIRED`: the field is missing. Send the id of one of your SSH keys.
`ssh_key_id` `SSH_KEY_NOT_FOUND`: no SSH key with this id on your account. Pick one from `GET /public/v1/ssh-keys`.
`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`.
## Change an instance's security group

```http
PUT /public/v1/instances/{instance_id}/security-group
```

Moves a standard instance to another of your security groups. The instance must be stopped; the new group applies when it starts again.

If the instance has a private database network interface, the new group must allow nothing from the private network's range (`10.250.0.0/20` in `af-abj-1`): a group with any ingress rule whose source overlaps it, `0.0.0.0/0` included, is refused.

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:

- `instance_id`: The resource's id.

Body:

- `security_group_id` (string, required): One of your security groups in the instance's zone, e.g. `sg_…`. If the instance has a private database network interface, the group must allow nothing from `10.250.0.0/20`.

Example:

```sh
curl -X PUT "https://api.pantechdynamics.com/public/v1/instances/vm_06gk3x9tq7m2v8r4y1c5d0zp8w/security-group" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "security_group_id": "sg_06gk3x9tq7m2v8r4y1c5d0zp8w"
  }'
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
- `409`: `INSTANCE_MUST_BE_STOPPED`: stop the instance first; the new group is applied when it starts again.
`SECURITY_GROUPS_NOT_SUPPORTED_IN_ZONE`: a VPC instance uses its subnet's firewall rules, not a security group.
`ACCOUNT_NOT_ACTIVE`: the organization's billing account is not active yet. Finish billing setup in the console (verify the organization and add a payment method), then retry.
`INVALID_RESOURCE_STATE`: the instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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`:
`security_group_id` `REQUIRED` or `SECURITY_GROUP_NOT_FOUND`: send the id of one of your security groups.
`security_group_id` `SECURITY_GROUP_ALLOWS_PRIVATE_NETWORK`: the group allows inbound traffic from the private database network, which this instance is attached to; the message names the range and the rules. Replace `0.0.0.0/0` (including the default group's ICMP rule) with narrower ranges that leave out the private range, then retry.
`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`.
## Open the browser console

```http
POST /public/v1/instances/{instance_id}/console
```

Returns a short-lived URL to the instance's browser console. **The console gives a logged-in shell; treat the URL as a password** and never log or share it. The instance must be running.

**Idempotency:** each request opens a new console URL; a retry is not replayed. Opening one changes nothing else, so retrying is safe.

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

Path parameters:

- `instance_id`: The resource's id.

Example:

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

Response `201`:

```json
{
  "url": "https://x.serial-console.pantechdynamics.com/…"
}
```

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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
- `409`: `CONSOLE_INSTANCE_NOT_RUNNING`: the instance must be running to open its console. Start it, wait for the operation to succeed, then retry.
- `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.
`CONSOLE_UNAVAILABLE`: the console is not available right now. Retry in a minute.
`PROVISIONING_UNAVAILABLE`: the provisioning service is temporarily unavailable. Wait a few seconds, then retry with the same `Idempotency-Key`.
## Issue a volume key credential

```http
POST /public/v1/instances/{instance_id}/volume-key-credential
```

Gives an instance the **credential** it uses to ask Pantech for the key of an encrypted data volume. Put the credential on the instance, in the volume agent's configuration, and attach the volume to any instance of your organization: that instance can unlock the volume, whether it runs an encrypted image or a plain one. A volume's key is released only to an instance of the same organization that the volume is attached to.

**The credential is a secret: it is shown once.** Pantech keeps only a hash of it, so it cannot be shown again. Treat it like a password and never log or share it. Each request issues a new credential and **ends the previous one** for that instance, so use it to rotate a credential too, and update the agent on that instance. The credential also ends when the instance is deleted.

**Idempotency:** each request issues a new credential; a retry is not replayed.

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

Path parameters:

- `instance_id`: The resource's id.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/instances/vm_06gk3x9tq7m2v8r4y1c5d0zp8w/volume-key-credential" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

Response `201`:

```json
{
  "instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "credential": "credential"
}
```

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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
- `409`: `INSTANCE_NOT_LIVE`: the instance is being deleted or has failed. Use another instance.
- `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`: encrypted volumes are not available right now. Retry in a minute.
`PROVISIONING_UNAVAILABLE`: the provisioning service is temporarily unavailable. Wait a few seconds, then retry with the same `Idempotency-Key`.
## Grant temporary SSH access

```http
POST /public/v1/instances/{instance_id}/ssh-access
```

Opens port 22 of a standard instance to the address this request comes from, for 15 minutes, so you can SSH in without leaving the port open. The instance must be running and be the only one using its security group.

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`. Then read the grant (`GET /public/v1/instances/{instance_id}/ssh-access/{grant_id}`, the operation's `resource_id` is the grant id) for the `host` and `port` to connect to once `status` is `active`.

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:

- `instance_id`: The resource's id.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/instances/vm_06gk3x9tq7m2v8r4y1c5d0zp8w/ssh-access" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "sshg_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_CALLER_ADDRESS`: the address this request comes from could not be used to scope access. Retry from a normal internet connection.
- `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.
- `409`: `SSH_ACCESS_INSTANCE_NOT_RUNNING`: the instance must be running. Start it, then retry.
`SSH_ACCESS_SECURITY_GROUP_SHARED`: the instance's security group is used by other instances too, so opening it would open them all. Move the instance to its own security group, then retry.
`SSH_ACCESS_NOT_SUPPORTED_IN_ZONE`: temporary SSH access is not available for VPC instances yet. Use a public IP with a port forwarding rule instead.
`INVALID_RESOURCE_STATE`: the instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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`.
## Get an SSH access grant

```http
GET /public/v1/instances/{instance_id}/ssh-access/{grant_id}
```

A temporary SSH access grant: its `status`, and the `host` and `port` to connect to once it is `active`. It expires by itself at `expires_at`.

Scope: `read`.

Path parameters:

- `instance_id`: The resource's id.
- `grant_id`: The resource's id.

Example:

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

Response `200`:

```json
{
  "id": "sshg_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "instance_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "status": "active",
  "host": "102.211.122.80",
  "port": 22,
  "expires_at": "2026-10-03T09:30:00Z",
  "revoked_at": "2026-10-03T09:30:00Z",
  "created_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 grant with this id on this instance, or the instance is not in your organization. Check both ids.
- `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.
## Revoke SSH access

```http
DELETE /public/v1/instances/{instance_id}/ssh-access/{grant_id}
```

Closes the port opened by a grant before it expires.

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:

- `instance_id`: The resource's id.
- `grant_id`: The resource's id.

Example:

```sh
curl -X DELETE "https://api.pantechdynamics.com/public/v1/instances/vm_06gk3x9tq7m2v8r4y1c5d0zp8w/ssh-access/sshg_06gk3x9tq7m2v8r4y1c5d0zp8w" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "sshg_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 grant with this id on this instance, or the instance is not in your organization. Check both ids.
- `409`: `INVALID_RESOURCE_STATE`: the grant is already revoked or expired. Nothing to do.
`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`.
## Delete an instance

```http
DELETE /public/v1/instances/{instance_id}
```

Deletes the instance. It is stopped, a safety snapshot of its root disk is kept for 24 hours, and then it is removed. Attached volumes are detached, not deleted. Release its public IP and delete port forwarding rules that point at it first.

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:

- `instance_id`: The resource's id.

Example:

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

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
- `409`: `INSTANCE_HAS_PUBLIC_IP`: a VPC public IP still maps to this instance. Detach it (`POST /public/v1/public-ips/{public_ip_id}/detach`) to keep the address, or release it (`DELETE /public/v1/public-ips/{public_ip_id}`), then retry.
`INSTANCE_HAS_PORT_FORWARDS`: port forwarding rules still point at this instance. Delete them, then retry.
`INSTANCE_BEHIND_LOAD_BALANCER`: the instance is still a load balancer target. Remove it from its load balancers (`PATCH /public/v1/load-balancers/{load_balancer_id}`), then retry.
`INVALID_RESOURCE_STATE`: the instance cannot do this in its current state, for example while it is being created or deleted. Wait for its current operation to finish, then retry.
`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 instance has already been deleted.
- `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`.
## Get instance metrics

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

CPU, memory, network and disk activity over `interval` (`1h`, `24h`, `7d` or `30d`), one point per bucket. `available` is false when no samples exist yet, for example just after creation.

Scope: `read`.

Path parameters:

- `instance_id`: The resource's id.

Query parameters:

- `interval` (string): One of `1h`, `24h`, `7d`, `30d`. Defaults to `24h`.

Example:

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

Response `200`:

```json
{
  "interval": "1h",
  "available": true,
  "sampled_at": "2026-10-03T09:30:00Z",
  "points": [
    {
      "bucket_start": "2026-10-03T09:30:00Z",
      "bucket_end": "2026-10-03T09:30:00Z",
      "cpu_percent": 1,
      "memory_percent": 1,
      "network_in_bytes": 1,
      "network_out_bytes": 1,
      "disk_read_peak_bytes_per_second": 1,
      "disk_write_peak_bytes_per_second": 1
    }
  ]
}
```

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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
- `422`: `VALIDATION_FAILED`: a field is invalid. Each entry of `errors` names the `field` and its `code`:
`interval` `INVALID_METRIC_INTERVAL`: use `1h`, `24h`, `7d` or `30d`.
- `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.
## Add a private database network interface to an instance

```http
POST /public/v1/instances/{instance_id}/private-network
```

Adds a second network interface on the zone's private database network to a standard instance, so it can reach your managed databases there by their private address. Only in a zone with a private database network (`af-abj-1`); a VPC instance already reaches databases in its own VPC. The interface is free.

When the operation succeeds, the instance's `private_network_state` is `attached` and `private_network_ip` is the interface's address: allow it on each database as a `/32` access rule (*Replace access rules*).

**The instance's security group must allow nothing from the private network's range** (`10.250.0.0/20`; the zone's `private_network_cidr` in `GET /public/v1/regions`). A security group applies to every interface of an instance, so any ingress rule whose source overlaps that range, whatever its protocol or ports, would let every database and every other customer's private interface on the network reach this instance. That includes `0.0.0.0/0` and `10.0.0.0/8`, and the ICMP-from-anywhere rule of your organization's default security group. Such a group is refused, naming the rules to change: replace `0.0.0.0/0` with the addresses you actually need, or with ranges that leave out `10.250.0.0/20`, or move the instance to a group without such rules. While the instance has the interface, rule changes and group moves that would allow the range are refused too.

The instance must be running or stopped with no other change in progress. A running instance gets the interface without a restart.

**In the guest** the interface stays down until you configure it. On Ubuntu add it to netplan with DHCP only, never the default route or DNS: `dhcp4: true` with `dhcp4-overrides: {use-routes: false, use-dns: false}` for the new interface (often `ens4` or `eth1`), then run `netplan apply`.

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`. If the operation fails, its `failure.code` says why: `SECURITY_GROUP_ALLOWS_PRIVATE_NETWORK` (a rule allowing the range was added meanwhile; nothing was attached), `INSTANCE_BUSY` (the instance kept changing; retry when it has settled), or `INSTANCE_PRIVATE_NETWORK_ATTACH_FAILED` (retry later, or contact support). The instance itself is never marked failed for it.

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:

- `instance_id`: The resource's id.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/instances/vm_06gk3x9tq7m2v8r4y1c5d0zp8w/private-network" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
`NOT_FOUND`: private database network interfaces are not offered on this platform yet.
- `409`: `PRIVATE_NETWORK_NOT_AVAILABLE`: this instance's zone has no private database network for instances (VPC instances reach databases in their own VPC). Nothing to attach.
`SECURITY_GROUP_ALLOWS_PRIVATE_NETWORK`: the instance's security group allows inbound traffic from the private network's range; `detail` names the range and each offending rule. Replace `0.0.0.0/0` in those rules (including the default group's ICMP rule) with narrower ranges that leave out `10.250.0.0/20`, or move the instance to another group, then retry.
`PRIVATE_NETWORK_ALREADY_ATTACHED`: the instance already has the interface, or it is being added. Read the instance for `private_network_ip`.
`PRIVATE_NETWORK_CHANGE_IN_PROGRESS`: the interface is being added or removed. Wait for that operation to finish.
`INSTANCE_NOT_SETTLED`: the instance is starting, stopping, resizing, restoring or being deleted. Wait until it is running or stopped, then retry.
`ACCOUNT_SUSPENDED`: an invoice is overdue, or the organization was suspended for non-payment. Pay the outstanding invoice in the console, then retry.
`CONCURRENT_UPDATE`: the resource changed while this request was being processed. Retry the request; if it repeats, read the resource first.
- `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`.
## Remove an instance's private database network interface

```http
DELETE /public/v1/instances/{instance_id}/private-network
```

Removes the instance's interface on the private database network. The instance must be running or stopped with no other change in progress; a running instance keeps running. `private_network_ip` stays readable until the operation succeeds; then remove any database access rule for it, and the interface from the guest's network configuration.

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`. A detach that cannot complete fails with `INSTANCE_PRIVATE_NETWORK_DETACH_FAILED`; retry later.

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:

- `instance_id`: The resource's id.

Example:

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

Response `202`:

```json
{
  "operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_id": "vm_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 instance with this id in your organization, or the id is not a instance id (`vm_…`). Check the id.
`NOT_FOUND`: private database network interfaces are not offered on this platform yet.
- `409`: `PRIVATE_NETWORK_NOT_ATTACHED`: the instance has no private network interface to remove.
`PRIVATE_NETWORK_CHANGE_IN_PROGRESS`: the interface is being added or removed. Wait for that operation to finish.
`INSTANCE_NOT_SETTLED`: the instance is starting, stopping, resizing, restoring or being deleted. Wait until it is running or stopped, then retry.
`CONCURRENT_UPDATE`: the resource changed while this request was being processed. Retry the request; if it repeats, read the resource first.
- `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`.
