Skip to content

Keys and scopes

The key

A key is one person in one space: it acts as the person who created it, only in that space, with a role ceiling and only with the scopes it was given. It never adds permissions: permissions, groups, rate limits and auditing are the same as in the panel, and they are checked on every request.

  • Format: tv_live_<prefix>_<secret>. The prefix (12 characters) identifies the key and is visible in the panel; the secret (48 characters) is compared in constant time. Only a hash of the secret is stored.
  • Where it goes: only in Authorization: Bearer. In the URL (?api_key=, ?token=…) it is rejected with 400 key_in_query, because URLs end up in every proxy's logs. If you already sent it that way, rotate it.
  • Where it never goes: a browser, or an app someone can open up. To show video on a web page, your server requests the session and passes only the view token to the browser (see Guides).

The role

You pick a maximum role when creating the key (admin, member or view only), and the key gets the lower of that and the role its creator has today. If the creator is downgraded, the key goes down with them.

Role What it can do
View only See the cameras in its groups. Cannot move, talk or take control.
Member See and operate the cameras in its groups (move, snapshots, recording, talk, take control).
Admin All cameras, configuring them, the bucket, webhooks and automations.

Scopes

Without the scope, the route answers 403 missing_scope (with required_scope) even if the role would allow it.

Scope Allows
cameras:read Listing cameras, their details and status, and command results
cameras:write Provisioning, decommissioning and rotating credentials (also requires admin)
ptz Moving, stopping, home and presets (saving and deleting presets requires admin)
live Opening live video (WebRTC and HLS)
talk Talking through the camera
snapshots Requesting and viewing snapshots
recordings Recordings, and turning recording on or off (changing its settings requires admin)
storage The space's bucket (admin)
events Camera events
webhooks Outgoing webhooks (admin)
control Taking control of a camera for a few minutes (control lease)
automations:read Viewing automations and their runs (admin)
automations:write Turning them on and off (admin)
automations:run Running the ones that have a Manual trigger (admin)

Grant only what the integration needs: a key that only watches video needs neither ptz nor control.

When it stops working

On the very next request:

If this happens Response
It is revoked, from the panel or the app 401 key_revoked
It expired 401 key_expired
Its creator leaves the space (or is removed) 401 key_revoked, and it stays revoked even if they rejoin
Its creator is suspended 401 creator_suspended; it works again once they are reactivated
The space is suspended 403 space_suspended

A lost key cannot be recovered: create a new one and revoke the old one. A revoked key immediately gives up control of any camera it was holding.

What gets recorded

Every request that changes something writes an api.call row to the hash-chained audit log, with the key id and actor: api:<key name>. The same goes for every row the panel writes during that request (watching video, downloading a file, changing settings), every command sent to the camera (actor in GET /cameras/{id}/commands/{command}) and every camera event caused by the request. Reads write at most one api.read row per key and route every 5 minutes.

Every response carries X-Request-Id: use it to find the request in the audit log.