Skip to main content
Reactor exposes the runtime logs of any session or instance you own as a live Server-Sent Events (SSE) stream. The protocol is a single HTTP call: open GET /logs with your API key as a bearer token (Authorization: Bearer <key>) and read the stream. The same key you use for the rest of the Reactor API is the credential the log stream accepts. The endpoint is served under the same https://api.reactor.inc origin as the rest of the Reactor API. The transport is plain HTTP and SSE; no SDK is required.

When to use which scope

There are two log scopes. Pick the one that matches the resource you want to read. You’ll typically use session logs to debug a specific request, and instance logs to investigate a misbehaving model deployment as a whole.
machine_id is the identifier Reactor returns for the pod a session is currently bound to. You can read it from GET /sessions/{session_id}/runtime once the session has been scheduled, or list the live instances for a model with GET /models/{model_id}/instances.

Open the stream

GET /logs accepts exactly one of session_id or machine_id as a query parameter. Send your API key as the bearer token in the Authorization header. A browser client sends its Clerk JWT in the same header. The legacy Reactor-API-Key: <key> header is still accepted when no Authorization header is present. Query-string credentials are never accepted (see error responses).
EventSource is not supported. The EventSource API cannot set custom headers, and /logs only reads credentials from headers. The server rejects a credential-looking query parameter (?ticket=, ?token=, ?jwt=, …) with 400 query_token_not_supported. Call /logs from your backend with fetch and a ReadableStream reader, as shown above. Then relay the stream to your frontend. See Streaming to a browser.
200 OK with Content-Type: text/event-stream opens the stream. Anything else terminates the request before the stream begins.

Error responses

The error body is JSON: {"error": "<code>", "message": "<human detail>"}.

Event types

The stream uses a small, stable set of SSE event names. Every event carries a single data: line containing JSON.

event: log

A single log line emitted by the runtime.
line is the raw log line as emitted by the runtime. Reactor’s first-party models emit structured JSON, so you can JSON.parse(line) to get per-line fields. Models that emit free-form text are passed through verbatim.

event: heartbeat

An empty keepalive emitted on a regular cadence (every ~15 s by default) so proxies and load balancers don’t close idle connections.
Heartbeats carry no information beyond “the stream is still alive”. You typically ignore them; clients that care about staleness can use them to reset a watchdog timer.

event: error

A server-side problem that is about to terminate the stream. The message is safe to render to end users; debug_id correlates with Reactor’s internal log entry so support can investigate without you leaking sensitive details.
An error event is always followed by a terminal end event, so clients that only switch on end still close cleanly.

event: end

The terminal frame. Every stream ends with exactly one end event, after which the server closes the HTTP connection. The reason field tells you which boundary fired.

Limits and lifecycle

Streams are deliberately short-lived. Plan to reconnect periodically; the protocol is designed for it. These values are the platform defaults today; they may be tuned per environment or per account. The active limits for your stream are enforced server-side, so your client does not need to mirror them.

Streaming to a browser

Log streaming is a server-side operation, for two reasons:
  • A raw API key is long-lived and gives access to your whole account. Never send it to a browser.
  • Session logs are the model runtime’s own container output. Reactor passes them through unchanged. There is no field allowlist and no redaction. The caller also picks the verbosity, so a stream can carry debug and trace lines. Treat the stream as internal detail. Filter it before your end users see it.
To show logs in your own UI, proxy the stream:
1

Keep the key in your backend

Store the API key in your server environment. Your frontend never sees it.
2

Open the upstream stream

From your backend, call GET /logs with Authorization: Bearer <key>, exactly as shown above.
3

Re-emit to your frontend

Forward the SSE frames to your own endpoint. Protect that endpoint with your own user session. Filter or redact anything you do not want end users to read.
This minimal Node relay uses the reader loop from the JavaScript example above.
Reactor’s own dashboard streams logs directly from the browser with a Clerk session. That path is first-party only, so partner applications cannot use it.

Putting it together

The expected client loop is short:
1

Resolve the scope

Pick a session_id (for session logs) or a machine_id (for instance logs). For instance logs, fetch the machine_id from GET /sessions/{session_id}/runtime or GET /models/{model_id}/instances.
2

Open the stream

GET /logs?{session_id | machine_id}=… with your API key as the bearer token in the Authorization header. There is no separate ticket step.
3

Process events

Switch on the SSE event name. Handle log, observe heartbeat, treat error as a soft warning, and close cleanly on end.
4

Reconnect when needed

On end with reason: "deadline" (or shutdown/rate_limit), wait a short backoff and reopen the stream with the same API key.
/logs enforces the same ownership check as the rest of the Reactor API: you can only stream sessions and instances tied to models your account owns.

Next

Authenticate the CLI

How to obtain and configure the credentials the Reactor API expects.

Deploy a release

Once a model is live, its sessions and instances are ready to stream from.