> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.rhombus.community/llms.txt
> Use this file to discover all available pages before exploring further.

# 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.

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:

```text theme={null}
/topic/change/{orgUuid}
```

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:

```json theme={null}
{
  "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                                                                                                       |

<Note>
  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.
</Note>

## 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 |

<Note>
  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.
</Note>

<Tip>
  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.
</Tip>

## 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:

```python theme={null}
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())}")
```

<Tip>
  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.
</Tip>

## Filtering Events

### By Entity Type

Filter for specific event types to reduce noise:

```python theme={null}
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:

```python theme={null}
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:

```python theme={null}
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:

```python theme={null}
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

```python theme={null}
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

```python theme={null}
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")
```

<Note>
  Cache camera names locally to avoid excessive REST API calls. Camera names change infrequently, so a cache with a 5-minute TTL works well.
</Note>

## Output Formats

### Formatted Alert Display

```python theme={null}
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 theme={null}
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 theme={null}
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 theme={null}
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 theme={null}
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()
```
