Skip to main content

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 — for get_cameras, get_camera_link, get_thumbnail.
    • Access: Read — for get_doors, door state polling, the events feed.
    • Access: Write — for unlock (admin pulse) and lock.
  • Outbound HTTPS to api.verkada.com (or your tenant's API base for GovCloud installs). No inbound firewall changes are required.

Setup

  1. 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.
  2. Create the GEM device. Admin → Devices → "+ Device". Driver verkada_command. Paste the key into api_key. Subsystem access or security is typical when control is in scope; camera is fine for read-only camera surfaces.
  3. Save and reload. The driver smoke-tests both rosters and logs e.g. verkada command connected: <id> <name> 12 cameras 4 doors. A (no access scope) / (no camera scope) note means the key is missing the corresponding scope — fix in Command, no GEM change needed.
  4. Discover devices.
    await GemApp.getInstance().command({device: <id>, action: 'get_doors'});
    await GemApp.getInstance().command({device: <id>, action: 'get_cameras'});
  5. Create one zone per device you care about:
    • Access door: zone.address = the door UUID from get_doors. Subsystem access or security.
    • Camera: zone.address = the 16-char hex camera_id from get_cameras. Subsystem camera.
  6. If you ever hit a collision (the same id appearing in both rosters), set the zone-level verkada_type attribute to door or camera to lock in the routing.

Attribute Reference

Device

AttributeRequiredDescription
api_keyyesThe Verkada Command API key. Stored encrypted.
api_basenoOverride the API base URL — set this only for GovCloud / dedicated tenants. Default https://api.verkada.com.
status_intervalnoDoor-roster poll interval in ms. Default 60000.
event_poll_intervalnoAccess-events poll interval in ms. Default 30000. Set to 0 to disable event polling entirely.
enable_event_pollingnoWhen false, skips the events loop even if event_poll_interval > 0. Default true.
unlock_secondsnoDefault unlock pulse duration when the unlock command is called without an explicit duration. Verkada caps at 60 s. Default 5.
request_timeoutnoHTTPS request timeout in ms. Default 15000.

Zone

AttributeDescription
verkada_typeOptional hint — door or camera. Only needed when auto-detection fails.

Zone State Attributes (written by the driver for door zones)

AttributeDescription
stateCoarse state from lock_statelocked, unlocked, open, closed, or the raw string when Verkada returns a value the driver doesn't yet map.
lock_stateRaw lock state from the API.
lock_statusRaw lock status string when present (some firmware exposes this separately from lock_state).
onlineBoolean — is the access controller reporting in.
site_nameThe Verkada site this door belongs to.
schedule_activeBoolean — is the door currently following a Command schedule.
last_eventMost recent event type seen by the events loop (e.g. door.access_granted, door.forced_open).
last_event_atUnix timestamp of the most recent event.
last_event_userUser name from the most recent event, if present.

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

CommandArgsNotes
get_camerasList all cameras visible to the API key.
get_doorsList all access doors with lock state and online status.
get_statusaddressRefresh and return current state for one zone. Auto-detects whether it's a door or camera.
unlockaddress, durationAdmin pulse-unlock a door. duration is 1-60 seconds (default 5). Logged in Command audit as "Admin Unlock".
lockaddressSend a lock command to a door. Doors under a Command schedule will reject this — adjust the schedule in Command instead.
get_eventssinceFetch access events newer than the supplied unix timestamp. Defaults to the last 5 minutes.
get_camera_linkaddressReturn a short-lived HLS playback URL for one camera (Verkada expires these in ~15 min).
get_thumbnailaddressReturn a JPEG thumbnail URL for one camera.
refreshForce 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 semantics depend on the door's Command schedule. lock will return a vendor error on doors that are under a Locked or Unlocked schedule block; in those cases adjust the schedule directly in Command.
  • 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_link returns signed URLs that expire (~15 min). Don't cache them; re-fetch when needed.
  • GovCloud / FedRAMP tenants. Set api_base to the correct hostname for those deployments. The default api.verkada.com will 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 401 on every callapi_key is wrong, revoked, or expired. Re-mint in Command.
  • http 403 on 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. Raise status_interval and event_poll_interval, and avoid hammering refresh.
  • unknown address; run get_doors or get_cameras — The zone address isn't in either cached roster, and auto-detection failed. Either run get_doors / get_cameras to refresh the cache, or set verkada_type on the zone explicitly.
  • lock not supported by this api key / door — adjust schedule in command — Either the API key lacks Access: 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. Bump since further back via get_events {since: <unix_ts>} to confirm.

Verification Status

This driver was authored against Verkada's public developer documentation (apidocs.verkada.com) and community references. The following call shapes are documented and used as-is; live verification on a Verkada Command tenant is recommended before production deployment:

  • GET /cameras/v1/devices, GET /cameras/v1/footage/link, GET /cameras/v1/footage/thumbnails.
  • GET /access/v1/doors, POST /access/v1/door/admin_unlock (body {door_id, duration_seconds}).
  • GET /events/v1?start_time=<unix>&limit=100.

POST /access/v1/door/lock is documented inconsistently across Command builds; the driver detects a 404 and surfaces a friendly error so you can switch to a Command schedule instead.