Skip to main content

Kisi (Cloud)

Kisi is a commercial cloud access-control platform commonly deployed in offices, coworking, gyms, apartment lobbies, and mixed-use buildings. This driver talks to the Kisi HTTP API at api.kisi.io to unlock doors, enumerate places and members, and surface the event feed to GEM zones. Credential provisioning, group and role CRUD, and floor-plan editing stay in the Kisi Web app — GEM only reads the roster and issues unlock requests.

Prerequisites

  • A Kisi account with admin (or per-place manager) rights on every door you plan to control from GEM. The driver inherits the account's place and role scope — an API key minted by a receptionist-tier user will only see the doors that account already sees.
  • An API key generated in Kisi Web → your name → Account → API Keys → New API Key. The secret is shown once at creation. Treat it like a password; store it in a password manager before pasting into GEM.
  • Doors already provisioned in Kisi (reader hardware paired to a Place and Group). Only online doors can be unlocked via the API; wireless-only Kisi Card locks and offline hardware are not part of Kisi's REST surface and are out of scope for this driver.

Setup

  1. Mint the API key. In the Kisi Web app (https://web.kisi.io), click your name in the top-right corner → AccountAPI KeysNew API Key. Give it a descriptive label like "GEM Controller — ". Copy the secret.
  2. Add the device in GEM. Go to Admin → System → Devices, click New Device, and pick the driver Kisi. Give the device a friendly name (e.g. "Kisi — HQ").
  3. Paste the credential. Fill in the Kisi API Key attribute. It is stored encrypted at rest via GEM's secure-attribute mechanism.
  4. (Optional) If the API key spans multiple places and you only want GEM to see one, set the Place ID (filter) attribute to that place's integer id. You can find place ids by running get_places.
  5. Save the device. Watch the log for kisi cloud connected: <id> <name> N doors reachable. If it says 0 doors reachable, the API key's underlying account is not on any place — go back to Kisi Web and grant the required group membership.
  6. Enumerate doors. Run the get_doors command (Devices → the Kisi row → Send Command → get_doors). The response lists each door's id, name, place, and lock reference.
  7. Create a zone per door. In Admin → System → Zones, add one zone per door you want to control. Set:
    • Device to the Kisi row you just created.
    • Subsystem to access_control (or door).
    • Address to the Kisi door id (integer, e.g. 12345).
  8. Verify unlock. From the zone tile in a UI, or Devices → the Kisi row → Send Command → unlock with address = <door_id>, trigger a live unlock. The door's strike should chirp within a second.

Attribute Reference

Device attributes

AttributeRequiredDescription
api_keyyesKisi Static Secret / API key. Secure — stored encrypted.
place_idnoOptional Kisi place id to scope roster and event polls. Leave empty to see everything the key can reach.
api_basenoOverride the base URL. Almost always https://api.kisi.io.
status_intervalnoDoor-roster poll cadence in ms. Default 60000. Kisi enforces per-account rate limits; keep at 60s+ on large orgs.
event_poll_intervalnoEvent-feed poll cadence in ms. Default 30000. Set to 0 to disable event polling entirely.
unlock_secondsnoAdvisory duration passed with the unlock request. Kisi controllers ultimately honor the strike-relay hold configured on the reader hardware. Default 5.
released_state_msnoHow long zone.state stays released after an unlock before reverting to locked. Default 5000.

Zone attributes (populated by the driver)

AttributeDescription
statelocked (default) or released for the strike-hold window immediately after an unlock.
onlineBoolean — whether the reader is reporting to Kisi cloud. Sourced from the door roster.
place_nameFriendly place label from Kisi. Handy for admin UI disambiguation.
last_eventMost recent event type on this door (e.g. unlock_granted, door_forced, access_denied).
last_event_atISO timestamp of last_event.
last_event_userName or email of the actor associated with last_event, when Kisi reports one.

Zone Address Format

zone.address is the Kisi door id — always an integer. You can find it either by:

  • Selecting a door in the Kisi Web app and reading the URL: .../places/<pid>/doors/<door_id>.
  • Running the get_doors command from GEM and copying the id field.

Do not use the lock id here. The driver resolves lock ids from the door id on connect (and refreshes on cache miss), so all commands are addressed by door.

Commands

CommandArgsDescription
unlockaddress (door id)Issues an unlock request. Zone state transitions locked → released → locked for released_state_ms.
get_doorsoptional limitEnumerates doors reachable to the API key. Respects place_id.
get_placesoptional limitLists Kisi places (locations).
get_membersoptional limitLists members visible to the API key. Respects place_id.
get_eventsoptional limit, since_idTails the event feed. Watermarked by highest seen id.
get_statusaddressFetches a single door's Kisi record (name, place, lock reference, online status).
refreshnoneForce-refresh the door → lock mapping cache. Rarely needed; the driver refreshes on cache miss automatically.

Known Limitations

  • No programmatic lock. Kisi's model is auto-relock on strike-hold expiry. GEM cannot force a door back to locked before the hardware timer expires — this driver only exposes unlock.
  • No door-position sensor. Kisi's REST API does not report a physical open/closed contact by default. The driver seeds zone.state = locked and toggles it to released on unlock, but does not know if a person actually opened the door. For true door-open telemetry, wire the reader's DPS contact into a relay/BACnet/Modbus point and mirror that into a separate GEM zone.
  • Polled events, not pushed. Kisi does not expose a webhook or push channel accessible without a paid Enterprise contract. The event loop polls; tune event_poll_interval per your rate-limit envelope.
  • Credential and group management out of scope. Adding, removing, or role-scoping members should be done in Kisi Web. GEM only reads get_members for visibility.
  • Rate limit ~120 req/min per key. Combined default polls (60s roster + 30s events) use well under this. On very large sites with many doors, raise the intervals.

Troubleshooting

Symptom: 401 unauthorized at connect.

  • The API key is stale or was truncated on paste. Regenerate it in Kisi Web → Account → API Keys and re-paste. Confirm the underlying account has not been suspended.

Symptom: get_doors returns zero entries.

  • The API key's user has no place membership. In Kisi Web → Places → Groups, add the user to the group that includes the doors, or generate the key from an admin-tier account instead.

Symptom: unlock returns kisi 403.

  • The API key's user is not on the door's access group. Grant access in Places → → Groups → → Members.

Symptom: unlock returns door <id> not found or has no lock associated.

  • The address does not match any door in the roster, or the door has been removed from Kisi. Run get_doors to list current door ids. If the door is a wireless Card lock without an online controller, it is not accessible via the REST API.

Symptom: zone.state sticks at released.

  • released_state_ms was set to 0 or a very large value, so the timer never fires. Raise it to 5000 (5 seconds), the physical strike default. Alternatively, verify that the event loop is running and that the door's follow-up event (unlock_granted) is being observed.

Symptom: Event feed is delayed by minutes.

  • Kisi does not push. Lower event_poll_interval to 10000 (10 seconds) if your rate-limit envelope allows it. Rate-limited responses return a 429; the driver surfaces the raw error in that case.

Also see