Skip to main content

List Permissions

Return every permission granted to the authenticated user, together with the role that each permission comes from. Useful when a client needs to render a UI conditional on many permissions, or when you are debugging why a user can or cannot perform an action.

GET /api/v1/authz/permissions

When to Use This​

  • Render UI elements conditionally on the full permission set.
  • Show the user what they can do on a profile or settings page.
  • Capture effective permissions for audit or compliance.
  • Diagnose authorization behavior — see the exact list that the server uses.

Request​

Attach the user's access token; no request body is needed. The token needs the authz:read scope (or the blanket admin scope). Request it in the authorization request and allow it on the client. Without it, the server answers 403 forbidden with details.required_scopes: ["authz:read"] and the token's details.granted_scopes. An API key (lmk_) cannot hold authz:read, so it always gets this 403. See User API scopes.

curl -X GET https://api.example.com/api/v1/authz/permissions \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response​

{
"user_id": "019d6190-9bba-7b5f-a0a5-07a6d10184d2",
"permissions": [
{
"slug": "document.view",
"description": "View documents",
"source": "role:Viewer"
},
{
"slug": "document.edit",
"description": "Edit documents",
"source": "role:Editor"
},
{
"slug": "document.create",
"description": "Create new documents",
"source": "role:Editor"
},
{
"slug": "user.view",
"description": "View user profiles",
"source": "role:Admin"
},
{
"slug": "user.manage",
"description": "Manage users",
"source": "role:Admin"
}
],
"count": 5
}

Response Fields​

FieldTypeDescription
user_idstring (UUID)ID of the authenticated user
permissionsarrayArray of permission objects
countintegerTotal number of permissions
data, paginationarray, objectThe same list again in the envelope that every LumoAuth list endpoint uses (data holds the permission objects, pagination the page metadata)

Permission Object​

FieldTypeDescription
slugstringUnique permission identifier (e.g., document.edit)
descriptionstringHuman-readable description
sourcestringThe role that granted this permission, always formatted role:<RoleName>
Permission Sources

The endpoint returns the user's effective permissions resolved through their roles, including roles inherited from group membership. The source label always names the granting role (role:<RoleName>); there is no separate group: source. A permission granted by more than one role appears once per granting role.

Examples​

Building a Navigation Menu​

async function buildNavigation() {
const response = await fetch('/api/v1/authz/permissions', {
headers: { 'Authorization': `Bearer ${accessToken}` }
});

const { permissions } = await response.json();
const permSet = new Set(permissions.map(p => p.slug));

const navItems = [];

// Always show dashboard
navItems.push({ label: 'Dashboard', href: '/dashboard' });

// Conditionally show based on permissions
if (permSet.has('document.view')) {
navItems.push({ label: 'Documents', href: '/documents' });
}

if (permSet.has('user.view') || permSet.has('user.manage')) {
navItems.push({ label: 'Users', href: '/users' });
}

if (permSet.has('settings.manage')) {
navItems.push({ label: 'Settings', href: '/settings' });
}

return navItems;
}

Caching the Permission Set​

class PermissionCache:
"""Cache user permissions for efficient checking"""

def __init__(self, access_token):
self.token = access_token
self._permissions = None

def load(self):
"""Fetch and cache all permissions"""
response = requests.get(
f'{LUMOAUTH_URL}/api/v1/authz/permissions',
headers={'Authorization': f'Bearer {self.token}'}
)
data = response.json()
self._permissions = {p['slug'] for p in data['permissions']}
return self._permissions

def has(self, permission):
"""Check if user has a permission"""
if self._permissions is None:
self.load()
return permission in self._permissions

def has_any(self, permissions):
"""Check if user has any of the permissions"""
if self._permissions is None:
self.load()
return bool(self._permissions & set(permissions))

# Usage
perms = PermissionCache(access_token)
if perms.has('document.delete'):
delete_document(doc_id)
Batch Checks

When you only need a handful of specific permissions for a page, use /api/v1/authz/check-bulk instead of downloading the full list.

System permissions you will see​

Besides the permissions that you define, every organization is seeded with system permissions. They appear in this endpoint like any other slug. The ones that matter for AI-agent features:

SlugGrantsHeld by default
jit.approveApprove or deny just-in-time permission requests raised by agents (portal inbox, REST, mobile, approval channels). The agent's owner and the on-behalf-of user are still refused for their own requests.Organization admins
agents.viewSee the agent list, agent policies and security-signals pagesOrganization admins
agents.create / agents.edit / agents.deleteRegister, change (including creating and toggling access policies) and remove agentsOrganization admins
policies.view / policies.create / policies.edit / policies.deleteManage AI policiesOrganization admins
settings.manageAuthentication and security policy plus organization infrastructure (API keys, custom domains, integrations), including security-signal streams and receivers and approval channels. It is not a blanket admin grant.Organization admins
authz.checkAsk authorization questions about other subjects: check, AuthZEN (the standard "may this subject do this action on this resource?" API), Zanzibar check and expandOrganization admins

jit.approve, the agents.* and policies.* write permissions (create, edit, delete) and settings.manage count as admin-equivalent. A role that grants any of them is treated like a system role for assignment safety. To let someone review agent requests without making them an admin, create a role that grants only jit.approve. See Who can approve.

Agent principals

An agent authenticated with an agk_ API key or an agent access token can call /api/v1/authz/check* for its own subject. Those credentials are stateless and are accepted only on agent, JIT and authz-check routes. See Agent API keys.