# Versioning

> The API is /v1 and webhook payloads carry a dated version that each webhook keeps. What BlueBot may add without warning, what counts as a breaking change, and how to move to a new version.

BlueBot versions its API in two places. Requests and answers are versioned by the path: everything in these docs is `/v1`. Webhook payloads are versioned by date: every event carries `api_version`, now `2026-11-13`, and each webhook keeps the version it was made with. Code you write against either keeps working when BlueBot changes.

## Where you see the version

| Where                                            | What it says                                                           |
| ------------------------------------------------ | ---------------------------------------------------------------------- |
| The path                                         | `https://usebluebot.com/api/v1/…`                                      |
| Every event, by webhook or from `GET /v1/events` | `"api_version": "2026-11-13"`: the shape of `data`.                    |
| Every webhook, from `GET /v1/webhooks`           | `"api_version": "2026-11-13"`: the version it was made with and keeps. |
| `GET /v1/status`                                 | `"api_version"`: the newest version.                                   |
| The OpenAPI file                                 | `info.version`: the version it describes.                              |

```bash
curl "https://usebluebot.com/api/v1/status" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY"
```

```ts
import BlueBot from '@usebluebot/sdk';

const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });

const status = await client.status();
console.log(status);
```

```python
import os
from bluebot import BlueBot

client = BlueBot(api_key=os.environ["BLUEBOT_API_KEY"])

status = client.status()
print(status)
```

The answer, `200`:

```json
{
  "object": "status",
  "status": "ok",
  "sending": "ok",
  "database": "ok",
  "api_version": "2026-11-13",
  "checked_at": "2026-11-13T19:41:00.000Z"
}
```

## What BlueBot changes without a new version

These changes can come at any time, and your code should expect them:

- **New endpoints**, and new optional fields and query parameters on existing ones.
- **New fields in answers and in event `data`.** Read the fields you need and ignore the rest. The OpenAPI file never says an object’s fields are closed, so code generated from it accepts new ones.
- **New event types.** A new type reaches a webhook made with `["*"]` (`"all_events": true`) at once, and no webhook that lists its types until you add it to `events` with `PATCH /v1/webhooks/{id}`. Your handler should ignore types it doesn’t know. [Webhooks](https://usebluebot.com/docs/webhooks)
- **New values in a list of reasons**, such as a new `hold.reason`, `failure.reason` or `cancel_reason`. Handle a value you don’t know the way you handle the closest general case, and log it.
- **New error codes** for situations that had none before. Every error has a `message` and a `hint` you can show or log whatever its code.
- **Different words** in `message`, `hint`, `hold.description` and `failure.detail`. Match on codes and reasons, never on words.
- **Different ids.** An id’s prefix stays the same (`msg_`, `chat_`, `ct_`); the rest is opaque and may grow longer. Store ids as strings of any length.
- **Different limits and defaults** that are your settings, such as the sending pace. The [Limits](https://usebluebot.com/docs/limits) page always has the current numbers.

## What counts as a breaking change

BlueBot never makes these to `/v1` or to a payload version once it’s out:

- Removing or renaming a field, an endpoint, an event type or a value.
- Changing a field’s type, or what it means.
- Making an optional request field required, or refusing a request that used to be accepted.
- Changing which error code a situation answers with.
- Changing what an event’s `data` holds.

A change like that to requests or answers comes as `/v2`, beside `/v1`, so you choose when to move. A change like that to event payloads comes as a new `api_version` date. Webhooks made before it keep the old shape: BlueBot stores each event in the newest shape and changes it back for each webhook as it delivers it.

Before a version is retired, BlueBot emails the account owner at least 90 days ahead, and lists the change on the [changelog](https://usebluebot.com/changelog). A version still in use is never turned off without that notice.

## Moving a webhook to a new version

A webhook keeps its version for good; you can’t change it on an existing webhook. To move, make a new webhook beside the old one:

### Make a new webhook

Make it with the same address and events, or a new address for a new handler. A new webhook gets the newest version.

```bash
curl -X POST "https://usebluebot.com/api/v1/webhooks" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: move-crm-webhook-2026-11-13" \
  -d '{
    "url": "https://crm.example.com/bluebot/webhooks",
    "events": [
      "message.received",
      "message.failed"
    ],
    "description": "CRM, new payload version"
  }'
```

```ts
import BlueBot from '@usebluebot/sdk';

const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });

const webhook = await client.webhooks.create({
  url: 'https://crm.example.com/bluebot/webhooks',
  events: ['message.received', 'message.failed'],
  description: 'CRM, new payload version',
}, { idempotencyKey: 'move-crm-webhook-2026-11-13' });
console.log(webhook);
```

```python
import os
from bluebot import BlueBot

client = BlueBot(api_key=os.environ["BLUEBOT_API_KEY"])

webhook = client.webhooks.create(
    url="https://crm.example.com/bluebot/webhooks",
    events=["message.received", "message.failed"],
    description="CRM, new payload version",
    idempotency_key="move-crm-webhook-2026-11-13",
)
print(webhook)
```

The answer carries the new webhook’s `api_version` and its own `secret`.

### Handle both versions

For a while each event arrives twice, once from each webhook, and each signed with its own webhook’s secret. Read `api_version` on each event to tell the two shapes apart, and use the event `id` to handle each event once.

```ts title="webhooks.ts"
if (await alreadyHandled(event.id)) return;
if (event.api_version === '2026-11-13') {
    await handleCurrent(event);
} else {
    await handleOlder(event);
}
```

### Delete the old webhook

Once your handler reads the new shape, delete the old webhook with `DELETE /v1/webhooks/{id}` or in the dashboard. From then on every event arrives once and in the new shape.

## The SDKs and the OpenAPI file

The SDKs follow semantic versioning. A new minor version adds what BlueBot added; a new major version comes only with a breaking change in the SDK itself or a new API version, and its release notes say what to change.

The [OpenAPI file](https://usebluebot.com/docs/openapi) describes `/v1` as it is now, and its `info.version` names the newest payload version. Generating a client from it again after BlueBot adds something never breaks code written against an older copy.
