# Versioning and compatibility

API v3 evolves along four independent axes. A release can change one of them without touching the others, and you track each one through a different channel.

## The four axes

| Axis | Where it appears | Changes when |
|---|---|---|
| Major route version | `/api/v3` in the path | The contract changes in a breaking way. v4 ships with its own OpenAPI document, and v3 keeps being served until the `Sunset` date of each operation |
| Contract release | `X-Cademi-Release` on every response, `info.version` in the OpenAPI document, the `release` field of `GET /capabilities` | Every compatible release: a minor release adds an operation, field, or enum value; a patch release fixes text or documentation without changing the shape |
| OpenAPI document format | `openapi: 3.1.0` at the top of the document | The OpenAPI specification version used to describe the API. It is not an API version |
| Event version | `version` in the event envelope, next to `type` | The shape of the event payload changes. A webhook destination pins `payload_version` and receives events in the version it subscribed to |

## Compatible changes

These changes fit within the same major version (`3.x`) and do not break existing integrations:

- a new operation;
- a new optional request field;
- a new response field;
- a new value in an enum marked `x-extensible: true` (treat unknown values as opaque);
- a new filter, a new header, or relaxed validation;
- a new error code under an already documented status.

Example: a resource response gains a new field. Clients that already read the JSON keep working, because nothing they received before is missing.

```json
{
  "id": "prd_01J9X8Y7Z6A5B4C3D2E1F0G9H8",
  "object": "product",
  "name": "Curso de Marcenaria",
  "status": "published",
  "visibility": "public"
}
```

## Breaking changes

These changes only ship in v4:

- removing or renaming an operation, field, parameter, or enum value;
- changing the type, format, or meaning of a field;
- making an optional field required;
- changing the HTTP status or the `code` of an already documented case;
- changing the default ordering of a list;
- lowering a limit (page size, batch size, upload size);
- adding a value to an enum marked `x-extensible: false`.

Example: renaming `name` to `title` in the same response breaks every client that reads `name`. A change like this only exists under another major version, never within `/api/v3`:

```json
{
  "id": "prd_01J9X8Y7Z6A5B4C3D2E1F0G9H8",
  "object": "product",
  "title": "Curso de Marcenaria",
  "status": "published"
}
```

Each enum declares its policy in the OpenAPI document. Closed enums (`x-extensible: false`) include state enums such as publication `status`, `operation.status`, and `credential.status`, as well as `format` and `denial_reason`. Open enums (`x-extensible: true`) include event `type` and operation `type`, and can gain new values without breaking your client.

## Runtime deprecation

An operation scheduled for removal gets at least six months of notice before it stops responding:

- the OpenAPI document marks the operation with `deprecated: true`, `x-cademi-deprecated-at` (the announcement date), and `x-sunset` (the cutoff date);
- every response from that operation includes these headers:

```
Deprecation: @1798761600
Sunset: Thu, 01 Jul 2027 00:00:00 GMT
Link: <https://api.cademi.com.br/docs/deprecations>; rel="deprecation"
```

- `GET /capabilities` lists deprecated operations in `deprecations[]`:

```json
{
  "data": {
    "object": "capabilities",
    "release": "3.2.0",
    "deprecations": [
      {
        "operation_id": "products.legacy_export",
        "deprecated_at": "2027-01-01",
        "sunset_at": "2027-07-01"
      }
    ]
  }
}
```

- between the announcement and the cutoff, the operation keeps responding normally;
- after the `Sunset` date, the operation stops executing and returns `410` with the error code below.

### Errors

| Code | Status | When |
|---|---|---|
| `operation_sunset` | 410 | The operation was called after its announced `Sunset` date |

No `/api/v3` operation is deprecated at this time. This section describes the process that applies once one is.

## Versioned events

Each release publishes, alongside the OpenAPI document, the catalog of public events at that point (`type`, `version`, visibility, and payload keys). A published event never changes shape without a new `version`: removing or renaming a field, or changing its visibility, produces a new version of the same `type`.

An event delivered to a webhook destination is always the JSON recorded when the event occurred, never rebuilt from the current payload shape. Replaying an older event (`POST /webhooks/{id}/replays`, `GET /events/{id}`) returns exactly what was recorded, in the version it was recorded in. Each webhook destination pins `payload_version` when you register it and receives events in that version. See [Webhooks](https://cademi.dev/api/webhooks.md) and [Events](https://cademi.dev/api/events.md).

## Tracking changes

- The [changelog](https://cademi.dev/api/changelog.md) for each release describes what changed and what you need to do (nothing, for compatible changes).
- The `X-Cademi-Release` header on any response tells you which release served the call.
- `GET /openapi.json` always serves the document for the latest published release, never an earlier one.
- `GET /capabilities` returns `release` and `deprecations[]`, so your integration can track both programmatically without reading the changelog.

## Version boundaries

API v3 runs on the same host as the legacy APIs (`/api/v1`, `/api/v2`, `/api/third`), but credentials from one are not valid on the other:

- a v3 credential secret (`ck_live_…`/`ck_test_…`) sent to `/api/v1`, `/api/v2`, or `/api/third` is rejected with `401`;
- a legacy authentication token sent to `/api/v3` is rejected with `401 unauthenticated`;
- a signed-in administrator session does not authenticate requests to `/api/v3`. Calls to v3 always require an API credential. See [Authentication](https://cademi.dev/api/authentication.md).

## Compatibility matrix

One row per published v3 release. No operation was removed or changed in a breaking way between them: each release only added.

| Release | Date | What it added | CLI |
|---|---|---|---|
| 3.0.0 | September 22, 2026 | Contract foundation: credentials and access policy, the HTTP request pipeline and base resources, the OpenAPI document, idempotency and concurrency control, asynchronous batch operations, file uploads, public events, auditing and usage limits, sandbox and reproducible scenarios | no client published |
| 3.1.0 | September 22, 2026 | Showcases, banners, and navigation; products and modules; lessons, content, and taxonomies; users, tags, and custom fields; appearance, security, email, and profile settings; administrators and credential governance; webhooks (destinations, deliveries, and replays) | no client published |
| 3.2.0 | September 23, 2026 | Enrollments, release rules, and progress; exams, questions, and results; certificates and legal terms; support tickets; sales, deliveries, and gateway integrations; gamification, points, and rankings; funnels, triggers, and automations; account domains and replicas; imports, exports, and reports; validation plans and batch application | no client published |
| 3.3.0 | September 23, 2026 | No new operations. Replica behavior: publishing a product or module through the API also publishes its copies in replicated accounts; replicated products, modules, showcases, and banners accept `status` in `PATCH`; copies from or to a replica, new modules in a replicated product, and lessons moved to a replicated product return `403 replica_readonly`; the showcase deletion description was corrected (the showcase's products are deleted with it). | no client published |
| 3.3.1 | September 24, 2026 | No new operations. The OpenAPI document groups its tags with the `x-tagGroups` extension, used by documentation tools to organize the navigation. | no client published |
| 3.3.2 | September 24, 2026 | No new operations. The `Sales`, `Account`, and `Settings` tags are split into smaller resource tags, and `x-tagGroups` gains separate `Account` and `Settings` groups. Operation IDs and paths are unchanged. | no client published |
