> ## 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.

# History backfill

> Fetch the full raw event log for a session, across any contract version

## Endpoint

```
GET /api/history/{session}
```

The session id alone addresses the log — the server resolves which sport the session belongs to.
This returns the full event log for one game: the events only, oldest to newest, in the same wire
format as the live feed, plus the contract version they were recorded under. The current state is
not included — for a live game the WebSocket carries the full snapshot on every frame. To rebuild
state from the log alone (a closed game, offline analysis), replay the events oldest to newest:
there is no client library — each sport page specifies every event's exact effect on state, and
each `undo` cancels the most recent earlier press, so keep undo rows in the replay. The
response's own `sport` field tells you which sport's event vocabulary and state shape apply, so a
stored session id is self-describing even without a [catalog](/api-reference/catalog) row (a
closed game leaves the open slice of the catalog, though it stays listed under `?status=closed`).

The endpoint works across every contract version. It reads the stored log directly, with no version
check, so a game recorded under an older schema stays available long after the live format has changed.
The schema identity is the **pair** (`sport`, `contractVersion`); compare **both** — versions count
independently per sport, so "v4" alone is ambiguous.

### Path parameters

| Parameter | Description              |
| --------- | ------------------------ |
| `session` | Session id for the match |

### Authentication

Use a Bearer token or the `?token=` query parameter — the same credential as `/subscribe`. You can
read a game if it is public, or if your account was granted that session. Finished games stay
readable, which is what public viewer links rely on. See [Authentication](/authentication).

<Note>
  This endpoint never checks the contract version — historical logs must stay readable across schema
  changes, so any `X-Contract-Version` header is ignored. Use the `contractVersion` in the response body
  to know which schema the events were recorded under.
</Note>

### Error responses

| Status | Condition                                                                                                                          |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing or invalid token                                                                                                           |
| `403`  | Valid token, but the game is private and the account was not granted that session                                                  |
| `404`  | Never published, or invalid session id format — the `detail` string distinguishes: `"session not found"` vs `"invalid session id"` |
| `410`  | The session was published, but its event log is permanently gone (`detail`: `"event log no longer available"`)                     |

A `404` always means the session was never published (or the id is malformed) — it never stands in
for a lost log. In the rare case a published session's stored log is lost (storage incident), the
answer is `410`, so a backfill pipeline can tell "this tape is gone" apart from "this session never
existed". The [catalog](/api-reference/catalog) flags the same condition up front via each row's
`historyAvailable` field, so you can skip a dead link without fetching it.

***

## Response

```json theme={null}
{
  "contractVersion": "v3",
  "sport": "soccer",
  "league": "wc",
  "sessionId": "wc-arg-bra",
  "matchup": { "a": "Argentina", "b": "Brazil" },
  "phase": "closed",
  "events": [...]
}
```

| Field             | Type                       | Description                                                                                                                                                                                                                                                                         |
| ----------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `contractVersion` | string                     | The contract version this game's log was **recorded under**. Read the events against this schema.                                                                                                                                                                                   |
| `sport`           | string                     | Which sport this game is: `"basketball"`, `"soccer"`, `"baseball"`, or `"football"` — the sport whose contract `contractVersion` counts under, and whose event vocabulary and state shape `events` use. The schema identity is the pair (`sport`, `contractVersion`); compare both. |
| `league`          | string \| null             | League slug the session was created under (e.g. `nba`, `wc`) — the same value the catalog carries. `null` for a log written before the session lifecycle.                                                                                                                           |
| `sessionId`       | string                     | Echo of the `session` path parameter. For the real-world game this session records, see the [catalog](/api-reference/catalog) row's `gameId`                                                                                                                                        |
| `matchup`         | object \| null             | The side display names `{ a, b }`, fixed when the session was created. Same value the WebSocket carries as the top-level `matchup` on every frame. Render the header from this. `null` for a log written before the session lifecycle.                                              |
| `phase`           | string \| null             | The session's lifecycle phase: `"open"` (live — more events may arrive over the WebSocket) or `"closed"` (sealed — the log is final). Use it to label a finished game and stop expecting live frames. `null` for a log written before the session lifecycle.                        |
| `sandbox`         | `true` (only when present) | Present — and `true` — only on a [sandbox mock session](/sandbox)'s history. **Absent on real games** (never `false`), so existing parsers are unaffected.                                                                                                                          |
| `events`          | array                      | The complete event log, oldest to newest. Same `WireEvent` shape as live WebSocket frames. See below.                                                                                                                                                                               |

***

## Events

Each entry is one scout press, in the same `WireEvent` format the WebSocket uses for live frames. The
full log is returned — there is no pagination.

```json theme={null}
{
  "type": "goal",
  "payload": { "team": "A" },
  "seq": 12,
  "serverIngestTs": 1718630400.12
}
```

| Field            | Type    | Description                                                                                                                                            |
| ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `seq`            | integer | Scout press sequence number — a stable, unique key that always increases                                                                               |
| `type`           | string  | Event type — see the [sport's events page](/sports/overview)                                                                                           |
| `payload`        | object  | Event fields minus `type`. Same shape as `WireEvent.payload` on the WebSocket                                                                          |
| `serverIngestTs` | number  | When the server received this press — Unix time in seconds, carried as a fractional number with microsecond-level precision (e.g. `1718630400.123456`) |

`serverIngestTs` is a receipt stamp, not an ordering key. Events are returned oldest to newest and
`seq` is the ordering authority — a press that arrives out of order is placed by its `seq`, so
sorting by `serverIngestTs` can differ from the log's own order. Treat the fractional part as
informative precision, not a tiebreaker.

### Reconnect and deduplication

On reconnect, re-fetch `/history` and remove duplicates against your existing log by `seq` before
appending live frames. Events you already buffered from the WebSocket will appear again in the
backfill — `seq` is the key that identifies them.
