+++
title = "Actions"
description = "An Action is slow work on a cluster: provisioning, deprovisioning, a plan change, or a change Bonsai makes on its own."
weight = 87
updated = 2026-11-03

[extra]
nav_title = "Actions"
weight = 30
+++

<!-- Generated by script/build-api-reference.js from data/openapi/v1.yml and data/api-reference/pages.yml. Edit those and run `npm run build:api-reference`, not this file. -->

An Action is slow work on a cluster: provisioning, deprovisioning, a
plan change, or a change Bonsai makes on its own. Follow one by polling
it. See [Actions](@/docs/api/actions/index.md) for the states, results
and failures.

**On this page:** [Action object](#action-object), [Show an Action](#get-action), [List a cluster's Actions](#list-cluster-actions).

## The Action object {#action-object}

| Field | Type | Description |
|---|---|---|
| `id` | string | Opaque: `act_` and 16 random characters, in no order. Never changes. |
| `type` | string | One of `cluster.provision`, `cluster.deprovision`, `cluster.change_plan`, `cluster.read_only`, `cluster.disable`, `cluster.enable`. More may be added. |
| `state` | string | One of `new`, `running`, `success`, `error`. More may be added. |
| `subject` | object | What the Action acts on. |
| `subject.type` | string | One of `cluster`. More may be added. |
| `subject.id` | string | The cluster's ID. |
| `created_at` | string (date-time) | When the Action started. |
| `finished_at` | string (date-time) or null | When it finished. null while it runs. |
| `error` | object or null | Why it failed. null unless `state` is `error`. |
| `error.code` | string | One of `payment_failed`, `timed_out`, `action_failed`. More may be added. Stable, snake_case. Branch on this, not on `message`. `payment_failed`: Stripe couldn't charge the account (no card, or a declined one), so billing for the cluster couldn't start; `action_url` says where to add a card. Only on Actions the legacy API started; a `/v1` create answers it at once, as a 422. `timed_out`: the Action stopped making progress. `action_failed`: anything else, which Bonsai support can see. |
| `error.message` | string | What went wrong, for people. It can change, so don't parse it. |
| `error.action_url` | string (URL) | Set on `payment_failed` to a Stripe Checkout page for adding a card, valid for at least an hour after the response; fetch the Action again for a current one. Only a token whose owner may manage the account's billing (an Admin or Billing user) gets a Checkout page, since adding a card through it needs no Bonsai login; any other token, and any when Stripe can't be reached, gets the dashboard page where an account Admin or Billing user adds one instead. Absent otherwise. |
| `result` | object or null | Set on success, for Actions that create something. |
| `result.cluster` | string or null | Cluster ID. |
| `result.credential` | string or null | Credential ID. |
| `steps` | array of objects | Customer-facing steps only; internal steps are left out. |
| `steps[].state` | string | One of `new`, `running`, `success`, `error`. More may be added. |
| `steps[].message` | string | What the step does. |
| `steps[].at` | string (date-time) | When the step reached its state. |

```json
{
  "id": "act_3k9x0q2m7v1c8b4n",
  "type": "cluster.provision",
  "state": "success",
  "subject": { "type": "cluster", "id": "my-search-4823919" },
  "created_at": "2026-10-26T14:03:00Z",
  "finished_at": "2026-10-26T14:03:06Z",
  "error": null,
  "result": { "cluster": "my-search-4823919", "credential": "scc-a8Kd" },
  "steps": [
    {
      "state": "success",
      "message": "Placing the cluster",
      "at": "2026-10-26T14:03:01Z"
    },
    {
      "state": "success",
      "message": "Creating the first credential",
      "at": "2026-10-26T14:03:05Z"
    }
  ]
}
```

## Show an Action {#get-action}

<p class="api-endpoint"><span class="api-method api-method-get">GET</span> <code>/v1/actions/{action_id}</code></p>

- **Scope:** `clusters:read`

Includes the customer-facing steps. While the Action runs, `Retry-After` says how long to wait before polling again.

### Parameters

| Name | In | Type | Description |
|---|---|---|---|
| `action_id` | path | string | Required. Opaque Action ID. |

### Example request

```bash
curl https://api.bonsai.io/v1/actions/act_3k9x0q2m7v1c8b4n \
  -H "Authorization: Bearer $BONSAI_TOKEN"
```

### Response

`200 OK`. The Action. Returns an [Action object](#action-object).

| Header | Description |
|---|---|
| `Retry-After` | Set while the Action is `new` or `running`. Seconds to wait before polling it again. |

```json
{
  "id": "act_3k9x0q2m7v1c8b4n",
  "type": "cluster.provision",
  "state": "success",
  "subject": { "type": "cluster", "id": "my-search-4823919" },
  "created_at": "2026-10-26T14:03:00Z",
  "finished_at": "2026-10-26T14:03:06Z",
  "error": null,
  "result": { "cluster": "my-search-4823919", "credential": "scc-a8Kd" },
  "steps": [
    {
      "state": "success",
      "message": "Placing the cluster",
      "at": "2026-10-26T14:03:01Z"
    },
    {
      "state": "success",
      "message": "Creating the first credential",
      "at": "2026-10-26T14:03:05Z"
    }
  ]
}
```

### Errors

| Status | Codes |
|---|---|
| `401` | [`unauthorized`](@/docs/api/errors/index.md#unauthorized) |
| `403` | [`missing_scope`](@/docs/api/errors/index.md#missing_scope) |
| `404` | [`not_found`](@/docs/api/errors/index.md#not_found) |
| `429` | [`rate_limited`](@/docs/api/errors/index.md#rate_limited), [`too_many_failed_authentications`](@/docs/api/errors/index.md#too_many_failed_authentications) |

## List a cluster's Actions {#list-cluster-actions}

<p class="api-endpoint"><span class="api-method api-method-get">GET</span> <code>/v1/clusters/{cluster_id}/actions</code></p>

- **Scope:** `clusters:read`

Newest first. Only customer-visible Action types are listed.

### Parameters

| Name | In | Type | Description |
|---|---|---|---|
| `cluster_id` | path | string | Required. The cluster's ID, which is its slug. It never changes. |
| `limit` | query | integer | Page size. Defaults to 25. |
| `cursor` | query | string | The `next_cursor` of the previous page. Opaque. |

### Example request

```bash
curl https://api.bonsai.io/v1/clusters/my-search-4823919/actions \
  -H "Authorization: Bearer $BONSAI_TOKEN"
```

### Response

`200 OK`. A page of [Action objects](#action-object) in `data`, with `next_cursor`. See [Pagination](@/docs/api/requests-and-responses/index.md#pagination).

```json
{
  "data": [
    {
      "id": "act_3k9x0q2m7v1c8b4n",
      "type": "cluster.provision",
      "state": "success",
      "subject": { "type": "cluster", "id": "my-search-4823919" },
      "created_at": "2026-10-26T14:03:00Z",
      "finished_at": "2026-10-26T14:03:06Z",
      "error": null,
      "result": { "cluster": "my-search-4823919", "credential": "scc-a8Kd" },
      "steps": [
        {
          "state": "success",
          "message": "Placing the cluster",
          "at": "2026-10-26T14:03:01Z"
        },
        {
          "state": "success",
          "message": "Creating the first credential",
          "at": "2026-10-26T14:03:05Z"
        }
      ]
    }
  ],
  "next_cursor": null
}
```

### Errors

| Status | Codes |
|---|---|
| `400` | [`invalid_parameter`](@/docs/api/errors/index.md#invalid_parameter) |
| `401` | [`unauthorized`](@/docs/api/errors/index.md#unauthorized) |
| `403` | [`missing_scope`](@/docs/api/errors/index.md#missing_scope) |
| `404` | [`not_found`](@/docs/api/errors/index.md#not_found) |
| `429` | [`rate_limited`](@/docs/api/errors/index.md#rate_limited), [`too_many_failed_authentications`](@/docs/api/errors/index.md#too_many_failed_authentications) |
