ProjectX Video
The video plane: sites, zones and cameras, stream ingest, natural-language search over what was seen, and clip playback. Search is a sentence rather than a filter expression, and what comes back says why each result matched and what the caller was not permitted to see — a shortened timeline that does not say it was shortened reads as 'there is no footage of that'. Playback is a short-lived signed URL against the data plane; no endpoint here returns footage.
111 operations · https://api.projectx.dev
Generated from the checked-in OpenAPI document for this service. The document itself is the authority, and it is what a generated client is built from.
access 2
- POST
/v1/video/access/importRequiresvideo:access:importFederate an access-control panel or ANPR reader and import its events.
Connects to an access-control panel — HID, Lenel OnGuard, Genetec Synergis, Gallagher, Nedap, Salto — or an ANPR reader, probes what it is, and writes the events it holds as rows in `video.access_events` tied to the site and zone the reader belongs to, joinable to the footage from the camera watching the same door. The source is asked before anything is written, so one that does not answer is refused as unreachable. The import is per-event: a malformed event is named in `skipped` and the rest are written. No image and no cardholder identity cross this path — the panel holds those; the platform records what happened, where and when. The `endpoint` must not carry credentials; the credential goes in the vault and the request holds a `credential_ref`.
- Body
- ImportAccessRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/iot/importRequiresvideo:access:importIngest IoT sensor readings over MQTT or OPC-UA.
Connects to an MQTT broker or an OPC-UA server, pulls the readings it holds, and writes each as a row in `video.sensor_readings` tied to the site and zone the sensor sits in, joinable to the footage from the camera watching the same place — a spike on a power meter at the moment a van appears is the kind of join this is for. The source is asked before anything is written. No image and no person crosses the path: a reading is a sensor, a value, a unit and a time. The `endpoint` must not carry credentials; the credential goes in the vault and the request holds a `credential_ref`.
- Body
- ImportIotRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
camera 21
- GET
/v1/video/camerasRequiresvideo:camera:listList cameras, optionally filtered by site or zone.
Parameters (3)
site· query — Only cameras at this site.zone· query — Only cameras in this zone.X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/camerasRequiresvideo:camera:registerRegister a camera.
The zone must already exist at the named site. An auto-created zone is a zone with no policy attached, and the camera in it would be governed by whatever the site-wide default happens to be. A `source` with credentials in it — `rtsp://user:pass@host` — is refused. The credential goes in the vault and the record holds a `credential_ref`.
- Body
- RegisterCameraRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/cameras:importRequiresvideo:camera:registerRegister many cameras in one call, from a list or a CSV.
Onboards a batch under a shared `template` — the fields every camera on a site has in common — with each row in `cameras`, or each line of a `csv`, overriding only what differs. The import is per-camera: a row that will not validate (a duplicate id, a zone that does not exist at the site, a `source` with a credential in it, a frame rate that is not a number) is refused whole and named in `skipped` with the reason, and the rows that are good still onboard. Nothing is left half-configured, and the batch is not all-or-nothing — one typo costs one camera, not the import.
- Body
- ImportCamerasRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- DELETE
/v1/video/cameras/{camera}Requiresvideo:camera:retireRetire a camera and deregister it from the catalog.
Deregistration is part of retiring rather than a follow-up. A catalog holding a camera that no longer exists sends whoever finds it looking for footage that was never recorded.
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/cameras/{camera}Requiresvideo:camera:readDescribe a camera, with its derived health.
Health is computed from the last frame seen rather than stored, so it cannot disagree with reality. Never includes `credential_ref`: a vault path in a response is a map to the thing it protects.
Parameters (2)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- PATCH
/v1/video/cameras/{camera}Requiresvideo:camera:configureChange a camera's configuration.
A partial update of what an operator reconfigures after a camera is on: its name, sampling rate, region of interest, placement and optics metadata, and labels, which merge rather than replace. What makes a camera a different camera or a different stream is refused by field — `site`, `zone`, `protocol`, `source` and `credential_ref` — because moving a camera to another zone is a re-registration (the zone is what policy is written against) and changing the source is a new connection. `state` is derived from ingest and is not settable. The changed camera is re-registered in the catalog, so a search reflects the new settings rather than the ones it was born with.
- Body
- UpdateCameraRequest
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/cameras/{camera}/eventsRequiresvideo:camera:view_liveRead the events an ONVIF camera reported.
Profile T analytics and motion, plus tamper and device triggers, pulled rather than pushed so the caller sets the pace. Each event carries its ONVIF topic and simple items — never a frame or a crop, because an event crossing into the control plane carries no pixels (invariant 7).
Parameters (3)
camera· path · requiredlimit· query — Most recent N events. Defaults to 50.X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/cameras/{camera}/ptzRequiresvideo:camera:controlDrive an ONVIF camera's pan, tilt and zoom.
Each axis is normalised to [-1, 1] rather than device units, because every vendor's units differ and a control surface that spoke degrees would be wrong on the next model. The device maps the command onto whatever it wants. Refused with 412 if the camera is not an ONVIF camera.
- Body
- PtzMoveRequest
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/cameras/{camera}/recordingsRequiresvideo:camera:view_recordedList what an ONVIF device recorded to its own storage.
Profile G. Bounds and a count, not the footage — the recording that never reached the platform because a link was down still exists on the device, and this is how an investigator finds it. Replay of the footage itself is a separate, signed act.
Parameters (4)
camera· path · requiredfrom· query — ISO-8601 start of the window.to· query — ISO-8601 end of the window.X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/cameras/{camera}/stream-keyRequiresvideo:camera:configureMint (or rotate) a push camera's stream key and push URL.
A push camera (RTMP, SRT, WebRTC, HLS) publishes to a URL the platform hands it, authenticated by a per-camera key. This mints that key and derives the push URL from the hosted ingest gateway, returning both — the stream key appears here exactly once and never in a camera read, because it is a secret stored in the vault like a password. Calling it again rotates: a new key is minted and the stored one overwritten, so the previous key stops authenticating. Rotation is the revoke. Refused for a pull camera (RTSP, ONVIF, a VMS, synthetic), which the platform reaches rather than being published to and so has no key to mint.
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/cameras/{camera}/superviseRequiresvideo:camera:readThe continuous-ingest status of one camera.
Reports whether the camera is under continuous ingest and what the last sweep observed — `pending` before the first sweep, `running` when a burst delivered frames, `reconnecting` when one delivered none and the restart policy is still trying, `failed` once it has given up, `stopped` when nobody is supervising it. A camera never supervised reads as `stopped` rather than a 404.
Parameters (2)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/config-templatesRequiresvideo:camera:readList this tenant's configuration templates.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/config-templatesRequiresvideo:camera:configureCreate a configuration template targeting a camera group.
A template is a named set of settings and the group they apply to, so a fleet is configured a group at a time rather than a camera at a time. The group must already exist. Only genuine configuration is settable — the sampling rate and labels — not device facts like model or firmware.
- Body
- CreateConfigTemplateRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- DELETE
/v1/video/config-templates/{template}Requiresvideo:camera:configureDelete a configuration template.
Parameters (3)
template· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/config-templates/{template}/applyRequiresvideo:camera:configureApply a template to the cameras its group resolves to now.
Applied live against the current membership, so a rule group picks up a camera added since the template was written. Each changed camera is re-registered in the catalog, so a search reflects the new settings rather than the ones it was born with.
Parameters (3)
template· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/credentialsRequiresvideo:camera:store_credentialStore a camera credential in the vault and return its reference.
The write that makes a camera's credential a reference rather than a secret. A camera record holds a `credential_ref`, never a password, so the password is put on file here first — a username and password joined as `username:password`, or a bare `secret` for a bearer token or a SIP password — and the reference it returns is set on the camera at registration. Write-only: the response carries the reference and never the secret, because a vault path in a response is a map to the thing it protects. The reference is tenant-scoped, so it resolves to the secret only inside the tenant that stored it.
- Body
- StoreCameraCredentialRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/fleet/firmwareRequiresvideo:camera:readThe fleet's firmware inventory, with advisory-flagged builds.
Groups the fleet by model and firmware — one row per build, with the cameras on it — and flags a build a security advisory names. Answered from the registry, not the devices, so a camera offline for the very fault an advisory describes is still counted. A build with no model or firmware recorded is reported as `unknown`: an inventory gap made visible rather than left silently absent.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/onvif/devicesRequiresvideo:camera:registerProbe an ONVIF device and register a camera for it.
The device is asked what it is and what it supports before anything is written. A device that does not answer is refused as unreachable, and one that cannot stream — no ONVIF Profile S — is refused rather than registered as a camera that never produces a frame. Its model, serial and supported profiles are recorded on the camera's labels. The `endpoint` is the device's ONVIF service URL and must not carry credentials; the credential goes in the vault and the record holds a `credential_ref`, exactly as for a bare RTSP camera.
- Body
- AttachOnvifDeviceRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/onvif/discoverRequiresvideo:camera:discoverFind ONVIF devices on the local network by WS-Discovery.
Multicasts a probe and returns every device that answered, with its ONVIF scopes decoded to a name, hardware and location and the URL its API is at. The scan is a network fact and carries no tenant; each device is marked `registered` when this tenant already has a camera pointed at its endpoint, so an onboarding view shows what is new to onboard and does not offer a duplicate. Onboarding a discovered device is `POST /v1/video/onvif/devices` with its endpoint.
Parameters (3)
timeout_ms· query — How long to listen. Defaults to 3000ms.X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/supervisionRequiresvideo:camera:readList this tenant's cameras under continuous ingest.
Every camera this tenant has placed under continuous ingest, with its last observed status, frame count and run time — the fleet view of which cameras are supervised and which of those are actually delivering. The sweep that drives them runs on the operations runtime, out of this service.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/vms/importRequiresvideo:camera:registerFederate a VMS and register the cameras it manages.
Connects to a Video Management System — Milestone, Genetec, an Axis Camera Station — probes what it is, and registers each enabled camera it manages as a `vms` camera pointed back at the server, which resolves the live stream through the vendor's own gateway at ingest time. The server is asked before anything is written, so one that does not answer is refused as unreachable rather than leaving a fleet that never streams. The import is per-camera: a disabled channel, or one whose registration is refused, is named in `skipped` and nothing is left half-configured. The `endpoint` must not carry credentials; the credential goes in the vault and the record holds a `credential_ref`.
- Body
- ImportVmsRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
clip 3
- GET
/v1/video/cameras/{camera}/clipsRequiresvideo:clip:listList the clips stored for a camera.
Read from `video.events`, so a clip appears here only if the event that produced it does — the timeline and the footage cannot disagree about what exists.
Parameters (3)
camera· path · requiredlimit· query — Most recent N. Defaults to 50.X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/cameras/{camera}/clips/{clip}Requiresvideo:clip:listDescribe one clip: window, size, and how it is encrypted.
Metadata only, and useful without a playback grant: establishing that a clip exists and is intact does not require watching it, so it does not require minting a capability.
Parameters (3)
camera· path · requiredclip· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/cameras/{camera}/clips/{clip}/playbackRequiresvideo:clip:playIssue a short-lived, signed playback URL.
The control plane returns a grant, never the bytes (invariant 7). The URLs are signed by the object store and expire; a clip stored in more than one segment comes back as `parts`, in order, all expiring together. Refused with 412 if the stored segment is not encrypted at rest, unless the process was started with `VIDEO_ALLOW_UNENCRYPTED_CLIPS=1`.
- Body
- PlaybackRequest
Parameters (4)
camera· path · requiredclip· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
connector 5
- GET
/v1/video/connectorsRequiresvideo:connector:readList this tenant's camera connectors.
The tenant's own connectors and their enrolment state — pending, enrolled or revoked — each with the fingerprint of its token and, once enrolled, the node it stood up. Never a token: a connector read carries the fingerprint, not the secret it is a prefix of.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/connectorsRequiresvideo:connector:enrolMint a one-time enrolment token for a customer connector.
A connector is the agent a customer runs to hold an outbound tunnel back to the platform, so cameras on a private network are reachable without opening a port — the existing edge-node model with `customer` ownership, except the tenant stands it up itself. This mints a one-time token and returns it with the install snippets to run the connector by Docker, Windows installer or Raspberry Pi image. The token appears here exactly once and never in a connector read, because it is a secret: a token in a response is a map to the thing it protects — whoever holds it can enrol a node as this tenant — so what a later list or get carries is only a short fingerprint. Tenant-scoped: a tenant mints, lists and enrols only its own connectors.
- Body
- MintConnectorRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/connectors/{connector}Requiresvideo:connector:readOne connector and its enrolment state.
Tenant-scoped: a connector that is not this tenant's is a 404, not a 403, because a 403 would confirm it exists for somebody else.
Parameters (2)
connector· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/connectors/{connector}/revokeRequiresvideo:connector:revokeRevoke a connector enrolment and invalidate its token.
Tears a connector down: it moves to revoked and a still-pending token enrols nothing further. A node already enrolled is left running — revoke is about the connector record and its token, not about retiring an appliance already serving the tenant's cameras. Returns the revoked connector so the console reflects the new state.
Parameters (3)
connector· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/connectors/enrolRequiresvideo:connector:enrolPresent a connector's token to enrol it and register its node.
The connector agent calls this with its one-time token in production; the token is matched by hash against the tenant's pending enrolments and, on a match, the backing edge node is registered with `customer` ownership under this tenant and the connector moves to enrolled. The response carries the enrolled connector and the registered node. A token that matches none — garbage, already used, revoked, or minted under another tenant — is refused with a generic sentence that names no connector, so the refusal discloses nothing about whose token it might have been.
- Body
- EnrolConnectorRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
fleet 46
- GET
/v1/video/alert-rulesRequiresvideo:fleet:alertsList the tenant's standing alert rules.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/alert-rulesRequiresvideo:fleet:alertsCreate a standing alert rule.
A saved search made standing, or a zone/line/threshold/dwell/absence/anomaly rule. Stores the query text, not a compiled plan, so the rule follows the estate. The rule names the notification channels its alerts fan out to.
- Body
- AlertRuleRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/alert-rules/{rule}/test-fireRequiresvideo:fleet:alertsFan a synthetic alert to a rule's channels without firing it.
The test-fire button behind the rule builder. It runs the same fan-out a live verdict runs, over the rule's own channels, and returns the dispatches — which channels, with what severity. Nothing is persisted and no transport is touched: a test fire that wrote an alert would pollute the queue and skew the analytics, and one that actually paged would make 'test' the most dangerous button on the page. The wire stays the operations runtime's, exactly as a live fan-out's does.
Parameters (3)
rule· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/alertsRequiresvideo:fleet:alertsThe alert queue, newest first.
Every alert and its lifecycle state. One severity scale across all pillars, so a camera alert is triaged against an identity or cloud one in the same queue.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/alerts:analyticsRequiresvideo:fleet:alertsAlert analytics: volume, precision, response time and camera noise.
The four figures a review asks of the alert stream. The per-camera noise ranking names the camera to tune, closing the loop with the feedback that quietens it.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/alerts/{alert}/acknowledgeRequiresvideo:fleet:alertsAcknowledge an alert.
Someone has seen it — the step escalation waits for. The order is the accountability: an alert cannot be resolved before it is acknowledged.
Parameters (3)
alert· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/alerts/{alert}/assignRequiresvideo:fleet:alertsAssign an acknowledged alert to an owner.
- Body
- AssignAlertRequest
Parameters (3)
alert· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/alerts/{alert}/resolveRequiresvideo:fleet:alertsResolve an alert.
Terminal — a recurrence is a new alert, so the count of how often a thing happened stays honest.
Parameters (3)
alert· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/cameras/{camera}/audio-sessionRequiresvideo:fleet:liveOpen a two-way audio session where the camera supports it.
Listen to a scene and talk back through a camera with a speaker and microphone. Refused on a camera that does not advertise the capability. Returns a grant the media plane honours — no audio crosses the control plane (invariant 7).
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/cameras/{camera}/mask-regionsRequiresvideo:fleet:governanceThe regions masked on a camera right now, decided server-side.
Returns the boxes the pixel path must burn in for this moment and operator role, and the configured regions behind them. The masking decision is the server's; there is no response that hands out an unmasked region on trust.
Parameters (3)
camera· path · requiredrole· query — The viewing operator's role, for conditional lifts.X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/cameras/{camera}/mask-regionsRequiresvideo:fleet:governanceDefine a privacy mask region on a camera.
A region the camera can see and must not show — a neighbour's window, a keyboard, a treatment room. Masked by default; a permanent mask never lifts, a scheduled one is visible only in its window, a conditional one lifts only for an approved role. Masking is enforced server-side over the pixels, never a flag the client is trusted to honour.
- Body
- SetMaskRegionRequest
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/cameras/{camera}/ptz/presetsRequiresvideo:fleet:liveThe saved PTZ presets for a camera.
Parameters (2)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/cameras/{camera}/ptz/presetsRequiresvideo:fleet:liveSave a named pan/tilt/zoom position for a camera.
A preset recalls a lens position by name — 'the gate', 'the till' — instead of three normalised floats. Upserted by name, so a re-mounted lens re-saves rather than duplicates.
- Body
- SavePtzPresetRequest
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/cameras/{camera}/ptz/toursRequiresvideo:fleet:livePlan a PTZ tour: an ordered walk of presets with a dwell.
A camera on no operator's screen steps gate to yard to bay and back on a dwell, so one lens covers what would need several. Returns the cycle it will walk; the runtime drives the moves.
- Body
- PlanPtzTourRequest
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/casesRequiresvideo:fleet:casesList this tenant's cases, newest first.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/casesRequiresvideo:fleet:casesOpen an investigation case.
From an alert, a search result or by hand. The origin is kept because 'why does this case exist' is the first question a review asks.
- Body
- CreateCaseRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/cases:analyticsRequiresvideo:fleet:casesCase analytics: time to resolution and volume by origin and site.
The figures a supervisor asks of the caseload, not one case. Time to resolution averages over closed cases; volume counts a case once per site it touches.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/cases/{case}Requiresvideo:fleet:casesDescribe one case.
Parameters (2)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/cases/{case}/commentsRequiresvideo:fleet:casesList a case's collaboration records, oldest first.
Parameters (2)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/cases/{case}/commentsRequiresvideo:fleet:casesAdd a comment, mention, handover or cross-case link.
The case's collaboration record, append-only like the custody chain — a note somebody could quietly edit is a note a review cannot trust.
- Body
- CaseCommentRequest
Parameters (3)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/cases/{case}/custodyRequiresvideo:fleet:casesThe chain of custody for a case, oldest first.
Who viewed, exported, attached or redacted the evidence, when, and why. Append-only: a chain an entry can be removed from is not a chain.
Parameters (2)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/cases/{case}/evidenceRequiresvideo:fleet:casesList the evidence attached to a case.
Parameters (2)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/cases/{case}/evidenceRequiresvideo:fleet:casesAttach an evidence item to a case.
A reference and its provenance, never the bytes (invariant 7). Attaching writes a chain-of-custody entry, so the record of who added what is a side effect of doing it.
- Body
- AttachEvidenceRequest
Parameters (3)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/cases/{case}/exportRequiresvideo:fleet:casesBuild a tamper-evident export package.
Redaction of face, plate and region is on by default and enforced server-side; lifting a target needs an attributed override with a reason and a named authorizer, refused without both. The manifest hashes each item and the package over those hashes, so altering a byte fails verification. Exporting writes a custody entry. Every item is in the package: a vaulted one is attested by its content hash (`attests: content`), a note or an unvaulted reference by a hash of its evidence record (`attests: record`).
- Body
- ExportCaseRequest
Parameters (3)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/cases/{case}/holdsRequiresvideo:fleet:casesPlace a legal hold that beats every deletion path.
Retention says footage is deleted after its window; a legal hold says not this, not yet, whatever the window. Lifted deliberately, never on a timer.
- Body
- PlaceHoldRequest
Parameters (3)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/cases/{case}/timelineRequiresvideo:fleet:casesReconstruct the case's cross-camera timeline.
The case's evidence merged into one ordered account across every camera and site. Reading it writes a custody entry: a review of who saw the account is part of the account.
Parameters (2)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/cases/{case}/transitionRequiresvideo:fleet:casesMove a case through open, investigating and closed.
Closed is terminal — a case that needs reopening is a new case linked to the old, so the record of how long the first took to close stays honest.
- Body
- TransitionCaseRequest
Parameters (3)
case· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/detectionsRequiresvideo:fleet:detectionsList raw detection rows, filtered by camera or class.
The rows the ranked search never scans: one per object per sampled frame, straight from `video.detections`. This is the verification read — what the detector actually said about a camera — and the surface a custom class is proven on: a registered class arrives as the row's own `class`, conditioned at detection time rather than re-labelled at read time. Filters go into the store scan rather than over its output, so a bounded page never claims the store held less than it did. The rows carry boxes and labels, which are personal data, so this is its own sensitive permission rather than part of reading a camera's configuration.
Parameters (4)
camera· query — Only detections from this camera.class· query — Only detections of this class — a coarse class or a registered custom class.limit· query — Page size: default 50, at most 500.X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/governance/access-windowsRequiresvideo:fleet:governanceSet an operator's time-window access to their shift hours.
Scope says where an operator may look; this says when. A view outside the window is refused server-side. A window that crosses midnight is the ordinary night shift, not a special case.
- Body
- SetAccessWindowRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/governance/anonymized-analyticsRequiresvideo:fleet:governanceCounts and a density heatmap, with no identifiable imagery.
Answers 'how many, where and when' without answering 'who'. Aggregates by class, zone and hour and a coarse density grid — no label, identity, track or per-object box crosses into the output, so the mode is anonymized rather than identified with the name hidden.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/governance/deletion-certificatesRequiresvideo:fleet:governanceThe deletion-certificate stream, newest first.
The attestable proof, kept after the footage is gone, that each deletion happened per policy — what a regulator asking 'you said this was gone, show me' is shown.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/governance/dpia-packRequiresvideo:fleet:governanceThe exportable DPIA support pack: the governance posture assembled.
What a data-protection officer hands a regulator: retention rules, mask regions, access windows, audit-stream coverage and which responsible-use boundaries are enforced — the posture already in force, made legible in one document.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/governance/export-auditRequiresvideo:fleet:governanceThe separately reportable export-audit stream, newest first.
Every export of evidence and its justification. Taking footage out of the platform is the most consequential access there is, and an export without a stated reason is refused, not logged as unexplained.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/governance/face-match-auditRequiresvideo:fleet:governanceThe separately reportable face-recognition match-audit stream.
Every face-recognition match, reportable on its own terms: a regulator can pull the biometric log — who was searched for by their face, by whom, under which licence — without the rest. The misses are recorded too, being as accountable as the hits.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/governance/retention/enforceRequiresvideo:fleet:governanceDelete footage past its retention window and certify each deletion.
For each candidate, retention decides — unless a legal hold covers it, which beats retention absolutely, so held footage is kept whatever its age. Every deletion gets a tamper-evident certificate; every kept item a reason. The certificate proves the deletion happened on schedule, by the rule, after the footage itself is gone.
- Body
- EnforceRetentionRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/governance/view-auditRequiresvideo:fleet:governanceThe separately reportable view-audit stream, newest first.
Every view of footage, in addition to the audit chain: the chain answers whether it was permitted, this answers how often footage was viewed, by whom and why — the aggregation a data-protection review asks for.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/heatmapRequiresvideo:fleet:liveA coarse detection-density heatmap, whole fleet or one camera.
The same privacy-safe, coarse-celled density grid the anonymized analytics produces, so a heatmap can never be zoomed into a tracker. Filtered to one camera when named.
Parameters (2)
camera· query — Restrict the heatmap to one camera.X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/mapRequiresvideo:fleet:liveThe map and floorplan view: camera positions, status and alert overlays.
Every camera's placement, the orientation cone it looks through, its derived health and whether it is alerting — the data a floorplan or map draws. Positions and status, never a frame (invariant 7).
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/saved-searchesRequiresvideo:fleet:searchList the saved searches this operator may see.
Their own, plus any another operator shared. A search kept for a named individual is as sensitive as running it, so the default is private and sharing is the deliberate act.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/saved-searchesRequiresvideo:fleet:searchSave a search by name.
Stores the query text, not a compiled plan, so the saved search follows the estate and is filtered by whoever runs it rather than whoever saved it. A saved search is the same grammar an alert rule is built from: keeping one is the first half of raising an alert on it.
- Body
- SaveSearchRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/searchRequiresvideo:fleet:searchSearch footage in natural language.
Returns a ranked timeline where each entry carries its match explanation: which terms matched, which did not, the dense and structured components of the score, and how many detections are behind it. `plan.excluded` names every restriction that narrowed the search and the policy behind it. A shortened result that does not say it was shortened is a wrong answer, and here the wrong answer is “there is no footage of that”.
- Body
- SearchRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/search-historyRequiresvideo:fleet:searchThis operator's own search history.
What this operator searched for, newest first, read from the same `video.searches` table the audit review aggregates. An operator sees their own history; who searched for whom across the tenant is a governance read, not this one.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/search:planRequiresvideo:fleet:searchCompile the search and return the plan without running it.
Same authorization, same obligations, no retrieval and no meter. What an operator uses to find out which cameras a policy will exclude before searching.
- Body
- SearchRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/wallRequiresvideo:fleet:liveThis operator's saved walls, one per monitor.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/wallRequiresvideo:fleet:liveSave this operator's video wall for one monitor.
One to sixty-four camera tiles, in position order. Keyed by operator and monitor, so a multi-screen desk keeps a separate arrangement per screen and a rearrangement replaces rather than accumulates.
- Body
- SaveWallLayoutRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/wall/resolveRequiresvideo:fleet:liveResolve the wall to show now, auto-focused and viewport-clamped.
The saved layout with the alerting camera promoted to the primary tile (auto-focus), clamped to a mobile viewport when asked. Auto-focus is resolved on read, not stored — which camera is alerting changes moment to moment.
Parameters (3)
monitor· query — Which monitor's wall. Defaults to primary.mobile· query — Set true to clamp to a phone-sized layout.X-Request-Id· header — Echoed on the response and recorded on the audit event.
group 4
- GET
/v1/video/groupsRequiresvideo:group:readList this tenant's camera groups.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/groupsRequiresvideo:group:createCreate a camera group, static or rule-based.
A group is what a permission and a search scope are written against — 'who may look at the loading docks' names the group, not each camera. A `static` group lists cameras by id, and each must already exist, because a permission group with a typo in it is a hole nobody sees. A `rule` group matches camera attributes (site, zone, protocol, labels), so a camera added later that matches is in the group without re-editing it; an empty rule is refused rather than treated as match-all, because a group meant for one dock would otherwise grant the whole estate.
- Body
- CreateCameraGroupRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- DELETE
/v1/video/groups/{group}Requiresvideo:group:deleteDelete a camera group and deregister it from the catalog.
Parameters (3)
group· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/groups/{group}Requiresvideo:group:readDescribe a group with the cameras it currently resolves to.
The membership is computed live rather than stored: a rule group reflects a camera registered after it, and a static group drops one retired since — a cached membership is the stale scope a search silently trusts.
Parameters (2)
group· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
integration 6
- GET
/v1/video/integrationsRequiresvideo:integration:readList this tenant's persistent integrations.
Each integration with its kind, where it points, and the outcome of its last sync — the status, when it last ran and the imported and skipped counts, or the error if the last sync was refused. Never the credential reference: a read carries where it dials, not how it authenticates.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/integrationsRequiresvideo:integration:manageRecord a persistent integration over a federation.
The one-shot imports — VMS, access control, ANPR, IoT — connect once and leave nothing to manage: the operator who wants to re-run one remembers the body they typed. This records the parameters of a federation as a persistent integration so it can be listed, edited, re-synced on demand and removed, with the outcome of the last sync visible as status. A create records the integration in the `configured` state and does not sync — configuring an integration and running it are two acts, so a source that happens to be down when the record is made does not make the record a failure. The `endpoint` must not carry credentials; the credential goes in the vault and the record holds a `credential_ref`, which no read returns. Tenant-scoped: a tenant creates, lists, syncs and removes only its own.
- Body
- CreateIntegrationRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- DELETE
/v1/video/integrations/{integration}Requiresvideo:integration:manageStop managing a persistent integration.
Removes the integration record. This stops managing the sync; it does not tear down the estate — the cameras a VMS integration imported and the rows an access or sensor import wrote are left in place, exactly as revoking a connector leaves a node it already enrolled running. Retiring a camera is a separate act.
Parameters (3)
integration· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/integrations/{integration}Requiresvideo:integration:readOne integration and the outcome of its last sync.
Tenant-scoped: an integration that is not this tenant's is a 404, not a 403, because a 403 would confirm it exists for somebody else.
Parameters (2)
integration· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- PUT
/v1/video/integrations/{integration}Requiresvideo:integration:manageEdit a persistent integration's parameters.
A partial update of the federation's parameters — name, endpoint, vendor/protocol, credential, site, zone, prefix. The `kind` is fixed once chosen and the status and last-sync fields are the service's, set by a sync and never by a write, so an edit cannot declare an integration synced it never ran. Takes effect on the next re-sync.
- Body
- UpdateIntegrationRequest
Parameters (3)
integration· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/integrations/{integration}/resyncRequiresvideo:integration:manageRe-run an integration's federation now.
Dispatches to the import the integration's kind names with its stored parameters: a `vms` integration registers the cameras its server manages, `access` and `anpr` write the events their source holds, `iot` writes the readings its broker reports. On success the integration moves to `synced`, stamps the moment and records the imported and skipped counts. A refusal — an unreachable server, a zone that no longer exists — moves the integration to `failed` and records the reason on the row, and the call returns 200 with that state rather than a 500: a federation that cannot reach its source is a status an operator reads and retries, not an error the console has to reconstruct from a log.
Parameters (3)
integration· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
node 13
- GET
/v1/video/fleet/overviewRequiresvideo:node:readPer-tenant camera fleet overview — counts, recording and dark.
A platform sweep over every tenant's cameras, the same staff posture as the node read: for each tenant, how many cameras it has, how many are recording (healthy) and how many are dark (not recording). Counts and the tenant key only — no camera id, source, credential, clip or frame — so an operator reads fleet health across the estate and never any footage (invariant 7).
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/node-enrolmentsRequiresvideo:node:readList node enrolments — pending and enrolled.
Each enrolment with its state — pending or enrolled — the fingerprint of its token, the hardware/site/channel hints it carries, and, once enrolled, the node it stood up. Never a token: an enrolment read carries the fingerprint, not the secret it is a prefix of.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/node-enrolmentsRequiresvideo:node:manageMint a one-time enrolment token for a new edge node.
The staff analog of a customer connector's token: an operator mints a one-time token and hands it to a new appliance, which presents it on first contact to register itself. This returns the token with the first-boot enrol snippet, and carries the hardware, site and channel the operator typed onto the node the enrolment will stand up. The token appears here exactly once and never in an enrolment read, because it is a secret: a token in a response is a map to the thing it protects — whoever holds it can enrol a node as the platform — so what a later list carries is only a short fingerprint. Staff-only: enrolling a platform node is a platform act.
- Body
- MintNodeEnrolmentRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/node-enrolments/enrolRequiresvideo:node:managePresent an enrolment token to register a new platform node.
The appliance calls this with its one-time token in production; the token is matched by hash against the pending enrolments and, on a match, the backing edge node is registered with `projectx` ownership and no tenant — the shape a staff-registered platform node has always had — carrying the enrolment's hardware, site and channel hints. The response carries the enrolment and the registered node. A token that matches none — garbage or already used — is refused with a generic sentence that names no enrolment, so the refusal discloses nothing.
- Body
- EnrolNodeRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/nodesRequiresvideo:node:readList the edge fleet — every node, its health and buffer.
Edge nodes are the on-site appliances the video runtime runs on, platform infrastructure that spans tenants rather than a tenant's resource, so this is a staff read over the whole fleet. Each node carries its hardware, agent version, release channel and last telemetry, plus the two judgements derived on read from the clock: whether it is online, how long since it was in contact, and — for one that is offline and buffering — an estimate of when its disk runs out. No footage: a node's buffer is a count and a byte total.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/nodesRequiresvideo:node:manageRegister an edge node in the fleet.
Records a node's identity, ownership (ours or a customer's own hardware), site, hardware and the channel it tracks. Staff-only: onboarding a node is a platform act.
- Body
- RegisterNodeRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- DELETE
/v1/video/nodes/{node}Requiresvideo:node:manageDeregister a node, removing it from the fleet inventory.
The operator's teardown of an appliance being decommissioned or replaced: the node drops out of the one fleet an operator reads whole. Staff-only, the mirror of retiring a camera. A node that was tenant-stamped — one a customer enrolled through a connector — is simply removed from inventory; deregister is about the fleet record, not the tunnel the connector holds.
Parameters (3)
node· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/nodes/{node}Requiresvideo:node:readOne node with its health, buffer and derived liveness.
Parameters (2)
node· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/nodes/{node}/diagnosticsRequiresvideo:node:diagnoseRun a remote diagnostic against a node, under break-glass.
Triage a misbehaving node from the console rather than a truck roll: pull its logs, test its camera streams, test inference. Requires a break-glass grant, because a diagnostic reaches into a node. The result is text and numbers — a stream test reports a frame rate, never a frame; the control plane has no route that returns footage (invariant 7), and a diagnostic is not an exception to it.
- Body
- RunDiagnosticsRequest
Parameters (3)
node· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/nodes/{node}/heartbeatRequiresvideo:node:manageRecord a node's heartbeat and latest telemetry.
The contact that keeps a node online, carrying its latest health and buffer depth. Driven by the node's own agent in production; here it is the write that moves a node from offline back to in-contact.
- Body
- NodeHeartbeatRequest
Parameters (3)
node· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/rolloutsRequiresvideo:node:readThe fleet's model rollouts — channel, stage, fraction and known-good.
Each rollout is a candidate model version widening through its canary ladder on a channel (stable, beta, canary). The row shows where it is: the stage, the fraction of the fleet on the candidate, and the version a node reverts to — the previous one throughout the canary, the candidate only once it is fully promoted. The fleet controller widens or auto-reverts it on node health; an operator can revert it by hand.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/rolloutsRequiresvideo:node:manageStart a canary rollout of a model version.
Begins the candidate on the first, smallest stage of the ladder. The fleet controller observes node health and widens it or reverts it automatically; this only starts it. Staff-only.
- Body
- StartRolloutRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/rollouts/{rollout}/rollbackRequiresvideo:node:manageRoll a rollout back by hand.
A deliberate revert, distinct from the automatic rollback the fleet controller makes on a health signal — for a candidate that passes its checks and is wrong anyway. Reverts to the previous version; a rollout already finished cannot be rolled back again.
Parameters (3)
rollout· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
retention 3
- GET
/v1/video/cameras/{camera}/retentionRequiresvideo:retention:readThe retention that applies to one camera, and which rule decided it.
Resolves the three scopes to a single answer and names its source, so an operator sees not just '30 days' but which rule to change to alter it.
Parameters (2)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.
- GET
/v1/video/retentionRequiresvideo:retention:readList this tenant's retention rules.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- PUT
/v1/video/retentionRequiresvideo:retention:configureSet a retention rule for a camera, zone or classification.
How long footage is kept, at one of three scopes. Most specific wins: a camera rule overrides its zone's, which overrides its classification's, which overrides the platform default. A rule may be set before the camera or zone it names exists — a zone's retention is configured before its cameras are onboarded. This sets the number; the purge that enforces it is retention enforcement in the governance stream.
- Body
- SetRetentionRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
site 2
- GET
/v1/video/sitesRequiresvideo:site:listList this tenant's sites.
Parameters (1)
X-Request-Id· header — Echoed on the response and recorded on the audit event.
- POST
/v1/video/sitesRequiresvideo:site:createCreate a site.
A site is the unit residency is decided at, so `country` is required and is what the PDP evaluates a residency policy against — not where the API call came from.
- Body
- CreateSiteRequest
Parameters (2)
X-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
stream 4
- POST
/v1/video/cameras/{camera}/streamsRequiresvideo:stream:ingestRead a bounded run of frames and write what was seen.
Returns the counts, including `max_write_latency_seconds` — wall-clock from a frame arriving to its detections being accepted by the row store, which is the thirty-second criterion measured rather than asserted. Bounded per call on purpose. Continuous ingest arrives in Phase 13 on the operations runtime, where a long-running reader has a supervisor and a restart policy.
- Body
- IngestStreamRequest
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- DELETE
/v1/video/cameras/{camera}/superviseRequiresvideo:stream:ingestStop continuous ingest of a camera.
Clears the desire to ingest and returns the record marked `stopped`. The next sweep skips the camera. Idempotent: a camera that was never supervised comes back stopped all the same, so a caller need not tell 'stopped it' apart from 'it was already stopped'.
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/cameras/{camera}/superviseRequiresvideo:stream:ingestStart continuous, supervised ingest of a camera.
Marks a camera as one that should be continuously ingested, and returns its supervision record. This sets desired state only — the long-running reader runs on the operations sweep runtime, not in this request, where it has a supervisor and a restart policy that reconnects on a transient failure and gives up on one that would fail the same way forever. Idempotent: supervising an already-running camera returns it unchanged. Authorized as `video:stream:ingest` because starting continuous ingest is an ingest act and carries the same cost as a bounded run. An optional restart policy applies the service-wide failure ceiling.
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- POST
/v1/video/cameras/{camera}/test-streamRequiresvideo:stream:ingestA bounded, unmetered reachability check for a camera.
Reads a handful of frames and reports whether any arrived, with the frames seen, the write latency and the last frame's timestamp — the test-stream button behind onboarding a camera, so an operator sees the source is reachable before trusting it. Unmetered on purpose: unlike `/streams`, which bills the frames and the camera-hours of a real ingest, a reachability pre-check emits no meter — charging for the honest check would make the honest thing the expensive one. `ok` is false for a source no frame reached, which also leaves the camera `unreachable`, the same outcome a zero-frame ingest records.
Parameters (3)
camera· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
zone 2
- POST
/v1/video/sites/{site}/zonesRequiresvideo:zone:createCreate a zone within a site.
Zones are what most policy is written against, because “who may look at the server room” is a sentence somebody actually says.
- Body
- CreateZoneRequest
Parameters (3)
site· path · requiredX-Request-Id· header — Echoed on the response and recorded on the audit event.Idempotency-Key· header — Makes a retry safe. A replay of the same key and body returns the stored response with Idempotency-Replayed: true; the same key with a different body is a 409.
- GET
/v1/video/zonesRequiresvideo:zone:listList zones, optionally within one site.
Parameters (2)
site· query — Only zones at this site.X-Request-Id· header — Echoed on the response and recorded on the audit event.