> ## Documentation Index
> Fetch the complete documentation index at: https://docs.campaign.ojage.org/llms.txt
> Use this file to discover all available pages before exploring further.

# API versioning

> Why the version is in the URL, what may change without one, and what happens when a change cannot.

Why the version is in the URL, what may change without one, and what happens when
a change cannot.

## The policy

**The version is a path segment.** Every endpoint lives under `/api/v1`.

Three alternatives were considered:

| Option | Why not |
| - | - |
| Header (`Accept-Version: 2`) | Invisible. A URL pasted into a browser, a log or a bug report does not carry it, so you cannot tell which version answered. |
| Media type (`application/vnd.campaigns.v2+json`) | Same problem, worse: it also changes content negotiation, caching and any client that hard-codes `Content-Type`. |
| Custom `X-API-Version` | Same invisibility, plus it is a header caches are not obliged to vary on. |
| **Path segment** | **Self-describing everywhere: URLs, logs, screenshots, `curl`.** The cost is that URLs differ per version, which is the point. |

The version constant lives in `packages/contracts/src/http/version.ts`. The client
prefixes every request with it, the server mounts routes under it, and the
generated specification is built from it — so the three cannot disagree.

## Within `v1`: additive only

Ship freely:

* new endpoints
* new optional request fields
* new response fields
* new error codes
* anything under a path not yet published

Clients written against the contract are expected to ignore fields they do not
know and to treat unknown fields as absent. Unknown *request* fields are ignored
rather than rejected, so a newer client can talk to an older API.

## Requiring `v2`

Ship a new version when:

* a field is removed or renamed
* an optional field becomes required
* a type, range or enum narrows
* a status code changes
* a default changes
* an existing field changes meaning

Renaming a field is breaking even if the value is identical: it breaks every
client that reads the old name, and a rename cannot be negotiated.

## Shipping both

During a migration the API serves several versions at once. Nothing is switched
over globally; clients move when they can.

For each superseded operation:

1. Mark it `deprecated: true` in the registry. The generated specification shows
   it as deprecated, so the reference stops recommending it.
2. Add the removal date as `sunset` (an HTTP-date). `DeprecationInterceptor`
   reads both fields off the registry and adds `Deprecation: true` and `Sunset`
   to every response for that operation, so a client discovers the date from a
   normal response without reading the documentation (RFC 8594).
3. Remove it, and only it, on that date.

The header and the specification cannot disagree, because both are derived from
the same registry entry: there is no second place to remember to update.

Removing a version is a deliberate act with its own change — never a side effect
of the next feature.

## What a client can rely on

* `x-api-version` on every response states which version answered. A client can
  assert it during rollout instead of guessing from a URL.
* Within a version, the specification is the contract. It is generated from the
  schemas the server enforces, so it cannot promise more than the server accepts.
* Unversioned paths do not exist. A request to `/api/customers` is a `404`, not a
  silent fallback to the current version — a client that lost its version is
  visibly broken rather than quietly talking to a different contract.

## The specification as a migration guide

The diff of `docs/reference/openapi.json` between versions is a field-by-field
account of what changed: names that disappeared, fields that became required,
enums that narrowed. Regenerate it with `pnpm openapi:write` and review it as
part of the change. See [ship a breaking change](/how-to/ship-a-breaking-change)
for the full procedure.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.