Cameras API
The cameras API is the admin-persona lookup for every /api/v1/cameras/* endpoint. Developers integrating from code use REST camera endpoints instead.
Endpoints
All paths are prefixed with /api/v1. Authentication is required on every endpoint. Role names below map to D78-D81.
| Method | Path | Required role | Purpose |
|---|---|---|---|
| GET | /cameras | Any authenticated | List cameras visible to the caller. Viewers are scoped to their assigned sites. |
| POST | /cameras | Operator or above | Create a camera. Validates the connection synchronously for gateway mode. |
| GET | /cameras/{id} | Any authenticated | Fetch one camera by ID. |
| PATCH | /cameras/{id} | Operator or above | Update allowed fields. See Updatable fields. |
| DELETE | /cameras/{id} | Operator or above | Soft-delete the camera (sets deleted_at). |
| POST | /cameras/validate-connection | Operator or above | Probe an RTSP URL before saving it. |
| GET | /cameras/{id}/diagnostics | Any authenticated | Return connectivity diagnostics. |
| GET | /cameras/{id}/health | Any authenticated | Live streaming health metrics. |
| GET | /cameras/{id}/thumbnail | Any authenticated | Latest JPEG thumbnail from the camera. |
| POST | /cameras/{id}/snapshot | Any authenticated | Request a fresh snapshot via the gateway. |
| POST | /cameras/{id}/trigger | Operator or above | Manually trigger a recording. See Trigger recording. |
| POST | /cameras/{id}/ptz | Operator or above | Send a PTZ ContinuousMove command. Body: {direction, magnitude}. Routed through the video tunnel to the gateway. v1 supports six directions (pan_up/down/left/right + zoom_in/out). No presets. |
| GET | /cameras/{id}/analysis-focus | Any authenticated (site-scoped) | Get per-camera AI analysis focus (focus text, ignore guidance, monitoring schedule, version). |
| PUT | /cameras/{id}/analysis-focus | Any authenticated (site-scoped) | Update per-camera AI analysis focus. Handler at cloud/internal/devices/analysis_focus_handler.go:28 does NOT apply RequireOperator() — the route is gated only by the same auth + site-access middleware as the read endpoints. To restrict edits to operator+, the handler would need an explicit role check. |
| GET | /cameras/{id}/analysis-focus/history | Any authenticated (site-scoped) | Get analysis-focus version history (default 20, max 100 entries). |
| GET | /analysis-focus-presets | Any authenticated | List built-in focus presets (baby_safety, package_delivery, perimeter_security, elderly_care, pet_monitoring, retail_security, traffic_monitoring). |
| GET | /cameras/{id}/nbd-override | Operator or above | Get per-camera “No Big Deal” override knobs. Empty object {} if no override is set. |
| PATCH | /cameras/{id}/nbd-override | Operator or above | Partial-update NBD override knobs. Fields validated against the per-rule schema. |
| DELETE | /cameras/{id}/nbd-override | Operator or above | Clear the camera’s NBD override (camera inherits the rule’s NBD config). |
| GET | /streams/{camera_id}/status | Any authenticated | Stream status (streamable, codec, viewer count). |
Updatable fields
PATCH /cameras/{id} accepts only these fields. Any other key in the request body is silently dropped.
| Field | Type | Description |
|---|---|---|
name | string | Display name. |
site_id | uuid | Move the camera to a different site. |
enabled | boolean | Toggle streaming and recording. |
status | enum | Override status (pending, online, offline, error, disabled). |
recording_duration_sec | integer | 5 - 120. |
pre_event_buffer_sec | integer | 0 - 10. |
post_event_buffer_sec | integer | 0 - 10. |
ai_analysis_mode | enum | snapshot_only, multi_frame, full_video, disabled. |
ai_pass_mode | enum | one_pass, two_pass. |
prompt_pack_id | uuid | Per-camera prompt pack override. |
ai_provider_override | string | Provider key override. |
ai_model_override | string | Model ID override. |
motion_sensitivity | enum | low, medium, high. |
detection_stream | enum | sub, main. |
recording_stream | enum | main, sub. |
motion_zones_json | jsonb | Polygon zones. |
stream_transport | string | Transport protocol override. |
stream_mode | string | single or dual. |
Trigger recording
POST /api/v1/cameras/{id}/trigger — manual event trigger per D49. Creates an event and records a clip using the camera’s pre- and post-event buffers.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/cameras/{id}/trigger |
| Required role | Operator or above |
| Request body | None |
| Success status | 202 Accepted |
| Rate limit | 1 request per camera per 5 seconds |
| Rate-limit response | 429 Too Many Requests with Retry-After: 5 |
| Camera must be | status = online (otherwise 409 Conflict) |
| Behaviour when a recording is already active | Gateway-mode: extends the active recording instead of creating a new event; returns status: "recording_extended". Direct-mode: extend is not supported — every trigger creates a fresh event (B-12, closed 2026-05-28). |
| Audit action | camera.manual_trigger (always for direct-mode; gateway-mode when no active recording) or camera.recording_extended (gateway-mode only when extending) |
Response body:
{ "request_id": "<hex-32>", "event_id": "<uuid>", "camera_id": "<uuid>", "triggered_at": "2026-04-22T09:15:00Z", "status": "triggered", "message": "Recording triggered successfully", "transport": "tunnel"}| Field | Type | Description |
|---|---|---|
request_id | string | 32-char hex; deduplication key. |
event_id | uuid | New event ID (or the existing ID when extending). |
camera_id | uuid | Echo of the path parameter. |
triggered_at | timestamp | Server-side trigger time in UTC. |
status | enum | triggered or recording_extended. |
message | string | Human-readable status. |
transport | enum | tunnel for gateway mode, direct for direct mode, fallback for the gateway upload path. |
List cameras query parameters
Supported query parameters on GET /cameras.
| Parameter | Type | Description |
|---|---|---|
site_id | uuid | Filter by site. |
status | string | Filter by status enum value. |
type | string | Filter by camera type (onvif, rtsp). |
connection_mode | string | Filter by connection mode (direct, gateway). |
gateway_id | uuid | Filter by gateway. |
cursor | string | Pagination cursor. |
limit | integer | Page size. Default 20, max 100. |
Stream status response
GET /streams/{camera_id}/status:
| Field | Type | Description |
|---|---|---|
streamable | boolean | True when enabled and status == "online". |
webrtc_supported | boolean | True for h264 and h265 codecs. |
codec | string | Current codec or empty string. |
resolution | string | WIDTHxHEIGHT or empty. |
connection_mode | string | direct or gateway. |
go2rtc_status | string | active or inactive. |
active_viewers | integer | Current WebRTC viewer count. |
Related
- REST camera endpoints — developer-persona view with SDK samples
- Camera and recording fields — database schema behind these endpoints
- Roles and permissions — role hierarchy
- Record a manual clip — operator how-to for the trigger endpoint