Skip to content

WebSocket Event Monitoring and Filtering

Subscribe to real-time WebSocket events from Rhombus — filter and process policy alerts, device state changes, motion detection, and access control events.

On this page

The Rhombus WebSocket event stream delivers real-time notifications about activity in your organization that your credential's role can see. This guide covers the event structure, available entity types, and patterns for filtering and processing events.

Event topic

All organizational events are published to a single topic:

/topic/change/{orgUuid}

Changes to many kinds of objects in your organization emit an event on this topic, from new policy alerts to camera state and door components; the event reference lists them. A connection receives the events its credential's role covers, policy alerts included; events for other devices and locations are dropped without an error (see What a connection receives).

Event payload structure

Each MESSAGE frame carries a JSON body. A new policy alert looks like this (shortened):

json
{
  "entity": "POLICY_ALERT",
  "type": "CREATE",
  "update": {
    "uuid": "Xq3vT1bKQ2m8c9Zr0aLwYg",
    "type": "POLICY_ALERT_V2",
    "deviceUuid": "ItkwWv1qT7Wy8-pY8KDesw",
    "locationUuid": "Rk2pQ7yLT0qX4b1nVt8sJw",
    "timestampMs": 1791367200000,
    "policyAlertTriggers": ["MOTION_HUMAN"],
    "textDescription": "Human detected at Front Lobby"
  }
}

Core fields

FieldTypeDescription
entitystringThe kind of object that changed
typestringCREATE, UPDATE, DELETE or REFRESH
entityUuidstringThe object that changed. Absent on a new policy alert: use update.uuid
updateobjectThe object's new state or changed fields; absent on many messages. Its shape depends on entity
deviceUuidstringThe device involved, on the few messages that set it
subLocationsHierarchyKeystringThe sub-location path, on some alarm-monitoring messages

The organization isn't in the body: it's in the destination and the ORG_UUID frame header. The event reference documents every field, including the full policy alert.

Change types

TypeMeaningExample
CREATESomething new, sent with the whole objectA new policy alert
UPDATEAn existing object changedA camera's state changed
REFRESHThe object changed; read it again if you need its stateA door sensor reported a change
DELETEThe object was removed, with no updateAn alert was removed or archived

Entity types

The entity field says what kind of object changed. The ones most integrations use:

EntitySent when
POLICY_ALERTAn alert is created (CREATE, with the alert), linked to a threat case (UPDATE) or removed (DELETE)
CAMERA_STATE, AUDIO_STATEA camera's or audio gateway's state changes (UPDATE, with the new state)
DOOR_STATE, CLIMATE_STATE, OCCUPANCY_STATE, PROXIMITY_STATEA sensor reports a change (REFRESH, with the changed fields)
CAMERA, DOOR_SENSOR, CLIMATE_SENSOR, PROXIMITY_SENSORA device is added (REFRESH)
ACCESS_CONTROLLED_DOOR, COMPONENT_*_SHADOWAn access-controlled door or one of its components changes
POLICY, SCHEDULEA policy or schedule is changed
SAVED_CLIPSA saved clip changes (REFRESH)

The event reference lists every entity type. Every type arrives on the same destination; there's no server-side filter, so filter on entity in your handler.

Inspecting policy alerts

A new alert arrives as CREATE with the whole alert in update: its uuid, the camera (deviceUuid), timestampMs, the policyAlertTriggers that fired, and more. It's sent once, when the alert is first stored, so fields Rhombus fills in afterward (such as clip locations and finalized) may still be at their initial values; fetch the alert from the REST API when you need its final state.

python
def inspect_alert(payload):
    if payload.get("entity") != "POLICY_ALERT" or payload.get("type") != "CREATE":
        return
    alert = payload["update"]
    print(alert["uuid"], alert.get("deviceUuid"), alert["timestampMs"], alert.get("policyAlertTriggers"))

Filtering events

By entity type

python
def handle_message(payload):
    entity = payload.get("entity")

    if entity == "POLICY_ALERT":
        handle_alert(payload)
    elif entity == "CAMERA_STATE":
        handle_camera_state(payload)
    else:
        pass  # ignore types you don't use

By change type

