Groups, Roles & Permissions
Roles, groups, and permissions in Arthur work through a hierarchical role-based access control (RBAC) model — and if your organization uses an identity provider (IDP) like Okta or Azure AD, you can configure IDP group mapping so that adding a user to an IDP group automatically grants them the correct Arthur access, with no manual steps required inside Arthur. This page explains how the permission model works, what roles are available, and how to configure IDP-managed group membership for your organization.
Overview
Managing access for dozens or hundreds of users one-by-one doesn't scale. Arthur's permission system is designed to solve this with three complementary mechanisms:
- Roles — named sets of permissions (e.g.,
admin,member,viewer) that define what a principal can do. - Role bindings — assignments that attach a role to a user or group at a specific scope (org, workspace, or project).
- Groups — collections of users that can receive a single role binding, so you manage membership in one place rather than updating individual bindings.
When you combine groups with IDP group mapping, your identity provider becomes the single source of truth for Arthur access. A user added to the arthur-data-scientists Okta group is automatically placed in the corresponding Arthur group and inherits its role bindings — no Arthur admin action required.
flowchart TD
IDP["Identity Provider (Okta / Azure AD)"]
AG["Arthur Group"]
RB["Role Binding (e.g., Workspace Member)"]
U["User"]
IDP -- "IDP group mapping" --> AG
AG -- "has role binding" --> RB
U -- "member of IDP group" --> IDP
U -- "inherits permissions" --> RB
The Arthur Permission Model
Arthur uses a three-level hierarchy. Permissions granted at a higher level flow down unless overridden at a lower level.
graph TD
ORG["Organization (top level)"]
WS["Workspace (groups related projects)"]
PROJ["Project (individual AI model or agent)"]
ORG --> WS
WS --> PROJ
| Level | Scope | Typical use |
|---|---|---|
| Organization | All workspaces and projects in the tenant | Org-wide admins, billing contacts |
| Workspace | All projects within a workspace | Team leads, ML engineers working on a product area |
| Project | A single model or agent | External reviewers, limited-access contractors |
A role binding at the organization level grants access across every workspace and project. A role binding at the workspace level grants access only within that workspace and its projects. A role binding at the project level is the most narrowly scoped.
Inheritance note: A user with an org-level role automatically has at least that level of access in every workspace and project. You can grant a more permissive role at a lower level (e.g., a workspace
adminwho is only an orgmember), but you cannot restrict an org-level role at a lower scope.
Built-In Roles
Arthur ships with a set of built-in roles. You can list all available roles via the API:
roles = arthur_client.get("/api/v1/organization/roles")
print(roles.json())const response = await fetch("https://<your-arthur-host>/api/v1/organization/roles", {
method: "GET",
headers: {
"Authorization": "Bearer <your-token>",
"Content-Type": "application/json"
}
});
const roles = await response.json();
console.log(roles);curl -X GET https://<your-arthur-host>/api/v1/organization/roles \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json"The roles returned by this endpoint are the authoritative list for your deployment. Built-in roles are organized by scope:
Organization-scoped roles (bindable at the org level):
| Role | Description |
|---|---|
Organization Super Admin | Full read and write access to all resources and configurations in the account — org, all workspaces, and all projects |
Organization Admin | Read and write access to the organization and its configuration. Does not grant access to workspace or project resources |
Organization Read All | Full read access to all resources in the account — org, all workspaces, and all projects |
Organization Reader | Read access to the organization and its configuration. Does not grant access to workspace or project resources |
Organization Member | Minimal read access that allows a user to view the basic Arthur UI |
Workspace-scoped roles (bindable at the workspace level):
| Role | Description |
|---|---|
Workspace Super Admin | Full read and write access to all resources and configurations in a workspace and its projects |
Workspace Admin | Read and write access to a workspace and its configuration. Does not grant access to project resources or engine management |
Workspace Read All | Full read access to a workspace and all projects within it |
Workspace Reader | Read access to a workspace and its configuration. Does not grant access to project resources |
Governance Admin | Access to view and manage workspace governance features |
Custom Aggregation Manager | Read and write access to all custom aggregations in a workspace |
Engine Manager | Read access to a workspace plus write access to engines in the workspace |
Project-scoped roles (bindable at the project level):
| Role | Description |
|---|---|
Project Admin | Read and write access to a project and all its resources |
Project Reader | Read access to a project and its resources. Does not grant access to raw dataset data |
Multi-scope role (bindable at org, workspace, or project level):
| Role | Description |
|---|---|
Raw Data Reader | Read access to view raw dataset data within the bound scope |
Use
GET /api/v1/permissionsto retrieve the full list of granular permissions, andPOST /api/v1/permissions/checkto programmatically verify whether a principal has a specific permission.
Groups and Bulk Access Management
A group is a named collection of users. Instead of creating one role binding per user, you create a single role binding for the group and manage membership separately. This is especially powerful when combined with IDP group mapping (see below).
Why use groups?
- Onboarding: Add a new hire to the
ml-engineersgroup in your IDP — they immediately have the right Arthur access. - Offboarding: Remove a user from the IDP group — their Arthur access is revoked automatically.
- Auditing: Review a group's role bindings in one API call rather than scanning individual user bindings.
View a group's role bindings
group_id = "grp_abc123"
bindings = arthur_client.get(f"/api/v1/groups/{group_id}/role_bindings")
print(bindings.json())const groupId = "grp_abc123";
const response = await fetch(
`https://<your-arthur-host>/api/v1/groups/${groupId}/role_bindings`,
{
method: "GET",
headers: {
"Authorization": "Bearer <your-token>",
"Content-Type": "application/json"
}
}
);
const bindings = await response.json();
console.log(bindings);curl -X GET https://<your-arthur-host>/api/v1/groups/grp_abc123/role_bindings \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json"Assign Roles at Org, Workspace, and Project Level
Role bindings connect a principal (user or group) to a role at a specific scope. The following examples show how to create role bindings at each level.
Organization-level role binding
import json
payload = {
"principal_id": "grp_abc123", # group or user ID
"principal_type": "group", # "user" or "group"
"role": "Organization Member"
}
response = arthur_client.post(
"/api/v1/organization/role_bindings",
data=json.dumps(payload)
)
print(response.json())const response = await fetch(
"https://<your-arthur-host>/api/v1/organization/role_bindings",
{
method: "POST",
headers: {
"Authorization": "Bearer <your-token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
principal_id: "grp_abc123",
principal_type: "group",
role: "Organization Member"
})
}
);
const binding = await response.json();
console.log(binding);curl -X POST https://<your-arthur-host>/api/v1/organization/role_bindings \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"principal_id": "grp_abc123",
"principal_type": "group",
"role": "Organization Member"
}'Workspace-level role binding
import json
workspace_id = "ws_xyz789"
payload = {
"principal_id": "grp_abc123",
"principal_type": "group",
"role": "Workspace Admin"
}
response = arthur_client.post(
f"/api/v1/workspaces/{workspace_id}/role_bindings",
data=json.dumps(payload)
)
print(response.json())const workspaceId = "ws_xyz789";
const response = await fetch(
`https://<your-arthur-host>/api/v1/workspaces/${workspaceId}/role_bindings`,
{
method: "POST",
headers: {
"Authorization": "Bearer <your-token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
principal_id: "grp_abc123",
principal_type: "group",
role: "Workspace Admin"
})
}
);
const binding = await response.json();
console.log(binding);curl -X POST https://<your-arthur-host>/api/v1/workspaces/ws_xyz789/role_bindings \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"principal_id": "grp_abc123",
"principal_type": "group",
"role": "Workspace Admin"
}'Project-level role binding
import json
project_id = "proj_def456"
payload = {
"principal_id": "usr_reviewer01",
"principal_type": "user",
"role": "Project Reader"
}
response = arthur_client.post(
f"/api/v1/projects/{project_id}/role_bindings",
data=json.dumps(payload)
)
print(response.json())const projectId = "proj_def456";
const response = await fetch(
`https://<your-arthur-host>/api/v1/projects/${projectId}/role_bindings`,
{
method: "POST",
headers: {
"Authorization": "Bearer <your-token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
principal_id: "usr_reviewer01",
principal_type: "user",
role: "Project Reader"
})
}
);
const binding = await response.json();
console.log(binding);curl -X POST https://<your-arthur-host>/api/v1/projects/proj_def456/role_bindings \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"principal_id": "usr_reviewer01",
"principal_type": "user",
"role": "Project Reader"
}'Remove a role binding
To revoke access, delete the role binding by its ID:
role_binding_id = "rb_001"
arthur_client.delete(f"/api/v1/role_bindings/{role_binding_id}")const roleBindingId = "rb_001";
await fetch(
`https://<your-arthur-host>/api/v1/role_bindings/${roleBindingId}`,
{
method: "DELETE",
headers: { "Authorization": "Bearer <your-token>" }
}
);curl -X DELETE https://<your-arthur-host>/api/v1/role_bindings/rb_001 \
-H "Authorization: Bearer <your-token>"IDP Group Mapping
IDP group mapping is the feature that makes large-scale access management practical. Once configured, Arthur listens to group membership claims in your IDP's tokens and automatically syncs users into the corresponding Arthur groups — no manual Arthur admin action required.
How it works
sequenceDiagram
participant Admin as IT Admin
participant IDP as Identity Provider (Okta)
participant Arthur as Arthur Platform
Admin->>IDP: Add user to "arthur-ml-engineers" group
Note over IDP: User's token now includes group claim
IDP->>Arthur: User authenticates (SSO)
Arthur->>Arthur: Read group claims from token
Arthur->>Arthur: Sync user into mapped Arthur group
Arthur->>Arthur: Apply group's role bindings
Note over Arthur: User has workspace Member access
Configuration steps
Prerequisites: You must be an
org_adminand have SSO already configured for your organization. Contact your Arthur account team if SSO is not yet enabled.
Step 1 — Identify your IDP groups
Decide which IDP groups should map to Arthur access. A common pattern:
| IDP Group | Arthur Group | Role Binding |
|---|---|---|
arthur-admins | Arthur Admins | Org Organization Admin |
arthur-ml-engineers | ML Engineers | Workspace Workspace Reader on Production workspace |
arthur-reviewers | Model Reviewers | Project Project Reader on specific projects |
Step 2 — Create Arthur groups
Create a group in Arthur for each IDP group you want to map. Note the returned group_id — you will need it for the mapping configuration.
Step 3 — Configure the IDP group claim
In your IDP (e.g., Okta), ensure that group membership is included as a claim in the SAML assertion or OIDC token sent to Arthur. The claim name (e.g., groups) must match what Arthur is configured to read.
Work with your Arthur account team or platform administrator to set the expected claim name in Arthur's SSO configuration.
Step 4 — Map IDP group names to Arthur groups
In Arthur's admin settings, create a mapping entry that links the IDP group name (as it appears in the token claim) to the Arthur group ID.
Step 5 — Assign role bindings to the Arthur group
Once the mapping is in place, assign role bindings to the Arthur group using the API calls shown in the Assign Roles section above. These bindings are inherited by every user who is a member of the mapped IDP group.
Step 6 — Test the mapping
- Add a test user to the IDP group.
- Have the test user log in to Arthur via SSO.
- Verify the user appears in the Arthur group and has the expected access.
# Verify the group's role bindings are correct
group_id = "grp_ml_engineers"
bindings = arthur_client.get(f"/api/v1/groups/{group_id}/role_bindings")
print(bindings.json())
# Verify a specific user's effective role bindings
user_id = "usr_test_user"
user_bindings = arthur_client.get(f"/api/v1/users/{user_id}/role_bindings")
print(user_bindings.json())// Verify the group's role bindings
const groupResponse = await fetch(
"https://<your-arthur-host>/api/v1/groups/grp_ml_engineers/role_bindings",
{ headers: { "Authorization": "Bearer <your-token>" } }
);
console.log("Group bindings:", await groupResponse.json());
// Verify a specific user's effective role bindings
const userResponse = await fetch(
"https://<your-arthur-host>/api/v1/users/usr_test_user/role_bindings",
{ headers: { "Authorization": "Bearer <your-token>" } }
);
console.log("User bindings:", await userResponse.json());# Verify the group's role bindings
curl -X GET https://<your-arthur-host>/api/v1/groups/grp_ml_engineers/role_bindings \
-H "Authorization: Bearer <your-token>"
# Verify a specific user's effective role bindings
curl -X GET https://<your-arthur-host>/api/v1/users/usr_test_user/role_bindings \
-H "Authorization: Bearer <your-token>"Checking a specific permission
You can programmatically verify whether a principal has a specific permission using the permissions check endpoint:
import json
payload = {
"principal_id": "usr_test_user",
"principal_type": "user",
"permission": "project:read",
"resource_id": "proj_def456",
"resource_type": "project"
}
result = arthur_client.post(
"/api/v1/permissions/check",
data=json.dumps(payload)
)
print(result.json()) # {"allowed": true}const response = await fetch(
"https://<your-arthur-host>/api/v1/permissions/check",
{
method: "POST",
headers: {
"Authorization": "Bearer <your-token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
principal_id: "usr_test_user",
principal_type: "user",
permission: "project:read",
resource_id: "proj_def456",
resource_type: "project"
})
}
);
const result = await response.json();
console.log(result); // { allowed: true }curl -X POST https://<your-arthur-host>/api/v1/permissions/check \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"principal_id": "usr_test_user",
"principal_type": "user",
"permission": "project:read",
"resource_id": "proj_def456",
"resource_type": "project"
}'Manage Permissions in the UI
All of the operations described above are also available in the Arthur platform UI, which is the recommended starting point for most admins.
Organization-level access
- Navigate to Settings → Organization → Members.
- Use the Groups tab to create and manage groups.
- Use the Role Bindings tab to assign org-level roles to users or groups.
- Use the IDP Mapping tab to configure IDP group → Arthur group mappings.
Workspace-level access
- Open the workspace from the left navigation.
- Navigate to Workspace Settings → Access.
- Click Add Role Binding to assign a role to a user or group within this workspace.
Project-level access
- Open the project.
- Navigate to Project Settings → Access.
- Click Add Role Binding to assign a narrowly scoped role for this project only.
Tip: The UI shows the effective permissions for each user, including permissions inherited from group memberships and higher-level scopes. Use this view to audit access before making changes.
Next Steps
Now that you understand how roles, groups, and permissions work in Arthur, here are the recommended next steps for your organization:
| Task | Where to go |
|---|---|
| Set up SSO for your organization | Settings → Organization → SSO Configuration |
| Create your first workspace and assign a team | Workspaces guide |
| Invite users and assign them to groups | Settings → Organization → Members |
| Configure alert notifications for your team | Alert Rules guide |
| Audit current access across your org | Use GET /api/v1/organization/role_bindings and GET /api/v1/workspaces/{workspace_id}/role_bindings |
For questions about SSO configuration or IDP group mapping setup, contact your Arthur account team or open a support ticket from the platform.
Updated about 22 hours ago