Reading Events with SSE¶
RT Server publishes a live stream of everything a running instance produces — object tracks, analytics events (intrusions, line crossings, …), object crops, attributes, and periodic statistics — over Server-Sent Events (SSE). SSE is a standard, one-way HTTP streaming protocol supported natively by browsers (EventSource) and by simple line-based parsers in any language.
This is the same stream the Web Panel Live View uses. If you only need events in an external system such as a broker or database, see Configuring Exporting (MQTT) and the Database queries below; SSE is the right choice when you want a live push stream without any extra infrastructure.
The endpoint¶
GET /v1/core/instance/{instanceId}/consume_events_sse
The instance must be running for events to flow. The connection stays open and the server pushes events as they happen, with these response headers:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Transfer-Encoding: chunked
Message format¶
Each event is delivered as a standard SSE record with three fields:
id: 286
event: track
data: {"$id":"track","$version":1,"object_class":"Person", ... }
id- a monotonically increasing sequence number for the connection.event- the event type (the SSE "event name"). Use this to route handling.data- the event payload as a single-line JSON string. Every payload also repeats its type in the$idfield and its schema version in$version.
Event types¶
event name |
Description |
|---|---|
track |
An update for a tracked object (position, class, confidence). High frequency. |
crop |
A cropped image of a detected object, linked to a track/event. |
attribute |
An attribute inferred for a track (for example a vehicle color or person attribute). |
statistics |
Periodic processing statistics for the instance. |
event-intrusion / event-intrusion-end |
An object entered / left an intrusion area. |
event-line-crossing |
An object crossed a tripwire. |
event-area-enter / event-area-exit |
Area crossing events. |
event-loitering / event-loitering-end |
Loitering started / ended. |
event-crowd-detection |
A crowding threshold was reached. |
event-dwelling |
A dwelling event. |
event-tailgating |
A tailgating event on a line. |
event-object-left-removed |
An object was left behind or removed. |
event-armed-person / event-armed-person-end |
Armed-person detection started / ended. |
event-fallen-person / event-fallen-person-end |
Fallen-person detection started / ended. |
event-count-changed |
A counting line total changed. |
event-status-changed |
An analytics status change. |
The exact JSON schema for every event type is published at https://bin.cvedia.com/schema/.
Filtering the stream¶
By default the stream delivers every event type, and track events alone can be high volume. Restrict the stream to the types you care about with the events query parameter (comma-separated):
GET /v1/core/instance/{instanceId}/consume_events_sse?events=event-intrusion,event-line-crossing
Only the listed event types are sent on that connection.
Consuming the stream¶
JavaScript (browser)¶
The browser's native EventSource handles reconnection automatically. Register a listener per event type:
const instanceId = "899c89f9-90c3-c633-8586-8f09b005ebed";
const url = `http://localhost:3546/v1/core/instance/${instanceId}/consume_events_sse`;
const source = new EventSource(url);
source.addEventListener("event-intrusion", (e) => {
const ev = JSON.parse(e.data);
console.log(`Intrusion in "${ev.area_name}" by ${ev.object_class}`);
});
source.addEventListener("event-line-crossing", (e) => {
const ev = JSON.parse(e.data);
console.log(`${ev.object_class} crossed "${ev.tripwire_name}" going ${ev.crossing_direction}`);
});
source.addEventListener("track", (e) => {
const t = JSON.parse(e.data);
// high frequency — update an overlay, etc.
});
source.onerror = () => console.warn("SSE connection lost; the browser will retry");
Python¶
No extra dependency is required — SSE records are blank-line-delimited blocks of field: value lines:
import json
import requests
instance_id = "899c89f9-90c3-c633-8586-8f09b005ebed"
url = f"http://localhost:3546/v1/core/instance/{instance_id}/consume_events_sse"
params = {"events": "event-intrusion,event-line-crossing"}
with requests.get(url, params=params, stream=True) as r:
event_type, data = None, None
for line in r.iter_lines(decode_unicode=True):
if line == "": # blank line ends a record
if event_type and data:
payload = json.loads(data)
print(event_type, "->", payload.get("object_class"))
event_type, data = None, None
elif line.startswith("event:"):
event_type = line[len("event:"):].strip()
elif line.startswith("data:"):
data = line[len("data:"):].strip()
curl¶
For a quick look at the raw stream, use curl -N (no buffering):
curl -N "http://localhost:3546/v1/core/instance/<instanceId>/consume_events_sse?events=event-intrusion"
Example payloads¶
The payloads below were captured live from a running SecuRT instance.
event-intrusion¶
{
"$id": "event-intrusion",
"$version": 1,
"event_id": "2ae0877d-b082-437b-afb1-6dc3d27e0aaa",
"area_id": "6cbd8545-0889-4e27-8d7b-e313c9643d2a",
"area_name": "Front gate",
"instance_id": "899c89f9-90c3-c633-8586-8f09b005ebed",
"object_class": "Person",
"ref_tracking_id": "f57cf500-6aa5-4cc9-881e-2a9e819b9ff4",
"location": { "x": 0.205, "y": 0.821, "width": 0.033, "height": 0.120 },
"event_timestamp_ms": 32081,
"system_timestamp": 1781889224024,
"system_datetime": "2026-06-19T17:13:44Z"
}
The matching event-intrusion-end carries the same event_id plus an event_duration_ms field, letting you measure how long the object stayed inside the area.
event-line-crossing¶
{
"$id": "event-line-crossing",
"$version": 1,
"event_id": "b67033ef-0d89-467f-b9d8-da4b4d8fd91e",
"tripwire_id": "d52795da-61c8-4761-8303-d9c625895c0b",
"tripwire_name": "Doorway",
"instance_id": "899c89f9-90c3-c633-8586-8f09b005ebed",
"object_class": "Person",
"ref_tracking_id": "9c62ff0e-58a9-4bfa-8ebf-e44fe18b5fd2",
"crossing_direction": "down",
"location": { "x": 0.776, "y": 0.472, "width": 0.022, "height": 0.067 },
"event_timestamp_ms": 33281,
"system_timestamp": 1781889225226,
"system_datetime": "2026-06-19T17:13:45Z"
}
track¶
{
"$id": "track",
"$version": 1,
"tracking_id": "ccfd3025-49a2-4421-af19-563502811c9d",
"instance_id": "899c89f9-90c3-c633-8586-8f09b005ebed",
"object_class": "Person",
"is_moving": 1,
"detection_confidence": 0.872,
"age_ms": 9600,
"location": { "x": 0.300, "y": 0.206, "width": 0.013, "height": 0.053 },
"events": ["abe32aeb-970c-4cf3-a097-fa33dbbe64d6"],
"event_timestamp_ms": 29681,
"system_timestamp": 1781889221623,
"system_datetime": "2026-06-19T17:13:41Z"
}
The events array links a track to any analytics events it triggered.
crop¶
{
"$id": "crop",
"$version": 1,
"ref_event_id": "2ae0877d-b082-437b-afb1-6dc3d27e0aaa",
"ref_tracking_id": "f57cf500-6aa5-4cc9-881e-2a9e819b9ff4",
"confidence": 0.932,
"image": "/9j/4AAQSkZJRgABAQAAAQABAAD...",
"location": { "x": 0.223, "y": 0.846, "width": 0.086, "height": 0.154 },
"crop_timestamp_ms": 31481,
"event_timestamp_ms": 32081,
"instance_id": "899c89f9-90c3-c633-8586-8f09b005ebed",
"system_timestamp": 1781889224024,
"system_datetime": "2026-06-19T17:13:44Z"
}
image is a base64-encoded JPEG. Use ref_event_id / ref_tracking_id to associate the crop with the event or track that produced it.
Coordinates are normalized
Every location bounding box is normalized to the frame: x / y are the top-left corner and width / height are the size, all in the range 0.0–1.0. Multiply by the frame's pixel dimensions to get pixel coordinates.
Polling alternative¶
If a persistent connection is impractical, the same events can be drained with a regular request:
GET /v1/core/instance/{instanceId}/consume_events
It returns the events accumulated since the last call as an array of { dataType, jsonObject } pairs, or 204 No Content when no events are pending:
[
{ "dataType": "event-intrusion", "jsonObject": "{\"$id\":\"event-intrusion\", ...}" }
]
jsonObject is a JSON-encoded string with the same payload described above. Poll this endpoint on an interval; each call consumes (and clears) the pending events.
Querying historical events¶
SSE and polling deliver live events. To query past events and aggregated analytics from the on-device database, use the POST /v1/database/events, POST /v1/database/analytics, and POST /v1/database/tracks endpoints. Those are covered in the Querying Stored Data tutorial.
Next steps¶
- Configuring Analytics - define the areas and tripwires that generate these events.
- Configuring Exporting - push the same events to MQTT for downstream systems.
- Querying Stored Data - query past events and analytics from the database.