Users & Accessv7.0.20
Manage the appliance’s local user accounts, their permissions, and per-user multi-factor authentication — plus the two self-service reads every authenticated client needs: the current user and the assignable permission list.
These endpoints administer local accounts: create and edit users, grant permissions, and manage per-user multi-factor authentication (MFA). Two self-service reads ride along — the current user and the assignable permission list — because every authenticated client needs them, not just administrators.
The endpoints belong to the system functional area: an MCP or OAuth token needs the mcp:system scope. The permission requirement, however, varies within this chapter:
- Reads (
getUsers,getPermissions, andme) are open to any authenticated user. This is deliberate: the Users settings page doubles as the self-service surface for API-token management, so a caller who holds onlyapi(notusers) must still be able to fetch their own row.getUsersfilters its result to the caller’s own account unless the caller holds theusersorsapermission, in which case it returns every user. - Writes (create/save/delete and all three MFA operations) require the
userspermission.
The super-admin account (user id 1) is protected: its id, username, and active flag are immutable, so it cannot be renamed or disabled through these endpoints.
The MFA write endpoints carry their own per-call authorization on top of the users permission: a caller may only generate, confirm, or disable MFA for their own account unless they hold the sa permission, in which case they may act on any user.
Users
List users
GET/api/getUsers
Permission any authenticated user
Returns user accounts with their permissions and tags. Admins (callers holding users or sa) get every user; everyone else gets only their own row. The self_only flag tells the caller which mode the response is in, and caller_id echoes the authenticated user’s id.
Response 200
{
"status": 201,
"hasError": false,
"users": [
{
"id": 7,
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com",
"username": "ada",
"active": true,
"totp": false,
"auth_source": "local",
"permissions": ["devices", "alerts"],
"tags": ["sitea"]
}
],
"self_only": false,
"caller_id": 7
}
totp is true when the user has MFA enabled; auth_source is local or ldap; permissions and tags are slug arrays.
Example
curl -sS https://APPLIANCE/api/getUsers \
-H "Authorization: Bearer $TOKEN"
Create a user
POST/api/createUserusers
Creates a new local user. The account is created active. All fields are required; the password must meet the complexity policy.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
first_name | string | yes | Max 255. |
last_name | string | yes | Max 255. |
email | string | yes | Valid email, unique across users. |
username | string | yes | Max 255, unique across users. |
password | string | yes | Min 8 chars; must contain a lowercase letter, an uppercase letter, and a digit. |
Response 200
{ "status": 201 }
Errors
| Status | Body | When |
|---|---|---|
200 | {"status":false,"hasError":true,"message":"…"} | Validation failed (e.g. duplicate email/username, weak password). The message is the first validation error. |
Both success and validation failure return HTTP 200. Branch on the status field (201 on success) and hasError, not on the HTTP status code.
Example
curl -sS -X POST https://APPLIANCE/api/createUser \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"first_name":"Ada","last_name":"Lovelace","email":"ada@example.com","username":"ada","password":"Sup3rSecret"}'
Update a user
POST/api/saveUserusers
Updates an existing user’s details, active state, tags, password, and permissions. Every field except user is optional — only the fields present in the body are touched. Deactivating a user (active: false) also revokes that user’s live API tokens immediately, not just future logins.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
user | integer | yes | Id of the user to update. |
first_name | string | no | New first name. |
last_name | string | no | New last name. |
email | string | no | New email; rejected if already used by another user. |
username | string | no | New username; rejected if already used by another user. |
active | boolean | no | Active state. Setting false also revokes the user’s live tokens. |
tags | array | no | Array of {value: <slug>} objects; replaces the user’s tag set. null clears all tags. |
password | string | no | New password; same complexity policy as create. Not allowed for LDAP users. |
permissions | array | no | Permission slugs; replaces the user’s permission set. For LDAP users, LDAP-managed permissions are left untouched. |
User id 1’s id, username, and active flag are immutable. Attempts to rename or disable the super-admin through this endpoint are ignored at the model layer.
Response 200
{ "status": 201 }
Errors
| Status | Body | When |
|---|---|---|
200 | {"status":false,"hasError":true,"message":"Email is already in use"} | New email collides with another user. |
200 | {"status":false,"hasError":true,"message":"Username is already in use"} | New username collides with another user. |
200 | {"status":false,"hasError":true,"message":"Cannot change password for LDAP users"} | Password change attempted on an LDAP-sourced account. |
200 | {"status":false,"hasError":true,"message":"…"} | Password failed the complexity policy. |
Example
curl -sS -X POST https://APPLIANCE/api/saveUser \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"user":7,"active":false,"permissions":["devices","alerts"]}'
Delete a user
POST/api/deleteUserusers
Permanently deletes a user account.
This permanently removes the user account. It cannot be undone.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
user | integer | yes | Id of the user to delete. |
Response 200
{ "status": 201 }
Errors
| Status | Body | When |
|---|---|---|
200 | {"status":false,"message":"…"} | The user id was not found (or another error occurred). |
Example
curl -sS -X POST https://APPLIANCE/api/deleteUser \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"user":7}'
MFA
MFA uses a stage-then-confirm flow. generateMfa stages a fresh TOTP secret and returns the provisioning material, but does not turn MFA on. The operator scans the QR, then calls confirmMfa with a working code to promote the staged secret to live. This guarantees the user can never lock themselves out if the QR scan fails. Each of these endpoints lets a caller act on their own account, or on any account if they hold sa.
Generate (stage) an MFA secret
POST/api/generateMfausers
Stages a new TOTP secret for the user and returns the key, hostname, and username for building a provisioning QR. The user’s existing MFA state is unchanged until confirmMfa succeeds.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
userId | integer | yes | User to stage MFA for. Must be the caller unless the caller holds sa. |
Response 200
{
"status": true,
"message": "MFA key staged — call /confirmMfa with a TOTP code to enable",
"mfaKey": "JBSWY3DPEHPK3PXP",
"hostname": "netmon",
"username": "ada",
"pending": true
}
Errors
| Status | Body | When |
|---|---|---|
403 | {"status":false,"message":"Not authorized to modify this user"} | Caller is acting on another user without sa. |
404 | {"status":false,"message":"User not found …"} | Unknown userId. |
500 | {"status":false,"message":"Error generating MFA key"} | Unexpected error. |
Example
curl -sS -X POST https://APPLIANCE/api/generateMfa \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"userId":7}'
Confirm and enable MFA
POST/api/confirmMfausers
Verifies a TOTP code against the staged secret. On success the staged secret is promoted to live and the user’s next login enforces MFA. On failure the live MFA state is unchanged.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
userId | integer | yes | User to confirm. Must be the caller unless the caller holds sa. |
code | string | yes | Current TOTP code from the authenticator app. |
Response 200
{ "status": true, "message": "MFA enabled successfully" }
Errors
| Status | Body | When |
|---|---|---|
400 | {"status":false,"message":"TOTP code is required"} | Empty code. |
400 | {"status":false,"message":"No pending MFA bootstrap to confirm. …"} | No staged secret — call generateMfa first. |
400 | {"status":false,"message":"TOTP code did not match the staged secret. …"} | Code mismatch. |
403 | {"status":false,"message":"Not authorized to modify this user"} | Caller acting on another user without sa. |
404 | {"status":false,"message":"User not found"} | Unknown userId. |
Example
curl -sS -X POST https://APPLIANCE/api/confirmMfa \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"userId":7,"code":"123456"}'
Disable MFA
POST/api/disableMfausers
Disables MFA for the user, clearing the live secret.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
userId | integer | yes | User to disable MFA for. Must be the caller unless the caller holds sa. |
Response 200
{ "status": true, "message": "MFA disabled successfully" }
Errors
| Status | Body | When |
|---|---|---|
403 | {"status":false,"message":"Not authorized to modify this user"} | Caller acting on another user without sa. |
404 | {"status":false,"message":"User not found"} | Unknown userId. |
500 | {"status":false,"message":"Error disabling MFA"} | Unexpected error. |
Example
curl -sS -X POST https://APPLIANCE/api/disableMfa \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"userId":7}'
Permissions
List assignable permissions
GET/api/getPermissions
Permission any authenticated user
Returns the full catalogue of permissions, used to populate the permission picker when editing a user. Each row carries the slug used by every permission:<slug> gate across the API.
Response 200
{
"status": 201,
"hasError": false,
"permissions": [
{ "id": 1, "slug": "devices", "name": "Devices" },
{ "id": 15, "slug": "capture", "name": "Capture" }
]
}
The full permission catalogue and the scopes that correspond to it are documented in Permissions & Scopes.
Example
curl -sS https://APPLIANCE/api/getPermissions \
-H "Authorization: Bearer $TOKEN"
Current user
Get the current user
GET/api/me
Permission any authenticated user
Returns the account record for the authenticated caller. This is the canonical way to validate a token and discover who it belongs to. The response is the bare user object; sensitive fields (password, remember_token, and the MFA secret) are never included.
Response 200
{
"id": 7,
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com",
"username": "ada",
"active": true,
"auth_source": "local",
"created_at": "2026-01-04T09:00:00.000000Z",
"updated_at": "2026-06-10T12:30:00.000000Z"
}
Example
curl -sS https://APPLIANCE/api/me \
-H "Authorization: Bearer $TOKEN"