Skip to main content
Reactor exposes the runtime logs of any model, session, or instance you own as a live Server-Sent Events (SSE) stream. Use the reactor CLI for everyday use. Call the HTTP endpoint directly to build your own log viewer or integrate outside the CLI.

Stream logs with the CLI

With no argument, reactor logs streams your project’s model, resolved from reactor.yaml. Pass -f/--follow to keep tailing live. The rest of this page documents the GET /logs endpoint reactor logs calls. Reactor streams the same logs as a live Server-Sent Events (SSE) stream. 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.

What you can stream

Use model logs to watch the whole deployment. Use session logs to debug one request. Use instance logs to check one pod.
The instance_id value 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

A call names exactly one of model, session_id, or instance_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. stream_id is an opaque per-stream identifier. On a merged model= stream that tails many pods at once, key a resume or dedupe cursor on stream_id, not on timestamp alone. attributes is an optional object of platform-owned fields, kept separate from the untrusted line. It currently carries attributes.instance_id — the pod that emitted the line — so a merged model= stream can attribute each line to its source instance. The object is omitted when no such field applies. Every record the runtime writes carries the id of the live session, so an instance stream tells you which session produced a line. Each record also names the phase it was written in, as state and its coarser form runtime_state. A model that logs through get_logger() from reactor_runtime, or through the standard library’s logging, gets the same fields.

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

Pick what to stream

Pick a model (all instances, <slug>/<name>), a session_id, or an instance_id (one instance). For an instance_id, fetch it from GET /sessions/{session_id}/runtime or GET /models/{model_id}/instances.
2

Open the stream

GET /logs?{model | session_id | instance_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 models, sessions, and instances 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.