# Versioning and change Every partner-facing response tells you which version of this surface answered it. Additive changes ship without notice; anything that could break a correct client needs a new version and 90 days. ## The header ``` X-Peritus-Api-Version: 1 ``` On success and error responses alike, including a `429` and a `404`. Log it, assert on it, and quote it to support. Versions are whole numbers, issued in order. `1` is the first. ## Your two pins, and the header that overrides them **Live and test are pinned separately.** A call authenticated by your API key — everything on this surface except `/embed/…`, `/partner/admin/…`, and the Game API — is answered on that key's own environment's pin when it sends no `X-Peritus-Api-Version` request header. Try a new version in test without moving live. `POST /partner/admin/api-version` moves one environment's pin, from the portal or from your own tooling against the same Firebase-authed admin surface: ``` POST /partner/admin/api-version Authorization: Bearer { "environment": "test", "version": 1 } 200 { "live": 1, "test": 1 } ``` A version outside what this build serves is refused, `400 unsupported_api_version`, and nothing is written. Moving backwards is a plain, unrefused write — the rollback for an upgrade that turned out to break something. Naming a served version in that same header as a *request* header uses that version for the one call it's sent on, whatever your account is pinned to. A name this build cannot serve gets `400 unsupported_api_version` instead of any version at all. `1` is what this build serves today. `/embed/…` and `/partner/admin/…` carry the response header too, but always at this build's current version — neither authenticates by API key, so neither has an account pin for it to reflect. ## Which responses carry it | Surface | Stamped | | --- | --- | | `/partner/…` — the session mint, the results feed, and the `/partner/admin/…` routes the portal drives | Yes, including its own 404s and 429s | | `/embed/…` — the frame document | Yes | | `/internal/…` — our own ops console | No | | The player-facing Game API your embedded session calls | No | `/internal/…` is not a contract with anyone. **The Game API is excluded because a partner's embedded player travels the same paths a first-party player does** — the same `/me/…` and `/matches/…` our own frontend calls. One call to `POST /partner/sessions` answers the version question for your whole integration. ## What may change without notice Any of these may ship at any time, on the current version: - **A new field on a response.** - **A new `reason` code.** - **A new value in an existing enum-like field.** - **A new endpoint.** ## What that requires of your client Those changes are additive: a correctly written client is never broken by one. “Correctly written” means exactly two things, and they are requirements rather than suggestions. 1. **Ignore fields you do not recognise.** Do not fail to parse a response because it grew a key, and do not assert on the full shape of a JSON object. 2. **Treat an unrecognised `reason` as the generic failure its HTTP status already describes.** A `403` carrying a code your build has never seen is still a `403`: refused. Branch on the codes you handle specially and fall through to the status for everything else. ## What may not change without a version bump and 90 days' notice These are breaking, and each one needs a new version and **90 days' notice**, with the old behaviour kept working for the whole period: - **Removing or renaming a field.** - **Removing a `reason` code, or changing what an existing one means.** A code that keeps its spelling but starts describing a different condition is still a break. - **Changing a field's type.** - **Changing the HTTP status for an existing condition.** - **Tightening validation**, so that a request which worked now fails. > **Notice is a changelog entry** naming the new version, what changed, and when the 90 days start and end. The changelog is below, and the whole document is at [/partner-api-changelog.md](/partner-api-changelog.md). ## The changelog One version has been announced, and it is the current one: `1`. No notice period is running yet. Each entry says what moved, when the ninety days started and when they end. Read the full text at [/partner-api-changelog.md](/partner-api-changelog.md) — published verbatim. ## Where the contract itself lives The version, this policy and every `reason` code are one document. [The error catalogue](/docs/errors.md) on this site is generated from that same document, published at [/partner-api-contract.md](/partner-api-contract.md), and every page under /docs is also available as markdown beside it (this one is [/docs/versioning.md](/docs/versioning.md)), with [/llms.txt](/llms.txt) as the index.