# Versions

Source: https://capacms.com/docs/concepts/versions

How Capa dates the API, how to pin a version, and how to hear about the next one.

The `/api/` surface has no version in its path. It has a date in a header.

```http
Capa-Version: 2026-10-01
```

A version is a dated snapshot of how the API behaves. Once a date is published, it never changes shape. New behaviour arrives as a new date, and your integration keeps the old one until you change the header or the key's pin.

Today there is one version, `2026-10-01`.

## Two dials

Capa separates the API's shape from your content's shape.

| Dial             | Header          | Who moves it                     | Today        |
| ---------------- | --------------- | -------------------------------- | ------------ |
| Platform version | `Capa-Version`  | Capa, when we change the API     | `2026-10-01` |
| Data contract    | `Capa-Contract` | You, when you change your models | `1`          |

Neither forces the other. Our envelope moving is our business. Your fields moving is yours.

## Which version a request gets

Capa picks the first of these that applies:

1. The `Capa-Version` header on the request, when it names a supported date.
2. The version your key is pinned to.
3. `2026-10-01`, the first version.

Nothing floats. A key never starts receiving a newer shape on the day we publish one.

A `Capa-Version` that is not a supported date is refused, never rounded:

```json
{ "error": { "type": "invalid_request", "code": "invalid_version",
             "message": "Capa-Version 2026-13-99 is not a supported version.",
             "param": "Capa-Version",
             "hint": "Supported versions: 2026-10-01.",
             "docs": "https://docs.capacms.com/errors/invalid_version" },
  "meta": { "version": "2026-10-01", "contract": 1, "requestId": "req_0f3c…" } }
```

Every response says which version served it, in the `Capa-Version` response header and in `meta.version`. See [invalid_version](https://capacms.com/docs/errors/invalid_version).

## Pin the key, not every call

A `cap_` key is pinned to the newest version at the moment you create it. That pin never moves on its own.

So the day a second date exists, the keys you already hold keep the date they were made under. Only a key made after that day gets the new one.

A legacy `pk_` or `sk_` key carries no pin. It resolves to `2026-10-01`.

To move a key to another date, change its pin. This is a session call, so it goes to `api.capacms.com` with your login token:

```bash
curl -X PATCH https://api.capacms.com/v2/tenants/api-keys/<keyId> \
  -H 'Authorization: Bearer <your session token>' \
  -H 'content-type: application/json' \
  -d '{ "apiVersion": "2026-10-01" }'
```

A date that does not exist is refused, so a key can never be pinned to a version that is not there.

The SDK sends the header for you. Pin it in code where you create the client:

```ts
import { createClient } from "@capacms/sdk/next";

const capa = createClient({
  baseUrl: "https://cdn.capacms.com",
  apiKey: process.env.CAPA_KEY!,
  version: "2026-10-01",
});
```

## List the versions

`GET /api/versions` needs no key. It is the one route a build script can call without a credential.

```bash
curl https://cdn.capacms.com/api/versions
```

```json
{ "data": { "current": "2026-10-01",
            "versions": [ { "date": "2026-10-01", "status": "current",
                            "changes": [ { "id": "2026-10-01.initial",
                                           "kind": "behaviour",
                                           "surfaces": ["rest"],
                                           "summary": "First dated version of /api/." } ] } ] },
  "meta": { "version": "2026-10-01", "requestId": "req_0f3c…" } }
```

| `status`     | Meaning                                                                          |
| ------------ | -------------------------------------------------------------------------------- |
| `current`    | The newest version.                                                              |
| `supported`  | Older, fully supported, no end date.                                             |
| `deprecated` | A successor exists and has been announced. It still serves every byte it served. |
| `sunset`     | Its end date has passed.                                                         |

## Hear about the next version

Ask for every version newer than yours:

```bash
curl 'https://cdn.capacms.com/api/versions?from=2026-10-01'
```

Today the list is empty. Put this call in a weekly job and your own tooling tells you when a new date ships.

Each change in the list has a stable `id` (`<date>.<slug>`), a `kind` (`response`, `request` or `behaviour`), the `surfaces` it touches (`rest`, `graphql`) and a one-sentence `summary`.

## Sunset

Capa does not retire a version on its own schedule while you are still calling it.

A version is deprecated only when its successor exists and has been announced. It gets a sunset date only after its traffic has been zero for thirty days.

No version is deprecated today. No response carries a `Deprecation` or `Sunset` header yet, and no version answers `410`.

## Contracts

`Capa-Contract` names your data-model version. There is exactly one today, `1`, and it is the default, so you can leave the header off.

Any other value is refused with [`404 contract_not_found`](https://capacms.com/docs/errors/contract_not_found). Multiple contracts arrive later, with screens to start, compare, publish and retire them.

## Related

* [API overview](https://capacms.com/docs/api) for the request and response shape.
* [API versions reference](https://capacms.com/docs/api/versions) and the [API changelog](https://capacms.com/docs/changelog/api).
* [Keys](https://capacms.com/docs/concepts/keys) for what else a key carries.
