list_protect_events
Query historical UniFi Protect events (motion, smart-detect, sensor open/close) from an NVR for a given time range, filtering by type, smart-detect subtype, camera, or category.
Instructions
Query historical Protect events (motion, smart-detect, sensor open/close, etc.).
This uses the private /proxy/protect/api/events REST path — the ONLY source of historical events. The official Protect Integration API exposes events solely over WebSocket (/v1/subscribe/events) with no REST query endpoint, so do not expect the integration path to answer this.
REQUIRED: host, start, and end. start/end are epoch SECONDS as INTEGERS (e.g.
1690000000 for 2023-07-22T06:13:20Z), NOT milliseconds and NOT an ISO 8601 string:
a millisecond-magnitude value is rejected up front, and a string fails schema
validation. This differs on purpose from query_isp_metrics, whose start_time/end_time
are ISO 8601 STRINGS — do not carry a format across the two tools.
host: console name, ID, or composite ID (MAC:numericId format). start/end: REQUIRED. Epoch SECONDS (UTC) as integers, converted to milliseconds internally. Ranges are inclusive on both ends. History depth is bounded by the NVR's retention. (Contrast query_isp_metrics, which wants ISO 8601 strings.) types: filter by event TYPE; single value or a list. Verified-present values: motion, smartDetectZone, smartAudioDetect, sensorOpened, sensorClosed, access. NOTE: person/face/animal/alrmSpeak are NOT event types — they are smart-detect subtypes and belong in smart_detect_types, not here. An unrecognised value returns zero events. smart_detect_types: filter by the smart-detect SUBTYPE — person, vehicle, animal, package, face, licensePlate (on smartDetectZone events) and the audio alarms alrmSpeak, alrmSiren, alrmBark, alrmCarHorn (on smartAudioDetect events). This is a distinct upstream parameter from types. The API only honours it when types is also set to the relevant event type(s); passing smart_detect_types alone is a silent no-op upstream, so this tool rejects that with a clear error. Example: types="smartDetectZone", smart_detect_types="person" for just person detections; types="smartAudioDetect", smart_detect_types="alrmSpeak" to isolate the dominant audio-alarm noise. cameras: filter by camera NAME or ID; single value or list. Names resolve to IDs (case-insensitive) — an unknown name errors rather than silently matching nothing. categories: filter by event category; single value or list. Verified values: motion, smart, iot, admin. Unknown values are silently ignored by the upstream API. without_descriptions: when true, ask the API to omit each event's description block (~16% smaller payload). Opt-in only — full-fidelity records are the default and descriptions are never dropped automatically. limit/offset: offset-based pagination (not cursor-based). By default (neither given) every page is drained and the complete event set for the window is returned — a wide window can hold tens of thousands of events, so expect all of them, not just the first page. Pass offset or limit to fetch a single manual page instead; a capped drain is flagged incomplete rather than truncating. order_direction: "ASC" (default, oldest-first) or "DESC" (newest-first).
Sensor events set the top-level sensor field to null; the sensor reference at
metadata.sensorId.text is promoted to that field so you can filter/join on it.
Events are passed through verbatim, including identifiers (MAC/IP/hostname/name) and
the metadata.name object carrying camera / recognised-person / license-plate text; the
recognised-person name on face events is at metadata.detectedThumbnails[].matchedName.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| end | Yes | ||
| host | Yes | ||
| limit | No | ||
| start | Yes | ||
| types | No | ||
| offset | No | ||
| cameras | No | ||
| categories | No | ||
| order_direction | No | ASC | |
| smart_detect_types | No | ||
| without_descriptions | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||