Realtime Events
Subscribe to workspace events with Server-Sent Events.
Realtime events are delivered from:
GET https://connect.torqee.app/api/v1/events/stream
The response is text/event-stream. Each event has event: <type> and JSON
data matching the WorkspaceEvent schema in the OpenAPI spec.
Event types
| Event type | Required scope |
|---|---|
session.created |
torqee:sessions:read |
session.status_changed |
torqee:sessions:read |
transcript.segment.created |
torqee:transcripts:read |
When filters is omitted, Connect subscribes to lifecycle events
(session.created and session.status_changed) and filters them by the scopes
the credential already has.
Filters
The filters query parameter is a URL-encoded JSON array. Entries are
OR-matched. Each entry has a type and an optional sessionId. For
transcript.segment.created, omit sessionId to receive segments from every
session in the workspace, including sessions that start after you connect.
Include sessionId to subscribe to just that session.
[
{ "type": "session.created" },
{ "type": "session.status_changed" },
{ "type": "transcript.segment.created", "sessionId": "session_123" }
]
To subscribe to lifecycle events and all transcripts in one connection:
[
{ "type": "session.created" },
{ "type": "session.status_changed" },
{ "type": "transcript.segment.created" }
]
curl -N "https://connect.torqee.app/api/v1/events/stream?filters=%5B%7B%22type%22%3A%22session.created%22%7D%5D" \
-H "Authorization: Bearer torqee_YOUR_API_KEY"
Browser EventSource cannot set an Authorization header directly. Use an SSE
client that supports headers, or stream the response with fetch:
const response = await fetch(
"https://connect.torqee.app/api/v1/events/stream",
{
headers: { Authorization: "Bearer torqee_YOUR_API_KEY" },
},
);
const reader = response.body?.getReader();
Cost and limits
Filters are limited to 20 entries. Each entry costs 10, and the current budget is 100. Requests above the budget return:
{
"error": "filter_cost_exceeded",
"cost": 110,
"budget": 100,
"entries": [
{ "type": "session.created", "cost": 10 },
{ "type": "session.status_changed", "cost": 10 }
]
}
Too many concurrent streams for the same workspace return:
{ "error": "connection_limit" }
Event delivery is best-effort. Events emitted while the client is disconnected are not replayed. Reconnect with backoff and reconcile missed state through the REST API.
Detecting missed segments
transcript.segment.created data includes sessionCommittedIndex, the same
index exposed by REST for committed segments. It starts at 0 and increases by
1 in commit order within each session, across recording executions. Delivery
is best-effort, so received indices can have gaps, and the same segment can
occasionally arrive more than once.
Track the last applied index separately for each session, starting at -1:
- If an event’s index is less than or equal to the last applied index, discard it as already applied. This also removes duplicate deliveries.
- If the index is exactly the last applied index plus 1, apply the segment and update the last applied index.
- If the index is higher, fetch
GET /api/v1/sessions/{sessionId}/transcripts?revisionKind=committedfrom Connect. Apply the committed segments insessionCommittedIndexorder through the last index in the response, then resume processing events. Buffer events for that session while fetching and process them with the same rules after reconciliation, discarding any segments already applied via REST.
These rules also detect missing history when the first received index is not 0. If an event has no sessionCommittedIndex, reconcile through the same REST
endpoint. The field is optional during the publisher rollout.
Do not use sequence to detect missed segments: it restarts for each recording
execution and can overlap within a session. Always reconcile through REST after
reconnecting, since disconnected events are not replayed.
Payload example
{
"type": "session.created",
"sessionId": "session_123",
"occurredAt": "2026-07-06T00:00:00.000Z",
"data": {
"sessionId": "session_123",
"occurredAt": "2026-07-06T00:00:00.000Z",
"session": {
"id": "session_123",
"label": "Discovery call",
"memo": null,
"status": "recording",
"startedAt": "2026-07-06T00:00:00.000Z",
"endedAt": null,
"createdAt": "2026-07-06T00:00:00.000Z",
"updatedAt": "2026-07-06T00:00:00.000Z"
}
}
}