# Operations

Follow asynchronous changes until they finish.

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

Every asynchronous write returns `202` with an `operation_id`. Poll the operation until its `status` is `succeeded` or `failed`, or open its event stream and wait. Polling every 2–5 seconds is plenty.

Endpoints:

- `GET /public/v1/operations/{operation_id}`: Get an operation
- `GET /public/v1/operations/{operation_id}/events`: Stream an operation

## The operation object



- `id` (string, required): e.g. `op_...`.
- `resource_type` (string, required): e.g. `instance`, `volume`, `network`, `security_group`, `database`. One of `instance`, `network`, `subnet`, `security_group`, `snapshot`, `volume`, `ssh_access_grant`, `relay_vm`, `network_acl_rule`, `public_ip`, `port_forwarding_rule`, `database`.
- `resource_id` (string, required): The resource the operation acts on, e.g. a `vm_…`, `db_…` or `snap_…` id. For a create, the new resource's id.
- `kind` (string, required): What it does, e.g. `create_instance`, `stop_instance`. For a database: `create_database`, `start_database`, `stop_database`, `delete_database`, `apply_database_access_rules` (an access-rule change, attaching or detaching security groups, or the automatic re-apply after an attached group's rules changed), `change_database_password`, `reset_database_password`, `resize_database_storage`. New kinds may be added.
- `status` (string, required): `submitting` and `submitted` while it runs; `succeeded` and `failed` are final. One of `submitting`, `submitted`, `succeeded`, `failed`.
- `failure` (Failure): Why it failed, when `status` is `failed`; 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).
## Get an operation

```http
GET /public/v1/operations/{operation_id}
```

One asynchronous operation. Every write that returns `202` gives you its `operation_id`: poll this until `status` is `succeeded` or `failed` (wait a second or two between polls), or use *Stream an operation* instead.

A failed operation has `failure.code`, a stable code to branch on, and `failure.reason`, a readable sentence safe to show to your users.

Scope: `read`.

Path parameters:

- `operation_id`: The resource's id.

Example:

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

Response `200`:

```json
{
  "id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "resource_type": "instance",
  "resource_id": "vm_06gk3x9tq7m2v8r4y1c5d0zp8w",
  "kind": "create_instance",
  "status": "submitting",
  "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 operation with this id in your organization, or the id is not a operation id (`op_…`). 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.
## Stream an operation

```http
GET /public/v1/operations/{operation_id}/events
```

Server-Sent Events for one operation: an `operation` event (data: the Operation) each time it changes, ending when it succeeds or fails. A `: keepalive` comment is sent while it is idle. Use it instead of polling.

If the stream stops before the operation finishes, its last event is `error`, with data `{"code", "detail", "request_id"}`: `OPERATION_STREAM_UNAVAILABLE` after 30 seconds of failing to read the operation, `UNAUTHENTICATED` within 30 seconds of the key being revoked or expiring, or `OPERATION_STREAM_TIMEOUT` after 15 minutes open. Reconnect, or poll `GET /public/v1/operations/{operation_id}`.

Scope: `read`.

Path parameters:

- `operation_id`: The resource's id.

Example:

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

Response `200`: The event stream.

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 operation with this id in your organization, or the id is not a operation id (`op_…`). 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.
