+++
title = "Moving from the Legacy API"
weight = 91
updated = 2026-11-03

[extra]
nav_title = "Moving from the Legacy API"
weight = 55
+++

The [legacy API](@/docs/api/legacy/_index.md) keeps working, so nothing has to move. If you'd like what v1 adds, such as Actions you can follow, safe retries, error codes, credentials, usage and metrics, this page maps each legacy call to its v1 equivalent.

## What changes

- **The base URL** gains a version: `https://api.bonsai.io/v1`.
- **Authentication** is Bearer only. Your token works on both APIs, but on v1 it goes in `Authorization: Bearer bonsai_<key>_<secret>`, not in Basic authentication. A token from before the `bonsai_` format goes in as `Authorization: Bearer <key>.<secret>`.
- **Clusters are created, deleted and moved between plans through Actions.** These answer `202` with an [Action](@/docs/api/actions/index.md) to poll, instead of the finished result.
- **Errors** are [problems with a `code`](@/docs/api/errors/index.md), instead of `{"errors": [...], "status": ...}`.
- **Lists** are [paginated with a cursor](@/docs/api/requests-and-responses/index.md#pagination), and return their results in `data`.
- **Spaces** are split: shared placement is chosen by `region`, and only private spaces are listed under `/v1/spaces`.
- **A cluster's connection details** are its [credentials](#credentials), instead of fields on the cluster.

## Each legacy call

| Legacy | v1 |
|---|---|
| `GET /clusters` | [`GET /v1/clusters`](@/docs/api/reference/clusters/index.md#list-clusters). `q` becomes `name`. `tenancy=child` becomes `single_tenant=false`. `location` becomes `region`, or `space` for a private space. |
| `GET /clusters/{slug}` | [`GET /v1/clusters/{id}`](@/docs/api/reference/clusters/index.md#get-cluster). The cluster's ID is its slug, unchanged. |
| `POST /clusters` | [`POST /v1/clusters`](@/docs/api/reference/clusters/index.md#create-cluster) with `{name, plan, region}`, plus `space` for a private space and `release` to choose one. Answers `202` with a `cluster.provision` Action; its `result` names the cluster and its first credential. |
| `PUT /clusters/{slug}` with `name` | [`PATCH /v1/clusters/{id}`](@/docs/api/reference/clusters/index.md#update-cluster) with `{name}`. |
| `PUT /clusters/{slug}` with `plan` | [`POST /v1/clusters/{id}/plan_changes`](@/docs/api/reference/clusters/index.md#change-cluster-plan) with `{plan}`. Answers `202` with a `cluster.change_plan` Action. |
| `DELETE /clusters/{slug}?name=` | [`DELETE /v1/clusters/{id}?name=`](@/docs/api/reference/clusters/index.md#delete-cluster), unchanged. Answers `202` with a `cluster.deprovision` Action. |
| `GET /plans` | [`GET /v1/plans`](@/docs/api/reference/catalog/index.md#list-plans). `available_spaces` becomes `regions`, and `available_releases` becomes `releases`. |
| `GET /plans/{slug}` | [`GET /v1/plans/{id}`](@/docs/api/reference/catalog/index.md#get-plan). The plan's ID is its slug. |
| `GET /releases` | [`GET /v1/releases`](@/docs/api/reference/catalog/index.md#list-releases). |
| `GET /spaces` | [`GET /v1/regions`](@/docs/api/reference/catalog/index.md#list-regions) for shared placement, and [`GET /v1/spaces`](@/docs/api/reference/catalog/index.md#list-spaces) for your account's private spaces. There's no `q`: filter the list yourself. |
| `GET /spaces/{path}` | No equivalent. A region's details are in `GET /v1/regions`, and a private space's in `GET /v1/spaces`. |

## Creating a cluster

On the legacy API, a create names a `space` path, such as `omc/bonsai/us-east-1/common`, and its response includes the cluster's username and password. On v1:

1. Send `region`, such as `aws-us-east-1`, instead of a shared space path. For a private space, send its `region` and its `space` ID.
2. Poll the Action until it succeeds. See [Actions](@/docs/api/actions/index.md).
3. Get the URL from the credential the Action's `result` names, with `GET /v1/clusters/{id}/credentials/{credential_id}`. That needs the `credentials:read` scope.

A v1 create also checks for a payment method and charges the account before it builds anything, so a missing or declined card is refused at once, with [`payment_method_required`](@/docs/api/errors/index.md#payment_method_required) or [`payment_failed`](@/docs/api/errors/index.md#payment_failed) and a link to add a card, rather than a failed build later.

## Credentials

A legacy cluster carries its connection details in `access`. On v1, connection details are [credentials](@/docs/api/reference/credentials/index.md), which you can list, create and revoke under `/v1/clusters/{id}/credentials`. Each has an access type: `rw` for full access, `ro` for read-only, and, where enabled, `rwm` and `acl` for finer control. Listing credentials doesn't show passwords; `GET /v1/clusters/{id}/credentials/{credential_id}` does, with the full URL.

## Scopes

v1 adds three scopes, which the legacy API doesn't use: `credentials:read`, `credentials:write` and `query_logs:read`. Tokens made before they existed don't have them, and new tokens get them only when picked. To read a cluster's credentials on v1, create a token with `credentials:read`. See [scopes](@/docs/api/authentication/index.md#scopes).
