Sending the token
?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 (neitherAuthorizationheader 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).
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).