User guide
Roles and Permissions
Looking for what it does rather than how to use it? Read the Roles and Permissions overview .
Last verified against the codebase: 2026-07-27. Where a capability exists but is not enforced, this guide says so rather than implying it is.
What it is
Every request into Solidlio is decided by three things: which portal the caller is in, what organization role they hold, and which tenant the data belongs to.
On top of that, each organization keeps a directory of named roles — Technician, Dispatcher, Accountant, and any you create — which decide who gets routed work, who is asked to approve spend, and who gets notified.
The three role vocabularies
Solidlio has three separate sets of role names. They are not interchangeable, and knowing which one you are looking at removes most of the confusion in this area.
| Vocabulary | Where it lives | What it decides | Values |
|---|---|---|---|
| Organization role | OrganizationMembership.role, one per person per organization | Authorization. Every API route’s floor is expressed in this vocabulary | platform administrator › MSP administrator › MSP technician › organization administrator › power user › CUSTOMER |
| Job-function role | Role + PersonRole, many per person | Routing and reporting — approver groups, notification targets, staff roster filters | 13 shipped system roles plus any custom role you create |
| Account role | AccountMember.role, one per person per account | Who administers the billing account — invites, member removal, account settings | OWNER › ADMIN › MEMBER |
Platform staff carry a fourth, narrower set — see section 12.
The single most useful thing to remember: the organization role is the one that grants or refuses access. A person with the job-function role “Administrator” and the organization role CUSTOMER is a customer.
The organization-role hierarchy is ordered, and the order surprises people
platform administrator > MSP administrator > MSP technician > organization administrator > power user > CUSTOMER
MSP technician outranks organization administrator. That is correct — a technician at the managed service provider is operating above the client’s own administrator — but it means “administrators only” cannot be written as “organization administrator and above”, because that phrase includes every technician. Solidlio therefore has two guard shapes:
| Guard | Shape | Reads as | Admits |
|---|---|---|---|
requireMinRole("X") | Rank | “X and everyone above X” | Everything at or above X in the list |
requireAnyRole(a, b, c) | Set | “exactly these roles” | Only the named roles, in any order |
Role administration uses the set (platform administrator, MSP administrator, organization administrator). Everything else in this area uses the rank.
How an authorization decision is made
Four checks run, in this order. All four must pass.
- A downstream service never re-reads the token.
- Portal.
requirePortal(...)refuses a caller whose active portal is not one of the named ones. Which portals a person may activate is derived from their roles, not chosen freely — see section 5. - Role floor.
requireMinRole(rank) orrequireAnyRole(set) on the route. There are 662 role-floor call sites across the services: 531 at organization administrator, 58 at MSP administrator, 39 at MSP technician, 24 at platform administrator, and one each at power user andCUSTOMER. - Tenant scope.
accountScope(req)is spread into the database query so the row set is confined to the caller’s account. Platform admins are unscoped; customer-portal callers are pinned to their single organization; everyone else sees any organization inside their account. A caller with no trusted account context matches nothing.
Steps 3 and 4 answer different questions and neither substitutes for the other. The role floor answers what rank is the caller; the tenant scope answers whose data is this. A route with only a tenant check will happily let a customer edit their own organization’s administrators.
Permission matrix
| Capability | platform administrator | MSP administrator | MSP technician | organization administrator | power user | CUSTOMER |
|---|---|---|---|---|---|---|
| Access the platform portal | ● | — | — | — | — | — |
| Access the MSP portal | ● | ● | ● | — | — | — |
| Access the organization portal | ● | ● | ● | ● | ● | — |
| Access the customer portal | ● | ● | ● | ● | ● | ● |
| List people in the tenant | ● | ● | ● | ● | ● | ● |
| Read own effective permissions | ● | ● | ● | ● | ● | ● |
| Read someone else’s effective permissions | ● | ● | ● | ● | — | — |
| Create a person | ● | ● | ● | ● | — | — |
| Edit or deactivate a person | ● | ● | ● | ● | — | — |
| List roles | ● | ● | ● | ● | — | — |
| See who holds a role | ● | ● | ● | ● | — | — |
| Read the permission catalogue | ● | ● | ● | ● | — | — |
| Create a custom role | ● | ● | — | ● | — | — |
| Edit / delete an organization’s own role | ● | ● | — | ● | — | — |
| Clone a role into an organization | ● | ● | — | ● | — | — |
| Change a role’s permission set | ● | ● | — | ● | — | — |
| Assign or remove a job-function role | ● | ● | — | ● | — | — |
| Change someone’s organization role (access level) | ● | ● | — | ● | — | — |
| Edit or delete a platform-wide (global) role | ● | — | — | — | — | — |
| Grant a platform-administration permission | ● | — | — | — | — | — |
| Create, edit or delete a permission in the catalogue | ● | — | — | — | — | — |
| Assign a membership role above your own tier | — | — | — | — | — | — |
Notes on specific cells:
- MSP technician can read roles but not write them. The read floor is a rank (a technician servicing a client needs to see who is who); the write floor is a set that names the three administrator roles. This matches what the web portals show — the MSP role screen offers create/edit/clone/delete only to MSP administrator, the org role screen only to organization administrator.
- “List people in the tenant” is open to every authenticated role because the people-picker on a ticket needs it. The result is still bounded by
accountScope, and a customer-portal caller sees only their own organization. - The last row applies to everyone including platform admins: a caller may assign a membership role at or below their own tier and no higher. An unknown or missing caller role ranks below
CUSTOMERand can assign nothing at all.
Portals
A person’s organization roles determine the highest portal they may activate; they may then work at that level or any level below it.
| Organization role | Highest portal | Portals they can activate |
|---|---|---|
| platform administrator | platform | platform, msp, organization, customer |
| MSP administrator | msp | msp, organization, customer |
| MSP technician | msp | msp, organization, customer |
| organization administrator | organization | organization, customer |
| power user | organization | organization, customer |
CUSTOMER | customer | customer |
Switching portals re-mints the token from the person’s current memberships rather than trusting the presented token’s claim, so a role that was removed cannot be carried forward through a portal switch.
Walkthrough — giving someone administrator rights
Administrator rights are the organization role, not a job-function role.
- Go to Users in the sidebar (
/org/users). - Open the person and select the Permissions tab.
- Under Access level, pick the new role and click Change.
The dropdown offers only roles you may actually grant. The change takes effect the next time that person’s access token is minted — at their next refresh or portal switch — because the role travels in the token. Revoke their sessions if it must apply immediately.
Four refusals apply, and the control hides or disables itself for the first three rather than letting you hit them:
| Refusal | Why |
|---|---|
Cannot grant a role higher than your own | An organization administrator cannot mint an MSP administrator; nobody but a platform admin mints one |
Cannot change a person who holds a role higher than your own | A client’s admin cannot demote the MSP admin operating their workspace |
You cannot change your own organization role | No self-service, in either direction |
Cannot remove the last administrator from this organization | The organization always keeps at least one active administrator |
To set the role at creation time instead, use Users → New user, which carries the same ceiling. The creation form does not accept platform administrator at all.
Change an MSP staff member’s access level from the organization portal, or through the API below.
Walkthrough — creating a custom role
A custom role is a named job function owned by one organization.
- Go to Administration → Roles in the sidebar (
/org/settings/roles, or/msp/settings/roles). - Click Create Role.
- Enter a display name (“Field Engineer”). The machine name is derived automatically and must be uppercase letters and underscores only —
A–Zand_, nothing else. - Optionally add a description.
- Click Create.
The role is created against your current organization, is never a system role, and cannot itself be cloned.
To see who holds a role, click View Users on its card. The list shows each holder, whether the assignment is their primary one, and whether it is active, revoked, or time-limited.
Walkthrough — cloning a system role
Cloning copies a shipped role and its permission set into your organization so you can adjust it without touching the shared original.
- Go to Administration → Roles.
- Find a role marked System — those with a Clone button are cloneable.
- Click Clone, give the copy a display name and machine name, and confirm.
The copy is independent. It records where it came from, but it does not inherit from it: later edits to the original do not reach the copy, and you can remove a permission from the copy that the original grants. (Until this release the clone was wired as a live subclass of its source, which meant a permission could never actually be removed from the copy — it fell back to the inherited one every time.)
You can only clone a role your account owns or a platform-wide system role. Another tenant’s role returns “Role not found”.
Job-function roles and what they drive
Thirteen system roles ship with the platform. They are shared by every tenant, carry a permission set, and are read by real product behaviour.
| Role | Permissions in the catalogue |
|---|---|
SUPER_ADMIN | 241 |
ADMIN | 120 |
FINANCE | 67 |
MANAGER | 35 |
SENIOR_TECHNICIAN | 34 |
| platform administrator | 27 |
SALES | 25 |
DISPATCHER | 21 |
TECHNICIAN | 19 |
ACCOUNTANT | 16 |
CLIENT_ADMIN | 14 |
CLIENT_USER | 8 |
JUNIOR_TECHNICIAN | 8 |
TECHNICIAN is the default role applied to new people.
What holding one of these actually does today:
| Consumer | Behaviour |
|---|---|
| Asset-expiry and contract notifications | Notifies holders of ADMIN / MANAGER / TECHNICIAN |
| Platform staff administration | The platform’s own staff ranks (see section 12) |
| Directory mapping (SCIM / identity provider) | A synced directory group can be mapped to one org-owned custom role |
What it does not do: the individual permission rows attached to a role (ticket:update, invoice:void, and 721 others) are not evaluated when a request is authorized. See Limits — this is the single most important caveat in this document.
Time-limiting and revoking an assignment
Every job-function assignment carries two lifetime controls:
| Control | Effect |
|---|---|
expiresAt | After this moment the assignment stops counting, with no job to run |
isActive | Set false to suspend the assignment without deleting it |
Both are honoured everywhere an assignment is resolved: effective permissions, spend-approval routing, ticket-workflow role conditions, asset-expiry and contract-renewal notification targeting, the RBAC resolver’s user context, and organization-membership resolution. An expired assignment is still listed on the role’s View Users panel, marked Expired, so you can see and clean up what lapsed.
Directory-driven roles (SSO and SCIM)
| Mechanism | What it sets |
|---|---|
OAuthProvider.defaultOrgRole | The organization role given to a person provisioned just-in-time at SSO login |
SCIM group → role mapping (ScimGroup.roleId) | Members of a synced directory group hold an org-owned custom role |
Identity-provider mapping (IdentityProviderMapping.targetRoleId) | Maps an identity provider user or group onto a job-function role |
A directory group may only be mapped to a role the organization owns — never a system or platform staff role.
Platform staff roles
Tridacom’s own staff carry a fifth vocabulary, separate from tenants entirely, managed at /platform/staff.
| Staff role | Rank (lower is more privileged) |
|---|---|
SUPER_ADMIN | 0 |
| platform administrator | 1 |
BILLING_ADMIN | 2 |
SUPPORT_AGENT | 2 |
CATALOG_ADMIN | 2 |
A staff member cannot invite or promote anyone to a rank above their own. The caller’s own rank is resolved from their real assignment rows, not from their token — the token only carries the coarse platform administrator organization role needed for login. A caller with no staff assignment row is treated as platform administrator, the least-privileged rank the platform portal admits.
Account roles
The billing account has its own three-level role, used only for account administration.
| Account role | Can do |
|---|---|
OWNER | Everything, including changing other members’ account roles |
ADMIN | Invite members at ADMIN or MEMBER |
MEMBER | Nothing administrative |
An ADMIN cannot mint an OWNER. The last remaining OWNER cannot be demoted.
Plan tiers
Roles and permissions are not plan-gated. Every tier gets the full role administration surface, custom roles, cloning and assignment. What each tier limits is the number of people you can have.
| Tier (MSP) | Seats per organization | Tier (End customer) | Seats per organization |
|---|---|---|---|
| Free | 5 | Free | 3 |
| Starter | 15 | Essentials | 10 |
| Growth | 25 | Professional | 25 |
| Scale | 50 | Business | 50 |
| Enterprise | Unlimited | Enterprise | Unlimited |
Single sign-on, which is how most organizations drive roles from a directory, is Enterprise-only on both tracks.
Troubleshooting
| What you see | What it means |
|---|---|
Insufficient role | The route’s floor rejected you. If you are an MSP technician on a role-editing screen, that is by design |
Portal access denied | Right role, wrong portal. Switch portals rather than changing the role |
Organization is outside your account | The target organization belongs to another tenant |
Cannot modify platform-wide roles / Cannot delete platform-wide roles | The role has no owning organization. Only a platform admin may touch it |
Cannot modify permissions of system roles | Clone it first, then edit the copy |
Cannot modify inheritance of system roles | A system role’s parent is fixed |
Cannot delete system roles | System roles are shared by every tenant |
Cannot delete role with assigned users | Remove the holders first — deleting would leave people with a dangling assignment |
Cannot delete a role that other roles inherit from | Re-parent or delete the children first |
Role name already exists in this organization | Machine names are unique per organization |
Cannot grant platform-administration permissions: … | Only a platform admin may attach permissions on platform, account, service_tier, permission and the platform commerce resources |
One or more permission ids do not exist | A stale permission id in the request |
Fine-grained permission restrictions are not enforced by any service, so they cannot be saved. … | You tried to save a per-assignment ticket-priority/category/asset-type restriction. Use the role’s scope instead |
| `Reading another person’s permissions requires organization administrator | You may always read your own |
Cannot grant a role higher than your own | The organization-role ceiling, or the account-role ceiling (OWNER › ADMIN › MEMBER) |
Cannot change a person who holds a role higher than your own | Demotion carries the same ceiling as promotion |
You cannot change your own organization role | Access levels are changed by someone else, in both directions |
Cannot remove the last administrator from this organization | Promote a replacement first |
Role not found when cloning | The source role belongs to another tenant, or does not exist |
Limits and known behaviour
Attached permissions are descriptive, not enforced. This is the important one. The catalogue holds 723 permissions across 55 resources, and the 13 system roles carry 635 grants between them — but no service consults those rows when deciding a request. Authorization is the four-step check in section 3: portal, organization role, route floor, tenant scope. Adding invoice:void to a custom role does not let its holders void invoices, and removing it does not stop them. Treat a role’s permission set as documentation of the job function, and use the organization role to control access. The API now refuses to let a non-platform-admin attach platform-administration permissions, so the catalogue cannot become an escalation path if enforcement is later wired up.
Custom roles are created with no permissions. Nothing in the web UI sets permissionIds, so a role created through the portal starts empty. Cloning is the only way to get a populated permission set. Given the point above, this affects the role’s description, not what its holders can do.
Per-assignment restrictions cannot be saved. TICKET_PRIORITY, TICKET_CATEGORY, ASSET_TYPE and REQUIRES_APPROVAL restrictions are modelled and were once persistable, but no service reads them. The API rejects a non-empty restriction list rather than confirm a control it would not apply. Clearing to [] is still accepted so old rows can be removed.
Role changes do not invalidate a live token. The organization role is a token claim, so a demotion takes effect at the next token mint (refresh, portal switch, or re-login). Revoke the person’s sessions if the change must be immediate.
A route that acts on a different organization taken from the URL or body must also constrain its query with accountScope — the role floor alone would let someone privileged in their own organization act on another.
The resolver caches for five minutes, per process. A role’s resolved permission set is memoised in-process for 5 minutes. Editing a role or its permissions clears that cache in the service that handled the edit; other service instances pick the change up when their entry lapses.
The membership role they may set is still capped at their own tier.
Global roles need a platform admin. Only platform administrator can edit or delete a role with no owning organization, including renaming the shared system roles.