python
def handle_alert(payload):
    change_type = payload.get("type")

    if change_type == "CREATE":
        send_notification(payload["update"])        # a new alert
    elif change_type == "UPDATE":
        refresh_alert(payload["entityUuid"])        # linked to a threat case
    elif change_type == "DELETE":
        remove_alert(payload["entityUuid"])         # removed or archived

Don't rely on DELETE messages to track every removed alert: they're limited to one per organization every 10 minutes.

By device

Where the device is depends on the entity: a policy alert names it in update.deviceUuid, and a device-state message is about the device in entityUuid.

python
WATCHED_DEVICES = {"ItkwWv1qT7Wy8-pY8KDesw"}

def device_of(payload):
    if payload.get("entity") == "POLICY_ALERT":
        return (payload.get("update") or {}).get("deviceUuid")
    return payload.get("deviceUuid") or payload.get("entityUuid")

def handle_message(payload):
    if device_of(payload) in WATCHED_DEVICES:
        process_event(payload)

Enriching events with REST API data

WebSocket events contain minimal data for efficiency. Use the REST API to fetch additional details when needed:

Get camera name from device UUID

python
import requests

def get_camera_name(api_token, device_uuid):
    """Fetch camera name for display purposes."""
    response = requests.post(
        "https://api2.rhombussystems.com/api/camera/getMinimalCameraStateList",
        headers={
            "x-auth-scheme": "api-token",
            "x-auth-apikey": api_token,
            "Content-Type": "application/json"
        },
        json={}
    )
    cameras = response.json().get("cameraStates", [])
    for camera in cameras:
        if camera.get("uuid") == device_uuid:
            return camera.get("name", "Unknown Camera")
    return "Unknown Camera"

Get alert details

python
def get_alert_details(api_token, alert_uuid):
    """Fetch full policy-alert details from the REST API."""
    response = requests.post(
        "https://api2.rhombussystems.com/api/event/getPolicyAlertDetails",
        headers={
            "x-auth-scheme": "api-token",
            "x-auth-apikey": api_token,
            "Content-Type": "application/json"
        },
        json={"policyAlertUuid": alert_uuid}
    )
    return response.json().get("policyAlert")

Output formats

Formatted alert display

python
def display_alert(payload, camera_name=""):
    entity = payload.get("entity", "UNKNOWN")
    change = payload.get("type", "UNKNOWN")
    update = payload.get("update", {})

    print(f"{change} {entity}  camera={camera_name}")
    print(f"  device={payload.get('deviceUuid', 'N/A')}")
    print(f"  uuid={payload.get('entityUuid', 'N/A')}")
    # `update` is entity-specific; print its keys to discover available detail.
    if update:
        print(f"  update keys={list(update.keys())}")
    print()

Raw JSON output

For piping to other tools or logging systems:

python
import json

def output_json(payload):
    print(json.dumps(payload, indent=2))

Integration patterns

Webhook relay

Forward Rhombus events to your own webhook endpoint:

python
import requests

WEBHOOK_URL = "https://your-server.com/webhooks/rhombus"

def relay_to_webhook(payload):
    try:
        requests.post(WEBHOOK_URL, json=payload, timeout=5)
    except requests.RequestException as e:
        print(f"Webhook relay failed: {e}")

Slack notifications

Send high-priority alerts to a Slack channel:

python
def send_slack_alert(payload, webhook_url):
    entity = payload.get("entity", "UNKNOWN")
    change = payload.get("type", "UNKNOWN")
    message = {
        "text": f":rotating_light: *Security Alert*\n"
                f"{change} {entity}\n"
                f"Entity: {payload.get('entityUuid', 'N/A')}  "
                f"Device: {payload.get('deviceUuid', 'N/A')}"
    }
    try:
        requests.post(webhook_url, json=message, timeout=5)
    except requests.RequestException as e:
        print(f"Slack notification failed: {e}")

Database logging

Persist events for historical analysis:

python
import sqlite3
import json

def log_event(db_path, payload):
    conn = sqlite3.connect(db_path)
    conn.execute("""
        INSERT INTO events (entity, entity_uuid, change_type, device_uuid, payload)
        VALUES (?, ?, ?, ?, ?)
    """, (
        payload.get("entity"),
        payload.get("entityUuid"),
        payload.get("type"),
        payload.get("deviceUuid"),
        json.dumps(payload)
    ))
    conn.commit()
    conn.close()