The Rhombus WebSocket event stream delivers real-time notifications about everything happening in your organization. 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:
Every create, update, and delete operation across your Rhombus organization emits an event on this topic.
Event Payload Structure
Each MESSAGE frame contains a JSON body with the following fields:
{
"entity": "POLICY_ALERT",
"entityUuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"update": { },
"type": "CREATE",
"deviceUuid": "d1e2f3a4-b5c6-7890-abcd-ef1234567890",
"targetUsers": [],
"targetRoles": [],
"subLocationsHierarchyKey": ""
}
Core Fields
| Field | Type | Description |
|---|
entity | string | The type of entity that changed |
entityUuid | string | Unique identifier for the specific entity |
update | object | Entity-specific payload. Its contents vary by entity type and are not a fixed schema — inspect the keys present for each entity type you handle |
type | string | Change type: CREATE, UPDATE, DELETE, or REFRESH |
deviceUuid | string | Associated device UUID (when applicable) |
targetUsers | array | Users this change is targeted to |
targetRoles | array | Roles this change is targeted to |
subLocationsHierarchyKey | string | Sub-location hierarchy key for the change |
Event-specific detail (for example, the particulars of a policy alert) lives inside the update object. Its shape depends on the entity type, so read the keys present on update rather than assuming a fixed structure.
Change Types
| Type | Description | Example |
|---|
CREATE | A new entity was created | New policy alert triggered |
UPDATE | An existing entity was modified | Alert status changed |
DELETE | An entity was removed | Alert dismissed or cleared |
REFRESH | A general “re-fetch this entity” signal with no specific change semantics. This is the default when a producer doesn’t set a more specific type, so expect to receive it | Entity changed in a way that isn’t a discrete create/update/delete |
Entity Types
The entity field indicates what kind of object changed. Common entity types include:
| Entity | Description |
|---|
POLICY_ALERT | Security policy violation alert |
DEVICE | A camera, sensor, or NVR device changed |
The entity types available depend on your organization’s configured devices and policies. Subscribe to /topic/change/{orgUuid} and log the entity field of each event to discover all available event types.
Start by filtering for POLICY_ALERT events, which are the most common use case. Every entity type is delivered on the same /topic/change/{orgUuid} topic — there is no server-side entity filter on the subscription, so filter client-side by inspecting the entity field in your message handler and ignoring the types you don’t need.
Inspecting Policy Alerts
When an event has entity: "POLICY_ALERT", the alert-specific detail is carried inside the update object. The shape of update depends on the entity type and is not a fixed public schema, so inspect the keys present on each alert rather than assuming particular fields:
def inspect_alert(payload):
if payload.get("entity") != "POLICY_ALERT":
return
update = payload.get("update", {})
# `update` is entity-specific. Discover its keys at runtime
# instead of assuming a fixed structure.
print(f"Alert {payload.get('entityUuid')} update keys: {list(update.keys())}")
To learn the structure of update for the entity types you care about, connect, subscribe, and log a few real events. The keys present depend on the entity type and may evolve, so treat update defensively.
Filtering Events
By Entity Type
Filter for specific event types to reduce noise:
def handle_message(payload):
entity = payload.get("entity")
if entity == "POLICY_ALERT":
handle_alert(payload)
elif entity == "DEVICE":
handle_device_change(payload)
else:
# Log or ignore other entity types
pass
By Change Type
React differently based on whether an event was created, updated, or deleted:
def handle_alert(payload):
change_type = payload.get("type")
if change_type == "CREATE":
# New alert - send notification
send_notification(payload)
elif change_type == "UPDATE":
# Alert updated - refresh dashboard
refresh_dashboard(payload)
elif change_type == "DELETE":
# Alert cleared - close ticket
close_ticket(payload)
By Device
Filter events for a specific camera or sensor:
WATCHED_DEVICES = {
"camera-uuid-lobby",
"camera-uuid-parking-lot",
}
def handle_message(payload):
device_uuid = payload.get("deviceUuid")
if device_uuid in WATCHED_DEVICES:
process_event(payload)
By Entity and Change Type
React to specific kinds of security events by combining the entity and type fields:
def handle_alert(payload):
if payload.get("entity") == "POLICY_ALERT" and payload.get("type") == "CREATE":
# A new policy alert was raised. Alert-specific detail is in `update`;
# inspect its keys to decide how to route the notification.
send_urgent_notification(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
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-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
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-apikey": api_token,
"Content-Type": "application/json"
},
json={"policyAlertUuid": alert_uuid}
)
return response.json().get("policyAlert")
Cache camera names locally to avoid excessive REST API calls. Camera names change infrequently, so a cache with a 5-minute TTL works well.
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:
import json
def output_json(payload):
print(json.dumps(payload, indent=2))
Integration Patterns
Webhook Relay
Forward Rhombus events to your own webhook endpoint:
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:
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:
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()