Skip to main content
The feed requires a consumer token on every read endpoint — the WebSocket, the history backfill, the sessions catalog, and the per-session health read. Present it as a Bearer token or a query parameter.

Sending the token

The query-parameter form (?token=) exists because a browser WebSocket cannot set an Authorization header. REST endpoints accept either form; browser WebSocket clients must use ?token=.
Browser clients making cross-origin POST requests (such as the scout UI) trigger a CORS preflight (OPTIONS). This is handled automatically by the browser and requires no action from a consumer. Consumer read paths (GET, WebSocket) are unaffected.

Failure modes

Every HTTP read endpoint (/api/history/{session}, /api/sessions, /api/health/{session}) refuses the same way, so the table is condition × transport rather than per endpoint. On HTTP {session} routes the id-format check runs before auth — a malformed id is 404 even with no credential at all. The WebSocket differs on credential refusal: the upgrade request itself is rejected with HTTP 403, before any WebSocket exists — so no close code or frame ever arrives. A browser WebSocket observes it as a failed connection (close 1006); a non-browser client sees the 403 response directly. The 401 body’s detail says which failure it was:
  • "authentication required" — no token was presented (neither Authorization header nor ?token=); some endpoints also answer this when the server cannot verify any token.
  • "invalid token" — a token was presented but does not resolve to an account (expired, revoked, or malformed).
  • "authentication unavailable" — the server itself has no way to verify tokens (a deployment misconfiguration, not something a consumer can fix — report it).
For the WebSocket’s full close-code vocabulary — including the post-handshake 4404/4403/1011 refusals and the error frame that precedes them — see WebSocket feed → Close codes.

Sessions

Every endpoint carries a {session} path segment that selects an independent match on the server — for example /api/history/T1. Sessions are isolated: a press on one never affects another. Your consumer token is valid across all sessions you are entitled to read, plus any session marked public — a public game is readable by any valid account regardless of entitlement (used for shareable promotional viewer links). Entitlements are granted per session: being able to read one session in a competition never implies access to another. Session ids are alphanumeric slugs. An id that does not match the expected format returns 404 before auth is checked on HTTP routes; on the WebSocket the handshake is refused pre-accept instead (observed as a failed connection — see Failure modes).