On this page0
Unfamiliar word? See Glossary: words used in these docs. Looking for the tasks in order? Begin at Start Here.
Reference
Included in: All plans. Which Plan Includes What
Roles & Permissions (RBAC)
Who can see and do what in your instance is decided by roles and permissions (role-based access control, RBAC).
What It Does
An administrator shapes access on three screens under Administration: Roles, Groups and Access Control. The diagram shows how a permission, a role and a group fit together.
How Access Is Decided
A person’s effective permissions are everything granted by the roles they hold directly, plus the roles they inherit from the groups they are in, combined with any direct grants made to them individually. Roles are the normal path; a direct grant is the exception you make for one person without inventing a new role.
A group is a set of people. A role can be attached to a group, and every member then holds that role for as long as they are in the group (resolved live from group membership, not copied onto the person). A group also carries an MFA policy. A persona is a provisioning template for a job function, and it can optionally list Zonitix groups and roles. When the persona is applied — on user creation, onboarding or a role change — the person is added to those groups and roles, on top of the baseline. The same limit applies as everywhere else: an administrator cannot use a persona to give access they do not hold themselves.
If something here is not working as you expect, start with When something goes wrong.
The plain explanation is above. This part is for IT and developers: the exact settings, permission names, API addresses and data fields behind it. Closed: select the heading above to open it. While it is closed, your browser’s find-on-page (Ctrl+F or Cmd+F) does not search this part.
Built-In Roles
Setup seeds these. They are ordinary roles — rename them, change their permissions, or ignore them and build your own.
| Role | What it is for |
|---|---|
| administrator | Full system access |
| standard_user | Baseline access for all employees |
| hr_manager | Manage recruitment and employee lifecycle. Also holds the employee-record permissions: view and edit employee files, view and add compensation, build the payroll hand-off file, people reports, leave, review cycles, training and surveys (bound to the HR Managers group) |
| hr_employee | HR employee: view users, view employee files, start onboarding/offboarding runs, manage personas (bound to the Human Resources group) |
| manager_approver | Approve onboarding/workflow steps and access requests for anyone in the organisation (not limited to their own reports), and decide the access-review items assigned to them (bound to the Manager group) |
| recruiting_coordinator | Handle scheduling and logistics |
| sourcer | Find and add candidates to top of funnel |
| hiring_manager | Review candidates and submit scorecards |
| interviewer | Participate in interviews and submit scorecards |
| executive_reviewer | Final sign-off on anonymised candidate packets |
| finance_manager | Financial reconciliation, AI insights |
| finance_ops | Finance operations: view spend, manage budgets, submit invoices (bound to the Finance group) |
| finance_approver | Finance approver/controller: approve and pay invoices, approve budgets, close the period |
| One further role | Belongs to an optional add-on and is not described in these pages. Your Roles page lists it with the others. |
| security_analyst | Monitor compliance and Security Lab findings. Also holds manage_security (Security Settings), so assign it deliberately. Holds manage_employee_training and manage_phishing_simulation as well |
| security_compliance_viewer | Read-only security & compliance visibility: read findings and logs, run scans (bound to the Security & Compliance group) |
| it_operations | IT staff: own persona blueprints without user or security admin (bound to the IT group) |
| cloud_architect | Oversee cloud infrastructure and resource mapping |
| integration_manager | Manage external integrations and identity hooks |
Operating RBAC — The API
Everything above is the model; this section is how you actually change it. Every route below is served by the user-management service behind the gateway, so the public path is prefixed /api/v1/user-management and every call needs a valid session (Authorization: Bearer <jwt>) that carries the listed permission. There is no separate RBAC console API — these are the same endpoints the dashboard’s Roles, Groups and Permissions screens call. Two things hold across all of them: writes replace, they do not merge (userIds, groupIds and permissionIds set the entire membership, group binding or permission set, so anything in a list you send that you omit is removed), and the subset invariant in its own section below gates every grant.
Roles
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/v1/user-management/roles | List every role with its permissions, member IDs (IDs only, not full profiles) and the groups it is attached to (id and name) | view_roles · manage_roles · view_dashboard |
| GET | /api/v1/user-management/roles/:id/members | Full labelled roster of one role (names/emails) for the Edit Role dialog | view_roles · manage_roles |
| POST | /api/v1/user-management/roles | Create a role and optionally seed its permissions, members and attached groups in one call | manage_roles |
| PUT | /api/v1/user-management/roles/:id | Rename, re-describe, and replace the permission set, membership and/or attached groups of a role | manage_roles |
| DELETE | /api/v1/user-management/roles/:id | Delete a role (the administrator role is protected and returns 403) | manage_roles |
| POST | /api/v1/user-management/roles/:roleId/permissions | Replace the full permission set of an existing role | manage_roles |
name is the only required field on create (it is unique and non-null); description, userIds, groupIds and permissionIds are optional. groupIds attaches the role to groups: every member of an attached group holds the role while they are in it (the dashboard calls this Assign to Groups). Like the other lists it is a full replacement, and GET /roles returns the current binding as Groups. Create returns 201 with the new role, update returns 200, delete returns 204, and POST /:roleId/permissions returns 200 { "ok": true }. A missing role is 404. A missing or blank name is 400 (Role name is required. on create, Role name cannot be blank. on update), and a name that is already in use is 409. The administrator role is special on every path: only a caller who already holds administrator may create it, rename into or out of it, change its membership (logged ADMIN_ROLE_MEMBERSHIP_CHANGED at critical severity), or change the groups attached to it (logged ADMIN_ROLE_GROUPS_CHANGED at critical severity, since every member of an attached group becomes an administrator); it always holds the entire catalog and cannot be deleted. A change that would leave it with no member, or remove the primary administrator or the caller from it, is refused with 403 and logged ADMIN_ROLE_LOCKOUT_BLOCKED (see the subset invariant below).
// Create a role with two permissions, one direct member and one attached group:
curl -X POST https://<your-instance>-api.zonitix.com/api/v1/user-management/roles \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "invoice_clerk", "description": "Submits invoices",
"permissionIds": [33, 35], "userIds": [42], "groupIds": [3] }'
// 201 Created → the new role. Logs ROLE_CREATED (info).
// Replace the whole permission set of role 7 (omitted permissions are removed):
curl -X POST https://<your-instance>-api.zonitix.com/api/v1/user-management/roles/7/permissions \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "permissionIds": [11, 19] }'
// 200 { "ok": true }. Logs ROLE_PERMS_UPDATED (info).
The Permission Catalog & Per-User Direct Grants
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/v1/user-management/permissions | The full catalog, each permission carrying its Roles[] and holder-user IDs (the org-wide who-holds-what map) | manage_permissions · manage_roles |
| POST | /api/v1/user-management/permissions/:id/assignments | Set which roles and which individual users hold this one permission (the inverse join) | manage_permissions |
GET /permissions is deliberately not readable by the baseline view_dashboard role: it returns the list of who holds manage_security, pay_invoice and the rest — a social-engineering target list — so it is gated to RBAC admins. A user’s own effective access is derived from their JWT, not from this endpoint. POST /:id/assignments is the per-user direct-grant path: userIds writes rows straight into the user’s effective permissions (these land in the JWT), and roleIds writes the same role↔permission rows from the other side. Both are optional but both are full replacements of that permission’s user set and role set respectively — and the administrator role is always re-added afterwards, so it can never be stripped of a permission this way. Returns 200 { "ok": true }, or 404 if the permission id does not exist.
// Grant permission 29 directly to user 42, and also to role 5:
curl -X POST https://<your-instance>-api.zonitix.com/api/v1/user-management/permissions/29/assignments \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "userIds": [42], "roleIds": [5] }'
// 200 { "ok": true }. Logs PERMISSION_ASSIGNMENT_UPDATED (info).
// Blocked with 403 if you do not yourself hold permission 29 (see the subset invariant).
Groups & MFA Policy
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/v1/user-management/groups | List groups with member IDs (IDs only) | view_groups · manage_groups · view_dashboard |
| GET | /api/v1/user-management/groups/:id/members | Full labelled roster of one group | view_groups · manage_groups · manage_roles |
| POST | /api/v1/user-management/groups | Create a group, optionally with members and an MFA policy | manage_groups |
| PUT | /api/v1/user-management/groups/:id | Rename/re-describe a group, set membership, set its MFA policy | manage_groups |
| DELETE | /api/v1/user-management/groups/:id | Delete a group | manage_groups |
Groups organise people, pass on any role attached to them (see Roles above), and carry an MFA policy. The policy is the three-way mfaPolicy: inherit (follow the organisation-wide setting), require (every member must use MFA) or exempt. An exemption takes two more fields: mfaExemptUntil (an ISO date, or null for no end date) and mfaExemptReason; both are cleared when the policy leaves exempt. The older boolean requiresMfa is still accepted when mfaPolicy is not sent (true maps to require, false to inherit) and is kept mirrored on the group.
Writable fields are allow-listed — name, description, userIds — and the MFA fields are only honoured for a caller who also holds manage_security. A policy change is audited as its own event: MFA_EXEMPTION_GRANTED and MFA_EXEMPTION_REVOKED at warning severity, and MFA_POLICY_CHANGED (info) for a move between inherit and require. Create returns 201, update 200, delete 204; a missing group is 404 and a name that is already in use is 409. A membership change on a group that confers permissions the caller does not hold is 403 (see the subset invariant below). Deleting a group is audited as GROUP_DELETED at warning severity.
Be precise: who can change MFA enforcement
A plain manage_groups operator who sends mfaPolicy: "exempt" (or the legacy requiresMfa: false) does not weaken MFA — every MFA field is silently dropped from the write and the group keeps its current policy. This is deliberate: it stops a delegated group manager from downgrading a security control. Changing any of mfaPolicy, mfaExemptUntil, mfaExemptReason or requiresMfa requires manage_security. Inspector check: as a manage_groups-only caller, PUT a group with mfaPolicy: "exempt" and confirm the stored policy is unchanged.
Scoped Compliance Access
| Method | Path | Purpose | Auth |
|---|---|---|---|
| POST | /api/v1/user-management/permissions/compliance-access/:userId | Grant one user the scoped view_assigned_compliance permission | manage_compliance |
| DELETE | /api/v1/user-management/permissions/compliance-access/:userId | Revoke that scoped grant from the user | manage_compliance |
These two are a narrow, fixed grant: they can only ever add or remove the single view_assigned_compliance permission (a direct user grant, so it touches only that one person), used when an auditor assigns or finalizes a compliance section. There is no body — the target is the :userId in the path. A missing user is 404; if view_assigned_compliance was never seeded, grant returns 400. Success is 200 { "ok": true }, logged COMPLIANCE_ACCESS_GRANTED / COMPLIANCE_ACCESS_REVOKED.
The Subset Invariant: You Cannot Confer What You Do Not Hold
This is the single most important control on the RBAC surface, and it is worth stating plainly because an earlier version of it failed by guarding one door of several. A non-administrator caller can never grant a permission they do not themselves hold — not by creating a role that carries it, not by editing a role into carrying it, not by replacing a role’s permission set, and not by assigning the permission from the permission side. The caller’s held set is resolved from the database (their roles’ permissions plus their own direct grants), never from the request header, because the gateway scopes that header per service. An administrator bypasses the check by design (it is root and holds everything), and an unidentifiable caller fails closed — it may grant nothing.
Guarded On Role Creation
POST /roles checks every id in permissionIds against the caller’s held set before the role is written. Excess permissions abort the whole transaction with 403 and a ROLE_ESCALATION_BLOCKED audit line (warn).Guarded On Every Assignment Path
PUT /roles/:id, POST /roles/:roleId/permissions and POST /permissions/:id/assignments run the identical check. Adding members to a role whose permissions exceed yours is blocked too, and so is attaching a group to it with groupIds (every member of the group would inherit the role) — building the powerful role and then filling it is the same escalation, one step later. Both return 403 with You cannot assign members or groups to a role holding permissions you do not have.Guarded On Group Membership
POST /groups and PUT /groups/:id refuse a membership change on a group that confers permissions the caller lacks: 403 with You cannot change membership of a group that grants access you don’t hold followed by the permission names, and a ROLE_ESCALATION_BLOCKED audit line (warn). A group with no role attached confers nothing, so ordinary group creation is unaffected.The Administrator Role Cannot Be Emptied
administrator role’s members or attached groups is judged on who would still be an administrator afterwards, counting direct members and the members of every attached group. It is refused with 403, and audited as ADMIN_ROLE_LOCKOUT_BLOCKED (warn), if it would leave the role with no member, remove the primary administrator, or remove the caller. Adding people, or re-saving an unchanged list, is never refused.Inspector verification
Sign in as an operator who holds manage_roles and manage_permissions but not manage_security. Look up that permission’s id from GET /permissions, then attempt to confer it four ways: create a role with it in permissionIds; PUT an existing role to add it; POST /roles/:roleId/permissions with it; and POST /permissions/<that id>/assignments with your own userIds. Each must return 403 { "error": "Privilege escalation blocked" } with a message naming the permission, and each must leave a ROLE_ESCALATION_BLOCKED (or PERMISSION_ESCALATION_BLOCKED) entry at warn severity in the audit log. If any of the four accepts the grant, the invariant is broken on that path.
Writes replace — omission removes
permissionIds, userIds and groupIds are not additive. Posting { "roleIds": [3] } to a permission removes it from every role except 3; replacing a role’s permissionIds drops any you leave out. To add without disturbing the rest, read the current set first and send the union.
Seeded Permission Catalog
Every atomic permission the platform seeds, straight from the canonical catalog the installer and the on-boot reconciler both read. It is the full set a role’s permissionIds can draw from.
| Module | Permission | Grants |
|---|---|---|
| Dashboard | view_dashboard | Access to the main executive overview |
| User Management | manage_users | Full administrative rights over users |
| User Management | view_users | Read-only access to browse users |
| User Management | manage_groups | Manage organizational groups |
| User Management | view_groups | Read-only access to view groups |
| RBAC | manage_roles | Create and modify system roles |
| RBAC | view_roles | Read-only access to view roles (prerequisite for manage_roles / manage_permissions) |
| RBAC | manage_permissions | Assign roles and per-user permission overrides |
| Workflow | manage_workflows | Design lifecycle workflows (build and submit for approval — not approve) |
| Workflow | approve_workflows | Approve or reject a workflow design and promote a version live |
| Workflow | execute_workflows | Manually trigger and execute workflows |
| Workflow | manage_approvals | Approve or reject workflow-driven requests |
| Audit | view_logs | Read-only access to the system audit trail |
| Audit | manage_logs | Reserved. Audit logs are append-only and cannot be deleted or purged by anyone. Today this only adds audit logs to reports, the same as view_logs |
| Integration | manage_hooks | Full control of Identity Hooks (superset of connect + policy + writeback) |
| Integration | manage_integrations | Connect/configure identity providers (CRUD + test + sync) — no policy edit or writeback |
| Integration | manage_alignment_policy | Edit the AlignSync source-of-truth policy and source file (does not grant writeback) |
| Integration | execute_identity_writeback | Push single-user attribute writeback / rollback to a connected directory |
| Integration | approve_identity_changes | Approve or reject pending AlignSync bulk reconciliations (separation of duties) |
| Integration | view_saas_discovery | View SaaS Discovery: per-app active users and utilisation, dormant accounts and accounts that match nobody in your directory (read-only) |
| Security | manage_security | Modify global security policies |
| Security | create_api_tokens | Create personal API tokens for scripted API access (scoped to a subset of your own permissions; revocable; expire within a year) |
| Security | view_cloud_infrastructure | View AWS/Azure cloud infrastructure & cost maps |
| Compliance | manage_compliance | Execute compliance audits |
| Compliance | view_assigned_compliance | View only the compliance sections assigned to you (auto-granted on assignment) |
| Reporting | view_reporting | Access to advanced system reporting |
| System | view_system | View the System panels: health, software/OS update status, and backup/restore points (read-only; not the reporting hub) |
| System | request_system_updates | Request a DB backup, application patch, or host OS patch (requires admin approval to run) |
| System | approve_system_updates | Approve or reject pending backup/patch/OS-update requests (admin-level) |
| Finance | view_finance | Access to Finance Insights |
| Finance | manage_finance | Full access to financial reconciliation |
| Finance | submit_invoice | Submit an invoice or expense into your own review queue (self-service; does not unlock the Finance workspace or other people’s documents) |
| Finance | approve_invoice | Approve submitted invoices (separation of duties) |
| Finance | pay_invoice | Mark approved invoices as paid (separation of duties) |
| Finance | close_finance | Lock an accounting period / run month-end close (separation of duties; distinct from approving invoices) |
| Finance | manage_finance_policy | Edit finance control policy: the amount-tiered approval rules for invoices and budget transfers, plus exchange rates and base currency (separate from manage_finance so operators cannot weaken their own approval requirements) |
| Finance | view_finance_budgets | View the budgets you own (your department); does not unlock the full Finance workspace |
| Finance | manage_finance_budget | Create and edit your own department budgets (a manager cannot edit a peer’s budget) |
| Finance | submit_finance_budget | Submit your own budget for approval (the submitter cannot approve it) |
| Finance | approve_finance_budget | Approve or reject a submitted budget and lock it (distinct from approving invoices; cannot approve your own budget) |
| Finance | view_finance_consolidated | View all budgets across every department (bypasses the owner scope) |
| Finance | manage_all_finance_budgets | Create, edit and lock any budget in any department |
| Finance | transfer_finance_budget | Reallocate budget across departments (two-person approval; fully audited) |
| Organization | view_org_chart | View company organizational structure |
| Organization | manage_org_chart | Manage and save organizational snapshots |
| HR | view_referrals | Access to the internal referrals portal |
| HR | manage_referrals | Submit and track your own employee referrals (same access as view_referrals; there is no screen for other people’s referrals) |
| HR | manage_recruitment | Manage job postings and candidate pipelines |
| HR | view_recruitment | View job postings and candidate pipelines |
| HR | conduct_interview | Submit interview scorecards |
| HR | approve_offer | Approve and send candidate offers |
| HR | schedule_interviews | Manage interview logistics without viewing feedback |
| HR | source_candidates | Add candidates to the top of the funnel only |
| HR | review_executive_packet | Read-only access to finalized candidate packets |
| HR | view_employee_records | View employee files: employment details, job history, personal details and emergency contacts |
| HR | manage_employee_records | Edit employee files and record job changes (title, department, manager, level, location) |
| HR | view_compensation | View employee compensation history (each view is audit-logged) |
| HR | manage_compensation | Add compensation records to an employee file |
| HR | view_employee_reports | View people reports: headcount, joiners and leavers, turnover and tenure |
| HR | export_compensation | Build the payroll hand-off file: joiners, leavers, pay changes and approved leave for a period, with pay amounts |
| HR | manage_employee_reviews | Create and launch performance review cycles, reassign reviewers, and read reviews (each read is audit-logged) |
| HR | manage_employee_training | Keep the list of training items, assign them to people, and see who has completed them |
| HR | manage_employee_surveys | Create, open and close anonymous staff surveys and read their combined results (never individual answers) |
| HR | manage_leave | Set up leave types and holiday calendars, adjust balances, and approve or decline any time-off request |
| Security | manage_phishing_simulation | Create, approve and send phishing simulations to your own staff, and see who clicked or reported (each view of per-person results is audit-logged) |
| Security Lab | view_security_findings | View security scan findings and reports |
| Security Lab | run_security_scan | Launch security scans and probes |
| Security Lab | manage_security_findings | Triage findings (status, risk acceptance, notes) |
| Security Lab | manage_security_scopes | Manage scan scopes and target assets |
| Security Lab | export_security_report | Export security findings reports |
| Devices | view_devices | View the managed Mac fleet and per-machine inventory |
| Devices | manage_devices | Enroll and manage devices (issue and revoke enrollment tokens) |
| Devices | manage_device_destructive | Destructive or secret device actions: remote wipe, recovery-key reveal, push-certificate key backup (requires re-authentication) |
| Access Governance | request_access | Submit a self-service request for an app, licence or access (routes to an approver) |
| Access Governance | approve_access_request | Approve or reject pending access requests (separation of duties; fulfilled only on approval, then at IT’s provisioning gate) |
| Access Governance | certify_access | Make keep/revoke decisions on access-certification items assigned to you (attestation) |
| Access Governance | view_access_reviews | View access-certification campaigns and their review items (read-only) |
| Access Governance | manage_access_reviews | Create and administer access-certification campaigns (launch, assign reviewers, close, revoke) |
| User Management | manage_personas | Create and edit role personas (the hardware/license/access bill-of-materials); complete a draft persona at the IT step |
| AI | use_ai | Use AI assistant features. Required in addition to the relevant feature permission only when “Require explicit AI permission” is enabled in Security Settings |