WebSocket Streaming
Receive real-time session events as your test runs via WebSocket.
Connection
Connect to the stream for a test by opening a WebSocket to the following URL. Replace {test_id} with the ID returned when you created the test.
wss://stream.simutest.dev/tests/{test_id}Authenticate by including your API key in the Authorization header as a Bearer token. The connection will be rejected with 4401 if the key is invalid or lacks the required scope.
Tip: The stream_url field in the create test response already contains the fully-formed WebSocket URL for that test.
Event Types
All messages are JSON-encoded. Each message includes a type field, a timestamp (ISO 8601), and a data object.
| Event Type | Description |
|---|---|
| session_started | A new AI session has started; includes persona details |
| session_action | The session performed a navigation action with thinking snippet |
| session_completed | A session finished; includes outcome, scores, and summary |
| test_progress | Periodic progress update for the overall test |
| test_completed | All sessions finished; includes aggregate scores and report URL |
Event Payloads
session_started
{
"type": "session_started",
"timestamp": "2025-01-15T10:02:34Z",
"data": {
"session_id": "sess_xyz789",
"persona_name": "Busy professional, 35, mobile-first",
"viewport": "mobile"
}
}session_action
{
"type": "session_action",
"timestamp": "2025-01-15T10:02:41Z",
"data": {
"session_id": "sess_xyz789",
"action": "click",
"target": "Pricing link in nav",
"thinking_snippet": "I need to find the pricing page. The navigation looks standard — let me check if there's a Pricing link..."
}
}session_completed
{
"type": "session_completed",
"timestamp": "2025-01-15T10:03:18Z",
"data": {
"session_id": "sess_xyz789",
"outcome": "success",
"duration_seconds": 44,
"scores": {
"task_completion": 1.0,
"cta_findability": 0.87,
"overall": 0.91
},
"summary": "User found the pricing page quickly via the nav link and completed sign-up without friction."
}
}test_progress
{
"type": "test_progress",
"timestamp": "2025-01-15T10:05:00Z",
"data": {
"sessions_completed": 42,
"sessions_total": 200,
"success_rate": 0.81,
"estimated_completion": "2025-01-15T10:22:00Z"
}
}test_completed
{
"type": "test_completed",
"timestamp": "2025-01-15T10:25:12Z",
"data": {
"test_id": "test_abc123",
"sessions_completed": 200,
"aggregate_scores": {
"task_completion": 0.84,
"cta_findability": 0.79,
"overall": 0.82
},
"report_url": "https://app.simutest.dev/reports/rpt_def456"
}
}Connection Handling
For production use, implement reconnection logic and heartbeat handling:
Reconnection
If the connection drops, reconnect with exponential backoff starting at 1 second, capped at 30 seconds. The stream replays the last 60 seconds of events on reconnect, so you won't miss events from a brief disconnect.
Heartbeat
The server sends a WebSocket ping every 30 seconds. Respond with a pong to keep the connection alive. Connections with no pong response for 60 seconds are closed with code 1001.
Error codes
4401 — unauthorized; 4404 — test not found; 4410 — test already completed (connect to the REST API instead).
const MAX_RETRIES = 5;
let retries = 0;
function connect(testId: string, token: string) {
const ws = new WebSocket(`wss://stream.simutest.dev/tests/${testId}`, {
headers: { Authorization: `Bearer ${token}` },
});
ws.on('open', () => {
retries = 0;
console.log('Connected');
});
ws.on('close', (code) => {
if (code !== 1000 && retries < MAX_RETRIES) {
const delay = Math.min(1000 * 2 ** retries, 30000);
retries++;
setTimeout(() => connect(testId, token), delay);
}
});
ws.on('ping', () => ws.pong());
return ws;
}JavaScript Example
A complete example handling all event types:
const ws = new WebSocket('wss://stream.simutest.dev/tests/test_abc123', {
headers: { 'Authorization': 'Bearer st_live_abc123...' }
});
ws.on('message', (data) => {
const event = JSON.parse(data);
switch (event.type) {
case 'session_started':
// { session_id, persona_name }
break;
case 'session_completed':
// { session_id, scores, summary, duration }
break;
case 'test_completed':
// { aggregate_scores, report_url }
break;
}
});On this page