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

# Versioning & breaking changes

> What we guarantee, what we ask of your integration, and how contractVersion moves

Every wire change is announced on the [changelog](/changelog), labelled **breaking** or not.
Breaking means an integration that was correct against the previous contract, and follows the
tolerance rules below, stops working or starts silently misreading.

A breaking change does not always bump [`contractVersion`](#contractversion) — the version covers
each sport's event and state schema, so a change to the catalog, history, health, or auth surfaces
has no version to move. Read the entry's label, not the version, to know if you are affected.

## What we guarantee, and what we ask of you

Some changes are breaking for a strict parser and harmless for a tolerant one. So that "breaking"
means something precise rather than depending on how your client is written, we classify every
change against these four assumptions about your integration:

1. **Ignore object fields you don't recognise.** We add fields without warning; a parser that
   rejects unknown keys will break on an addition we consider safe.
2. **Ignore event `type` values you don't recognise** — don't throw on an unmatched branch. New
   event types appear as a sport's vocabulary grows.
3. **Treat `league` and `sport` as opaque strings.** They are open vocabularies; new values appear
   when we add a league or a sport.
4. **Don't parse ids.** `sessionId` and `gameId` are opaque — their format is not a contract, and
   it has changed before.

Given those, an added field, an added event type, an added endpoint, an added optional query
parameter, and a field that becomes *more* guaranteed (nullable → always present) are **not**
breaking. Removing or renaming a field, changing a field's type or meaning, a field becoming
nullable, removing an event type, changing a value in an open vocabulary, moving an endpoint,
changing what a status code means, and rejecting input we previously accepted **are**.

## `contractVersion`

The WebSocket **hello frame** and every history response carry a `contractVersion` string.
**Each sport's feed versions independently** — basketball, soccer, baseball, and football each
carry their own `contractVersion` — so the schema identity is the **pair**
(`sport`, `contractVersion`); compare **both**. Versions count independently per sport, so "v5" alone is
ambiguous — both fields ride beside each other wherever the version appears. It bumps
**only** when that sport's wire schema does — a field added, removed, renamed, an event type added, or a
field's meaning changed. Logic fixes and additive optional fields that carry a default do **not** bump it.

It is a schema fingerprint, not a breakage signal: it moves for changes that cannot affect you
(a contract catching up to bytes you already receive), and stays put for changes that can
(a catalog field rename). Use it to detect that the schema you fold against has moved; use each
entry's `breaking` label to decide whether you must act.

Treat a pair you don't recognise as "read the changelog before trusting this frame":

```python theme={null}
EXPECTED = ("basketball", "v9")  # the identity is the pair — the version alone collides across sports
if (frame["sport"], frame["contractVersion"]) != EXPECTED:
    raise RuntimeError(
        f"feed is {(frame['sport'], frame['contractVersion'])}, integration expects {EXPECTED} "
        f"— see https://.../changelog before upgrading"
    )
```
