The same log as a live tail, framed as SSE: one event per row, `id` the seq, `event` the wire `type`, `data` the same [`SessionEventBody`] the paged read renders. The stream stays open across idle stretches -- a keep-alive comment holds the connection while nothing happens -- so one connection spans a session's whole life rather than a single run. With no position named it starts at the live head, fixed before the response begins; a client that wants history pages `/events` newest-first and deduplicates on seq against what the tail delivers. An initial id-only frame establishes the reconnect cursor without dispatching a message. Continuity before that frame is received requires an explicit `after_seq`.
A separate operation rather than an Accept on the paged read: an OpenAPI
operation carries one response body per status, so negotiating two on one
path leaves a generated client able to reach only whichever media type its
generator picked.
GET
/v1/sessions/{session_id}/events/streamAuthorization
AuthorizationBearer token · headerrequiredPath parameters
session_idstring<uuid>requiredSession id
Query parameters
after_seqinteger<int64>Events with seq strictly greater. Omitted tails from the current head;
0 replays the whole log.
Header parameters
Last-Event-IDinteger<int64>The seq of the last event received. Takes precedence over after_seq, since a reconnecting client sends it on its own and the original query is stale by then.
Responses
200The event stream from after_seq, then live. The start position is fixed before the response begins, so a client that opens the stream, then pages /events newest-first and deduplicates on seq, misses nothing on that connection. An initial id-only frame establishes the reconnect cursor without dispatching a message. Continuity before that frame is received requires an explicit after_seq. Subsequent events carry the seq as their id, the wire type as their event name, and one SessionEventBody as their data. Stored rows the wire cannot render are skipped, and the cursor still advances over them.
seqinteger<int64>required400Malformed path parameter or Last-Event-ID header.
errorErrorDetailrequiredShow propertiesHide properties
codeErrorCoderequiredThe machine-readable half of an error response, and the field a client
branches on. Closed: every error carries exactly one of these.
Allowed:
unauthorizedforbiddennot_foundinvalid_requestconflictclient_outdatedprecondition_failedtoo_many_requestsinternalmessagestringrequired401Missing, malformed, or invalid bearer token.
errorErrorDetailrequiredShow propertiesHide properties
codeErrorCoderequiredThe machine-readable half of an error response, and the field a client
branches on. Closed: every error carries exactly one of these.
Allowed:
unauthorizedforbiddennot_foundinvalid_requestconflictclient_outdatedprecondition_failedtoo_many_requestsinternalmessagestringrequired403Caller lacks session.read.
errorErrorDetailrequiredShow propertiesHide properties
codeErrorCoderequiredThe machine-readable half of an error response, and the field a client
branches on. Closed: every error carries exactly one of these.
Allowed:
unauthorizedforbiddennot_foundinvalid_requestconflictclient_outdatedprecondition_failedtoo_many_requestsinternalmessagestringrequired404No such session, or the caller cannot see it -- including a session token presented against any session but its own.
errorErrorDetailrequiredShow propertiesHide properties
codeErrorCoderequiredThe machine-readable half of an error response, and the field a client
branches on. Closed: every error carries exactly one of these.
Allowed:
unauthorizedforbiddennot_foundinvalid_requestconflictclient_outdatedprecondition_failedtoo_many_requestsinternalmessagestringrequired