Agent

Subscribe to events over WebSocket

A read-only WebSocket on the events stream address: handshake auth, frame format, close codes, limits and backfill.

Besides pulling session events with Stream events (SSE), you can receive them over a WebSocket on the same address: one frame per event, with exactly the same event data as SSE. This is an access mode the platform offers in addition to the official SSE, suited to a server that keeps a long-lived connection and forwards events into your own system.

wss://api.deepraxis.com/agent/v1/sessions/{session_id}/events/stream

The connection is read-only: it only pushes events and accepts no messages. Sending messages and interrupting still go through Send events.

Handshake and authentication

The handshake carries the same auth headers as the HTTP endpoints: Authorization: Bearer <API Key> or x-api-key: <API Key>.

A browser's native WebSocket cannot set request headers, so this is a server-side surface. In a browser, use SSE, or let your server open the connection and relay it to the front end.

When the handshake fails, no connection is opened and the server answers with an HTTP error (body format in Errors):

CaseResponse
No credential, or the key is invalid or disabled401
The session does not exist, or is not in this key's workspace404
In-flight requests exceed the key's concurrency limit429, see Rate limits

An open WebSocket, like an in-progress SSE, holds one of the key's concurrency slots until it closes.

Frames

Every frame is one JSON text. After connecting, the first frame is a ready notice:

{"type":"session.events.ready"}

After that, each event produced in the session arrives as one frame, and its event field is the SSE event data:

{"type":"session.event","event":{"id":"d4dc3d96-…","type":"agent.message","processed_at":"2026-09-26T13:35:04.906Z","content":[{"type":"text","text":"OK"}]}}
FieldMeaning
typesession.events.ready (ready, sent once) or session.event (one event)
eventOnly on session.event frames; same shape as a single event from List events

The same event can arrive more than once. A user.message, for example, first appears with processed_at: null (accepted, pending) and again with a timestamp once processed. Deduplicate by event.id and keep the latest.

Close codes

CodeMeaningWhat to do
1000The server ended the subscription normallyReconnect if you still need events
1008The client sent a message on the connectionThe connection is read-only; send no frames
1011Internal server interruptionReconnect and backfill with List events
1013A limit was exceeded for now (see below)Reconnect and backfill with List events

Limits

  • One connection pushes at most about 512 KB of events in total, then closes with 1013. Be ready to reconnect on long sessions.
  • The server retries its internal connection to the session automatically; if repeated retries fail, it also closes with 1013.

Disconnects and backfill

Events produced while you are disconnected are not replayed. After reconnecting, fill the gap with List events by time:

GET /agent/v1/sessions/{session_id}/events?order=asc&created_at[gt]=<processed_at of the last event you received>

Backfilled events and newly pushed ones can overlap; deduplicate by event.id.

Examples

Bun (the built-in WebSocket supports headers):

const ws = new WebSocket(
  `wss://api.deepraxis.com/agent/v1/sessions/${sessionId}/events/stream`,
  { headers: { Authorization: `Bearer ${process.env.PRAXIS_API_KEY}` } },
);
ws.addEventListener("message", (message) => {
  const frame = JSON.parse(String(message.data));
  if (frame.type === "session.events.ready") console.log("ready");
  if (frame.type === "session.event") console.log(frame.event.type, frame.event);
});
ws.addEventListener("close", (close) => console.log("closed", close.code));

Node (the ws package):

import WebSocket from "ws";

const ws = new WebSocket(
  `wss://api.deepraxis.com/agent/v1/sessions/${sessionId}/events/stream`,
  { headers: { Authorization: `Bearer ${process.env.PRAXIS_API_KEY}` } },
);
ws.on("message", (data) => {
  const frame = JSON.parse(String(data));
  if (frame.type === "session.event") console.log(frame.event.type, frame.event);
});
ws.on("close", (code) => console.log("closed", code));

Python (the websockets package):

import asyncio, json, os
import websockets

async def main(session_id: str):
    url = f"wss://api.deepraxis.com/agent/v1/sessions/{session_id}/events/stream"
    headers = {"Authorization": f"Bearer {os.environ['PRAXIS_API_KEY']}"}
    async with websockets.connect(url, additional_headers=headers) as ws:
        async for message in ws:
            frame = json.loads(message)
            if frame["type"] == "session.event":
                print(frame["event"]["type"], frame["event"])

asyncio.run(main("sesn_123"))
Export

On this page