Salto KS
Cloud control for Salto KS — Salto's cloud-managed commercial access control platform — via the public Connect API. The driver does online unlock/lock, lock-roster reads, user enumeration, and audit-feed polling. Offline-only locks programmed via the Salto Space client are out of scope.
Prerequisites
- A Salto KS subscription with the Connect API add-on enabled by Salto support. The API is not exposed on a stock KS account — submit a request through your Salto rep before commissioning.
- A client_id / client_secret pair minted in the KS portal under Account → API Credentials. The driver assumes at minimum these scopes:
locks:readlocks:writeusers:readaudit:read
- Online locks (XS4 One, Neo Cylinder with online module, IQ Door on the online tier). Pure offline locks are returned by
get_locksbutunlockwill fail with a Salto-side error.
Setup
- In the Salto KS portal → Account → API Credentials, create a new credential pair. Copy both
client_idandclient_secret. - Create a device row with driver
salto_ks. - Paste
client_idinto theusernamefield andclient_secretinto thepasswordfield. (The dedicatedclient_id/client_secretattribute names are also accepted if you prefer.) - Optional: if your tenant is on a regional endpoint other than
clp.my-clay.com, setapi_base(e.g.https://api.regional.saltoks.com/v1.1) andauth_url. - Run
get_sites. Copy the site UUID and paste into the device'ssite_idattribute. Single-site accounts are auto-detected — the driver fillssite_idon first connect. - Run
get_locksto enumerate doors. Create one zone per lock;zone.address= Salto lock UUID exactly as returned. - Run
get_status address=<lock-uuid>for one zone to verify end-to-end connectivity.
Attribute reference
| Attribute | Required | Default | Notes |
|---|---|---|---|
username (or client_id) | yes | — | OAuth2 client_id. |
password (or client_secret) | yes | — | OAuth2 client_secret. Stored encrypted. |
site_id | sometimes | — | Required for multi-site accounts; auto-detected when only one site is visible. |
api_base | no | https://clp.my-clay.com/v1.1 | Resource API base. |
auth_url | no | https://clp.my-clay.com/oauth2/token | OAuth2 token endpoint. |
status_interval | no | 60000 ms | Lock-roster sweep cadence. Mind tenant rate limits. |
event_poll_interval | no | 30000 ms | Audit-feed cadence. Set 0 to disable the audit loop entirely. |
unlock_seconds | no | 5 | Default release duration when unlock is called without seconds. Clamped 1–60. |
released_state_ms | no | 5000 ms | How long zone.state stays released after an unlock before reverting to locked. |
Per-zone override:
| Attribute | Notes |
|---|---|
salto_type | Override for ambiguous addresses. lock (default) or door. |
Zone address
Zone address is the Salto lock UUID — copy it verbatim from get_locks. One zone per online lock.
Commands
| Command | Arguments | Notes |
|---|---|---|
unlock | address, optional seconds (1–60) | Online release for unlock_seconds (or seconds arg). Sets zone.state = released, reverts to locked after released_state_ms. |
lock | address | Programmatic lock. Not supported on every hardware revision — older XS4 strikes return 422. |
get_sites | — | Sites visible to this credential. |
get_locks | — | Lock roster for the configured site. |
get_users | — | User roster for the configured site. |
get_status | address | Per-lock detail: battery, online, last-event. |
get_events | limit (≤200), since (ISO ts) | Tail the audit feed. |
refresh_token | — | Discard cached bearer and re-authenticate. |
State surfaced per zone
The status loop sets these attributes on each zone whose address matches a Salto lock id:
online(bool)battery_level(int 0-100)state—locked(default) orreleased(during/after unlock)
The audit loop additionally sets:
last_event— event type/action stringlast_event_at— ISO timestamplast_event_user— user name or email if present in the audit row
Known limitations
- Offline-only locks cannot be unlocked over the API. They appear in
get_lockswithonline=false.unlockwill return a Salto-side error. lock(programmatic lock) is hardware-dependent. Older XS4 strikes do not implement it and return 422.- No event push. The Connect API does not push; state and last-event surfacing both poll. Tune
status_intervalandevent_poll_intervalto your tenant's rate limit. - Audit endpoint variation. Some legacy KS tenants expose the audit feed at
/audit/loginstead of/audit-trails. The driver falls back automatically on 404. - SIP video stations are out of scope. Use a dedicated intercom driver (
aiphone_ix,butterflymx,comelit_vip,gds3710,doorbird) for door-station video. - User management is read-only in this driver. Credential issuance, schedule writes, and access-rule changes are intentionally not exposed — they belong in the Salto KS portal where the audit chain is preserved.
Troubleshooting
oauth token failed (status 401)—client_idorclient_secretis wrong, or the credential was disabled in the KS portal. Re-mint and update the device row.oauth token failed (status 400)— The Connect API add-on is probably not enabled on this tenant. Contact Salto support.salto ks 403— The credential is valid but lacks the scope for that endpoint. Re-mint with the scopes listed under Prerequisites.salto ks 422on unlock — Lock is offline or hardware does not support the operation. Confirm withget_status.- No events appearing — Verify
event_poll_interval > 0and that the credential hasaudit:read. If the audit feed returns rows fromget_eventsbutzone.last_eventis not updating, confirmzone.addressmatches thelockIdfield in the audit payload exactly.