The API version
Every documented route is under/api/v1, and the prefix is a compatibility
promise. Within v1:
These will not change:
- A route’s path or method.
- A field’s name, type or meaning in a response.
- The meaning of a status code for a given failure.
- A scope’s name, or what it permits.
- The pagination contract — cursors stay opaque and remain valid the way Pagination describes.
- New fields appearing in a response. Parse leniently: a client that rejects unknown fields will break on an additive release, and that is the client’s bug rather than an API break.
- New routes, new optional request fields, new enum members in a field documented as extensible.
- The opaque contents of a cursor. Treat it as a token, never decode it.
- Rate limits and quotas, which are operational rather than contractual. See Errors and limits.
/api/v2, not an edit to v1.
The specification is the contract
The OpenAPI specification — the endpoint pages in the API reference are generated from it — is the machine-readable version of the above, and a test keeps it honest: it enumerates the routes the router actually registers and fails the build if the specification and the code disagree about which routes exist. Stated with its limit, because a guard trusted beyond its limit is worse than no guard: it covers routes and methods, not request and response schemas. Those are hand-written and can drift. If you find the specification and a response disagreeing on a field, the response is what the software does — please report it.Release versions
Releases use semantic versioning on a tag:v0.1.0, then v0.2.0, v0.2.1,
and so on.
A release identifier orders releases and nothing more; it does not encode
compatibility. The API version does that. This is worth stating twice under
semver, because the scheme invites the opposite assumption: a major version bump
here would describe the product — a migration that cannot be rolled back, a
capability withdrawn — and the API underneath would still be /api/v1 unless
the API itself broke. Read the changelog labels for upgrade safety, not the
number.
Releases before v0.1.0 were identified by date — the
changelog entries numbered 2026.07, 2026.08 and 2026.09.
Those identifiers are left as they were published rather than renumbered, so the
record matches what was announced at the time.
Deprecation
A capability being replaced is announced asdeprecated in the changelog entry
that introduces its replacement, and the entry names what to move to. It keeps
working until a release explicitly removes it, and that removal is labelled
changed.
There is no automatic sunset window and no Deprecation response header today.
If you are building something long-lived against the API, the changelog is the
surface to watch — it is where a deprecation is stated.

