Verkada Command
Verkada Command cloud REST API — surfaces the camera and access-door rosters for a Verkada org, polls door lock state, follows the events feed for access activity, and offers remote admin-unlock for doors plus on-demand HLS and thumbnail links for cameras.
One GEM device row = one Verkada org (one API key). One zone per Verkada device you want to surface — a door, a camera, or both.
Prerequisites
- Verkada Command admin access for the org you want to integrate.
- An API key minted in Admin → API with the scopes you intend to use:
Cameras: Read— forget_cameras,get_camera_link,get_thumbnail.Access: Read— forget_doors, door state polling, the events feed.Access: Write— forunlock(admin pulse) andlock.
- Outbound HTTPS to
api.verkada.com(or your tenant's API base for GovCloud installs). No inbound firewall changes are required.
Setup
- Mint an API key. In Command go to Admin → API → "+ Create API Key". Choose only the scopes you need. Copy the key immediately — Verkada does not show it again.
- Create the GEM device. Go to System → Devices (Open Devices) and click Add Device. Choose the Verkada Command driver, then paste the key into the Verkada API Key field. The device-level Subsystem picker only matters when a driver auto-creates a zone for the device — this driver does not, so you can leave it; you choose a subsystem per zone in step 5.
- Save and reload. On save the driver smoke-tests both rosters against the key. The device shows Connected on the Devices grid as soon as the key validates against at least one roster (cameras or access). If the server log notes
(no access scope)or(no camera scope), the key is missing that scope — add it to the key in Command; no GEM change is needed. - Discover the rosters. Open the device and, from its Commands tab, run
get_doorsandget_cameras.get_doorsreturns each door's UUID, name, site, online flag, and lock state;get_camerasreturns each camera's 16-charcamera_id, name, model, and site. Note the id of each door or camera you want to surface — that is the zone address in the next step. - Create one zone per door or camera you want to surface. The driver does not create or discover zones for you — running
get_doors/get_camerasonly lists what the key can see; it does not add anything to GEM. For each one, add a zone (see Zones) bound to this device and set the zone Address:- Access door — Address = the door UUID from
get_doors. Place it in the Doors subsystem (or Security if you manage doors alongside the alarm panel). - Camera — Address = the 16-char
camera_idfromget_cameras. Place it in the Cameras subsystem.
- Access door — Address = the door UUID from
- Resolve an id collision if one ever occurs. If the same id turns up in both rosters, set the zone-level Verkada Device Type (
verkada_type) attribute todoororcamerato pin the routing. Otherwise the driver auto-detects by roster lookup, falling back to the id shape (16-char hex → camera, UUID → door).
A default GEM install seeds Doors, Gates, Cameras, and Security subsystems — there is no access subsystem, and the camera subsystem is Cameras (plural). When you open the New Zone editor for a Verkada zone it pre-selects Security (the only one of this driver's subsystem hints that matches a seeded subsystem), so change it to Doors or Cameras as appropriate. A zone's subsystem only governs where the door or camera appears in the rest of GEM — it does not affect how the driver talks to Verkada.
Attribute Reference
Device
| Attribute | Required | Description |
|---|---|---|
api_key | yes | The Verkada Command API key. Stored encrypted. |
api_base | no | Override the API base URL — set this only for GovCloud / dedicated tenants. Default https://api.verkada.com. |
status_interval | no | Door-roster poll interval in ms. Default 60000. |
event_poll_interval | no | Access-events poll interval in ms. Default 30000. Set to 0 to disable event polling entirely. |
enable_event_polling | no | When false, skips the events loop even if event_poll_interval > 0. Default true. |
unlock_seconds | no | Default unlock pulse duration when the unlock command is called without an explicit duration. Verkada caps at 60 s. Default 5. |
request_timeout | no | HTTPS request timeout in ms. Default 15000. |
Zone
| Attribute | Description |
|---|---|
verkada_type | Optional hint — door or camera. Only needed when auto-detection fails. |
Zone State Attributes (written by the driver for door zones)
| Attribute | Description |
|---|---|
state | Coarse state derived from lock_state — locked, unlocked, open, closed, the raw lowercased string when Verkada returns a value the driver doesn't yet map, or unknown when the door reports no lock value. |
lock_state | Raw lock state from the API. |
lock_status | Raw lock status string when present (some firmware exposes this separately from lock_state). |
online | Boolean — is the access controller reporting in. |
site_name | The Verkada site this door belongs to. |
schedule_active | Boolean — is the door currently following a Command schedule. |
last_event | Most recent event type seen by the events loop (e.g. door.access_granted, door.forced_open). |
last_event_at | Unix timestamp of the most recent event. |
last_event_user | User name from the most recent event, if present. |
Camera zones are not polled for state — the door status loop only refreshes door zones. A camera zone is simply a handle for the on-demand get_camera_link, get_thumbnail, and get_status commands. The access-event attributes (last_event, last_event_at, last_event_user) are cataloged, so they autocomplete in the Attribute editor and can drive attribute triggers; the lock-state attributes above are written live as the door roster is polled, without a catalog entry.
Zone Address Format
- Door zones: the Verkada door UUID (looks like
12345678-90ab-cdef-1234-567890abcdef). - Camera zones: the 16-char hex
camera_id(e.g.a1b2c3d4e5f60718).
Commands
| Command | Args | Notes |
|---|---|---|
get_cameras | — | List all cameras visible to the API key. |
get_doors | — | List all access doors with lock state and online status. |
get_status | address | Refresh and return current state for one zone. Auto-detects whether it's a door or camera. |
unlock | address, duration | Admin pulse-unlock a door. duration is 1-60 seconds (default 5). Logged in Command audit as "Admin Unlock". |
lock | address | Send a lock command to a door. Doors under a Command schedule will reject this — adjust the schedule in Command instead. |
get_events | since | Fetch access events newer than the supplied unix timestamp. Defaults to the last 5 minutes. |
get_camera_link | address | Return a short-lived HLS playback URL for one camera (Verkada expires these in ~15 min). |
get_thumbnail | address | Return a JPEG thumbnail URL for one camera. |
refresh | — | Force an immediate door-roster + events sweep. |
Known Limitations
- Camera control surface is read-only. PTZ, recording controls, and audio talk-down are not exposed by the public API and so are not implemented here.
- Lock is best-effort and schedule-dependent. There is no universal "lock now" verb across every Command build, so the driver issues a lock request and, on doors that are under a
LockedorUnlockedschedule block — or where the build doesn't expose the verb — returns a friendly error directing you to set the schedule in Command instead. For deterministic lock control, drive the door's schedule in Command rather than thelockcommand. - Admin-unlock attribution. Verkada audit logs show the unlock as "Admin Unlock" performed by the API key name, not by an end user. If end-user attribution matters, the call needs to go through the user-mode unlock endpoint, which requires a per-user OAuth token rather than the org-level API key — out of scope for this driver.
- Events watermark drift. The driver tracks the last event timestamp it consumed and only asks for newer events on subsequent polls. A long disconnect will leave the watermark stale; on reconnect, the first sweep may pull a large batch.
- Camera footage links are ephemeral.
get_camera_linkreturns signed URLs that expire (~15 min). Don't cache them; re-fetch when needed. - GovCloud / FedRAMP tenants. Set
api_baseto the correct hostname for those deployments. The defaultapi.verkada.comwill return 401/404 against the wrong tenant. - API key scopes are silent. A key missing a scope returns 403 from the call that needs it, not at key creation. The driver logs the HTTP status and the body so missing-scope errors are easy to identify.
Troubleshooting
http 401on every call —api_keyis wrong, revoked, or expired. Re-mint in Command.http 403on a specific endpoint — the key lacks the scope needed for that endpoint. Edit the key in Command and add the scope.http 429— You're being rate-limited. Raisestatus_intervalandevent_poll_interval, and avoid hammeringrefresh.unknown address; run get_doors or get_cameras— The zone address isn't in either cached roster, and auto-detection failed. Either runget_doors/get_camerasto refresh the cache, or setverkada_typeon the zone explicitly.lock not supported by this api key / door — adjust schedule in command— Either the API key lacksAccess: Write, or the door is on a schedule that owns lock state. Both are configured in Command.- No events appear after enabling event polling — Either the API key lacks
Access: Read, or the org has no recent activity. Bumpsincefurther back viaget_events {since: <unix_ts>}to confirm.
Related documentation
- Zones — create one zone per door or camera and set its Address.
- Subsystems — the Doors, Cameras, and Security subsystems these zones belong to.
- Devices — adding, reloading, and running commands on the Verkada device.
- Commands — running
get_doors/get_camerasand theunlock/lockcontrol commands.