Roles & Permissions API
Heimdall provides full CRUD for roles and permissions via both GraphQL and REST APIs.
GraphQL
Queries
List Roles
query {
roles {
id
name
description
isSystem
requiresTwoFactor
level
createdAt
updatedAt
}
}
Get Role with Permissions
query {
roleWithPermissions(id: "role_admin") {
id
name
description
isSystem
requiresTwoFactor
permissions {
id
resource
action
description
adminOnly
displayOrder
}
}
}
List Permissions
query {
permissions {
id
resource
action
description
adminOnly
displayOrder
createdAt
updatedAt
}
}
Mutations
Create Role
Permission required: roles:write
mutation {
createRole(input: { name: "Editor", description: "Can edit content" }) {
id
name
description
}
}
Update Role
Permission required: roles:write
System roles cannot be updated.
mutation {
updateRole(id: "role_editor", input: { description: "Updated description" }) {
id
name
description
}
}
Delete Role
Permission required: roles:delete
System roles cannot be deleted.
mutation {
deleteRole(id: "role_editor")
}
Assign Permission to Role
Permission required: roles:write
Assigns a permission to a role. Automatically invalidates permission caches and broadcasts updates via WebSocket to affected users.
mutation {
assignRolePermission(roleId: "role_editor", permissionId: "perm_content_write")
}
Remove Permission from Role
Permission required: roles:write
mutation {
removeRolePermission(roleId: "role_editor", permissionId: "perm_content_write")
}
Create Permission
Permission required: permissions:write
mutation {
createPermission(input: {
resource: "content"
action: "write"
description: "Can write content"
adminOnly: false
displayOrder: 10
}) {
id
resource
action
description
}
}
Update Permission
Permission required: permissions:write
mutation {
updatePermission(id: "perm_content_write", input: { description: "Updated" }) {
id
resource
action
description
}
}
Delete Permission
Permission required: permissions:delete
Cannot delete permissions that are still assigned to roles.
mutation {
deletePermission(id: "perm_content_write")
}
User Role Management
Get User Roles
Permission required: users:read
query {
userRoles(userId: "user-123") {
id
name
description
isSystem
requiresTwoFactor
level
createdAt
updatedAt
}
}
Assign Role to User
Permission required: users:write
Assigns a role to a user. Automatically invalidates permission caches and broadcasts roles_updated + permissions_updated via WebSocket.
Role hierarchy enforcement: You can only assign roles with a level strictly below your own highest role level. Super Admins bypass this check. Non-assignable roles (role_system, role_api_full_access, role_api_read_only) are always blocked.
mutation {
assignUserRole(userId: "user-123", roleId: "role_editor")
}
Remove Role from User
Permission required: users:write
Role hierarchy enforcement: You can only remove roles with a level strictly below your own highest role level. Super Admins bypass this check.
mutation {
removeUserRole(userId: "user-123", roleId: "role_editor")
}
REST API
Roles
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /v1/roles | roles:read | List all roles |
| GET | /v1/roles/{id} | roles:read | Get role by ID |
| POST | /v1/roles | roles:write | Create role |
| PATCH | /v1/roles/{id} | roles:write | Update role |
| DELETE | /v1/roles/{id} | roles:delete | Delete role |
| POST | /v1/roles/{id}/permissions | roles:write | Assign permission |
| DELETE | /v1/roles/{id}/permissions/{permId} | roles:write | Remove permission |
Permissions
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /v1/permissions | permissions:read | List all permissions |
| POST | /v1/permissions | permissions:write | Create permission |
| PATCH | /v1/permissions/{id} | permissions:write | Update permission |
| DELETE | /v1/permissions/{id} | permissions:delete | Delete permission |
REST Examples
Create Role
curl -X POST https://api.elcto.com/v1/roles \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Editor", "description": "Can edit content"}'
Assign Permission to Role
curl -X POST https://api.elcto.com/v1/roles/role_editor/permissions \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"permission_id": "perm_content_write"}'
Audit Events
All role and permission mutations are logged as audit events:
| Event | Description |
|---|---|
role_created | A new role was created |
role_updated | A role was updated |
role_deleted | A role was deleted |
role_permission_assigned | A permission was assigned to a role |
role_permission_removed | A permission was removed from a role |
permission_created | A new permission was created |
permission_updated | A permission was updated |
permission_deleted | A permission was deleted |
role_assigned | A role was assigned to a user |
role_removed | A role was removed from a user |
Cache & Real-time Updates
When permissions are assigned/removed from roles:
- Redis cache invalidation — Permission caches for all affected users and API keys are cleared
- WebSocket broadcast —
permissions_updatedmessages are sent to affected users so frontends can refresh permissions in real-time
Types
Role
interface Role {
id: string;
name: string;
description: string | null;
isSystem: boolean;
requiresTwoFactor: boolean;
/** Hierarchy level (higher = more privileged) */
level: number;
createdAt: string;
updatedAt: string;
}
Default Role Levels
| Role | Level |
|---|---|
| Super Admin | 100 |
| Admin | 80 |
| Moderator | 60 |
| Developer | 40 |
| User | 20 |
| System/API roles | 0 (non-assignable) |
Permission
interface Permission {
id: string;
resource: string;
action: string;
description: string | null;
adminOnly: boolean;
displayOrder: number;
createdAt: string;
updatedAt: string;
}
RoleWithPermissions
interface RoleWithPermissions extends Role {
permissions: Permission[];
}