Devices
Concept: Devices. Base path: /api/org/{slug}/devices (authenticated). The current device is identified by the session’s device_id claim.
Every route below requires an org-scoped permission in addition to authentication. The
organization Owner and Admin bypass these checks; any other member must hold the listed
scope. Missing permission returns 403 with errorCode = "PERMISSION_DENIED" and the required
scope in required.
| Method · Route | Purpose | Required scope |
|---|---|---|
GET / | List devices (?ownership=asset): { devices, totalCount, assetCount, byodCount, pendingCount }, with isCurrent per row | org.device_identity.list |
POST /register | Register a device: deviceName, deviceModel?, deviceOS?, assignedToEmail, ownership (Asset or BYOD). Returns { id, message } | org.device_identity.create |
PUT /{deviceId}/status | Change status (block/activate/…): { status, reason? } | org.device_identity.update |
DELETE /{deviceId} | Delete a device | org.device_identity.delete |
POST /{deviceId}/notify-tpm | Send TPM-match notification | org.device_identity.update |
PUT /{deviceId}/geofence | Set allowed countries: { countries: ["KR", "US"] }; an empty list clears the restriction | org.device_identity.device_binding.update |
POST /{deviceId}/assign | Assign device to a member: { email } | org.device_identity.device_binding.update |
POST /assign-by-key | Assign by device key, sent in the body: { deviceKey, email } | org.device_identity.device_binding.update |
POST /check-assignable | Check whether a device key can be assigned (no side effect): { deviceKey } → { assignable, error } | org.device_identity.device_binding.read |
Anti-lockout
Section titled “Anti-lockout”Status-change and delete enforce anti-lockout: you cannot block/delete your own current or last device. Such attempts return 409 Conflict; a precondition failure (e.g. delete requires prior block) returns 400, and an unknown device 404. The body is { "error": "<reason>" } in each case.
Device modes (paid)
Section titled “Device modes (paid)”Restricting logins to pre-registered devices is a paid capability. Both device modes need the
Advanced Device Trust add-on (addon.device_trust), whatever the plan. They are set with
PUT /api/org/{slug}/settings/security (deviceMode):
- Fortress (only pre-registered devices may log in).
- Hybrid (pre-register and distinguish company devices).
Without the add-on, saving either mode answers 402 with
{ "error": "ADDON_REQUIRED", "addon": "addon.device_trust", "detail": "…" }. The same answer
covers the other controls on that card: allowed email domains, device-bound passkeys, country
access rules and shared-device allowance. Turning any of them off never needs the add-on.
PUT /api/org/{slug}/settings/security validates the session policy and the device mode, and
answers 400 with the reason in error when a value is out of range:
| Field | Allowed |
|---|---|
maxSessionHours | 1–24 |
idleTimeoutMinutes | 5–120 |
maxConcurrentSessions | 1–10 |
deviceMode | open, hybrid or fortress (open when omitted) |
newDevicePolicy | Follows the mode: allow for open, approve for hybrid, block for fortress. Leave it out, or send the matching value; any other value is refused |
The same add-on is checked at the pre-registration routes themselves, so it cannot be bypassed by calling the API directly:
- POST
/registerwithownership: "Asset"(admin pre-registration) → 403 with the reason inerror. Registering your own device stays free. - POST
/{deviceId}/assign(company-device assignment) → 402 with the sameADDON_REQUIREDbody as above. - POST
/assign-by-key(including the device picked in a member invite) → 400 with the reason inerror;/check-assignableanswersassignable: falsewith the reason inerror.
Registering a personal seal (passkey) on any device is always free. There is no per-organization device-count limit. The Device Trust add-on is $99/month plus $2/month for each company (asset) device that is pre-registered or assigned (pending or active; a shared device counts once). Personal (BYOD) devices are never counted.