Custom Roles
Define granular roles with permission flags beyond the default admin/user binary.
Atlas supports custom role definitions with granular permission flags. Move beyond the default admin/member/owner hierarchy by creating named roles with specific permission sets.
SaaS Feature
Custom roles are available on app.useatlas.dev Enterprise plans. Self-hosted deployments use the built-in admin/member roles.
Prerequisites
- Managed auth enabled
- Internal database configured (
DATABASE_URL) - Active Enterprise plan on app.useatlas.dev
- Admin role required for all role management endpoints
How It Works
- Atlas ships with three built-in roles: admin, analyst, and viewer
- Admins create custom roles via the admin API or the admin UI
- Each role is a set of permission flags (additive model)
- Users are assigned roles via the organization membership system
- Permission checks happen at request time, falling back to legacy behavior when enterprise is not enabled
Permission Flags
| Permission | Description |
|---|---|
query | Can send chat queries |
query:raw_data | Can see raw row data (vs aggregates only) |
admin:users | Can manage users |
admin:connections | Can manage data connections |
admin:settings | Can manage application settings |
admin:audit | Can view audit logs |
admin:roles | Can manage roles |
admin:semantic | Can edit the semantic layer |
Permissions are additive — a role is the union of its flags. There is no "deny" mechanism.
Built-in Roles
Three roles are seeded automatically and cannot be modified or deleted:
| Role | Permissions | Use Case |
|---|---|---|
admin | All permissions | Full administrative access |
analyst | query, query:raw_data, admin:audit | Data team members who need raw data and audit visibility |
viewer | query | Stakeholders who only need aggregate query results |
Admin UI
Navigate to Admin Console > Roles to manage roles visually:
- View built-in and custom roles with their permission badges
- Create new roles with a permission checkbox dialog
- Edit custom role descriptions and permissions
- Delete custom roles (built-in roles show a lock icon)
API Reference
All endpoints are mounted under /api/v1/admin/roles and require admin authentication.
List Roles
GET /api/v1/admin/rolesReturns all roles (built-in + custom) for the active organization, along with the list of available permissions.
{
"roles": [
{
"id": "...",
"orgId": "...",
"name": "admin",
"description": "Full access to all features and administration",
"permissions": ["query", "query:raw_data", "admin:users", "..."],
"isBuiltin": true,
"createdAt": "2026-01-01T00:00:00Z",
"updatedAt": "2026-01-01T00:00:00Z"
}
],
"permissions": ["query", "query:raw_data", "admin:users", "..."],
"total": 3
}Create Role
POST /api/v1/admin/roles
Content-Type: application/json
{
"name": "data-engineer",
"description": "Can query and manage connections",
"permissions": ["query", "query:raw_data", "admin:connections"]
}Role names must be lowercase, start with a letter, and contain only letters, numbers, hyphens, or underscores (1-63 characters).
Update Role
PUT /api/v1/admin/roles/:id
Content-Type: application/json
{
"description": "Updated description",
"permissions": ["query", "admin:connections", "admin:semantic"]
}Built-in roles cannot be modified. Attempting to update a built-in role returns 403.
Delete Role
DELETE /api/v1/admin/roles/:idBuilt-in roles cannot be deleted. Attempting to delete a built-in role returns 403.
List Role Members
GET /api/v1/admin/roles/:id/membersReturns users assigned to the specified role in the organization.
Assign Role to User
PUT /api/v1/admin/roles/users/:userId/role
Content-Type: application/json
{
"role": "data-engineer"
}The role name must match an existing role (built-in or custom) in the organization.
Backward Compatibility
Custom roles are fully backward compatible with the existing auth system:
- No enterprise license? The legacy
admin/member/ownerroles continue to work exactly as before.adminandownerget all permissions;membergets query permissions. - Enterprise enabled but no custom roles assigned? Users fall back to their existing role's default permissions.
- Mixed deployment? The permission resolver checks for custom roles first, then falls back to the legacy mapping.
Integration with Permission Checks
The checkPermission function can be used in route handlers to enforce fine-grained access:
import { checkPermission } from "ee/src/auth/roles";
// In a route handler:
const denied = await checkPermission(user, "admin:connections", requestId);
if (denied) {
return c.json(denied.body, denied.status);
}The hasPermission function returns a boolean for simpler conditional checks:
import { hasPermission } from "ee/src/auth/roles";
if (await hasPermission(user, "admin:semantic")) {
// User can edit semantic layer
}