Skip to main content

Permissions

GET /orgs/{orgId}/api/v1/admin/permissions
POST /orgs/{orgId}/api/v1/admin/permissions
GET /orgs/{orgId}/api/v1/admin/permissions/{permissionId}
PATCH /orgs/{orgId}/api/v1/admin/permissions/{permissionId}
DELETE /orgs/{orgId}/api/v1/admin/permissions/{permissionId}
GET /orgs/{orgId}/api/v1/admin/permissions/{permissionId}/usage
GET /orgs/{orgId}/api/v1/admin/scopes
POST /orgs/{orgId}/api/v1/admin/scopes
DELETE /orgs/{orgId}/api/v1/admin/scopes/{scopeId}

Permissions are fine-grained access controls identified by a slug (e.g., repo.write). They can be assigned directly to users, groups, or roles.

Authentication​

All permission and scope endpoints require an API key or a Bearer token with admin:permissions:read (list, get, usage) or admin:permissions:write (create, update, delete). See Authentication.

Each endpoint needs the credential's scope and a permission. A session or Bearer caller must hold the permission; for an API key its creator must still hold it. A missing permission returns 403 forbidden with details.required_permissions. See Required permissions.

MethodEndpointPermissionAPI-key / token scope
GET/admin/permissionsroles.viewadmin:permissions:read
GET/admin/permissions/{permissionId}roles.viewadmin:permissions:read
POST/admin/permissionsroles.assign-permissionsadmin:permissions:write
PATCH/admin/permissions/{permissionId}roles.assign-permissionsadmin:permissions:write
DELETE/admin/permissions/{permissionId}roles.assign-permissionsadmin:permissions:write
GET/admin/permissions/{permissionId}/usageroles.viewadmin:permissions:read
GET/admin/scopesroles.viewadmin:permissions:read
POST/admin/scopesroles.assign-permissionsadmin:permissions:write
DELETE/admin/scopes/{scopeId}roles.assign-permissionsadmin:permissions:write

The Permission Object​

Permission Object
{
"id": 42,
"slug": "repo.write",
"name": "Write to Repository",
"description": "Allows writing to code repositories",
"resource": "repos",
"action": "write",
"isSystem": false,
"isCustom": true,
"createdAt": "2026-01-15T10:30:00Z"
}

isCustom is true when the permission is organization-defined (rather than a built-in system permission).

System permissions (isSystem: true) are the same in every organization. They include the admin catalogue (users.*, roles.*, settings.manage, zanzibar.*, abac.view, abac.manage, gdpr.manage, gdpr.dpo, billing.manage, authz.check, and so on) and jit.approve, which lets a user approve or deny just-in-time permission requests raised by AI agents in the approval inbox. Organization administrators hold jit.approve through the built-in admin role.

List Permissions​

List Permissions
curl "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/permissions?page=1&limit=50" \
-H "Authorization: ApiKey lmk_abc123"

Query Parameters​

ParameterDescription
pagePage number (default: 1)
limitResults per page (default: 25, max: 100)
searchFilter by name, slug, or description
resourceFilter by the permission's resource value
sortBy, sortDirSort field and direction (asc/desc)
List Response
{
"data": [
{
"id": 42,
"slug": "repo.write",
"name": "Write to Repository",
"description": "Allows writing to code repositories",
"resource": "repos",
"action": "write",
"isSystem": false,
"isCustom": true,
"createdAt": "2026-01-15T10:30:00Z"
}
],
"meta": {
"total": 18,
"page": 1,
"limit": 25,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
},
"resourceType": "permissions"
}

Create Permission​

Create a Permission
curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/permissions \
-H "Authorization: ApiKey lmk_abc123" \
-H "Content-Type: application/json" \
-d '{
"slug": "repo.write",
"name": "Write to Repository",
"description": "Allows writing to code repositories",
"resource": "repos",
"action": "write"
}'

Slug rules​

The slug is validated before anything is written. A slug is accepted only if it:

  • uses lowercase letters and digits, separated by single ., - or _ characters (pattern ^[a-z0-9]+(?:[._-][a-z0-9]+)*$, at most 100 characters), for example reports.view or repo-write;
  • contains no wildcard (*, **, users.*), because a wildcard slug would match every permission in checks;
  • is not the slug of a built-in system permission (for example users.view, settings.manage, jit.approve), does not sit inside a system namespace (any slug starting with a system resource such as users., billing., gdpr. or abac., for example users.impersonate) and does not start with tenants., which is reserved for platform administration;
  • is not a retired slug the platform once checked: agents.manage, users.manage, zanzibar.manage, webhooks.test, gdpr.export_other, abac.policies.* and abac.attributes.*.

The same reserved list applies in the portal and to AI policy authoring. Custom slugs are unique per organization (another organization's custom permissions are neither visible nor blocking); reserving the system slugs and namespaces stops an organization from pre-empting a permission the platform seeds later.

ProblemResponse
Malformed, wildcard, too long or reserved slug400 invalid_request; message explains the rule
Slug already exists in this organization or as a system permission409 conflict

The colon form repo:write is not accepted; use repo.write or repo-write. The permission resource and action fields are free text and do not have to match the slug.

Update Permission​

Update a Permission
curl -X PATCH https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/permissions/42 \
-H "Authorization: ApiKey lmk_abc123" \
-H "Content-Type: application/json" \
-d '{
"description": "Allows writing and force-pushing to code repositories"
}'

Get Permission Usage​

Returns the roles that have been assigned this permission.

Get Permission Usage
curl "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/permissions/42/usage" \
-H "Authorization: ApiKey lmk_abc123"
Usage Response
{
"data": {
"roles": [
{ "id": 17, "name": "developer", "slug": "developer" }
],
"roleCount": 1
}
}

Delete Permission​

Delete a Permission
curl -X DELETE "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/permissions/42" \
-H "Authorization: ApiKey lmk_abc123"
warning

System permissions (isSystem: true) cannot be deleted.

OAuth 2.0 Scopes​

Custom scopes are distinct from permissions — they are included in OAuth access tokens and control API access granted to OAuth clients.

Scope names are unique per organization. POST /admin/scopes returns 409 conflict when the name already exists in this organization or matches a system scope (openid, profile, email, admin, admin:users:read, and so on). Two organizations may define the same custom scope name independently. See Scopes for the full catalogue.

List Scopes​

List Custom Scopes
curl "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/scopes" \
-H "Authorization: ApiKey lmk_abc123"

Create Scope​

Create a Scope
curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/scopes \
-H "Authorization: ApiKey lmk_abc123" \
-H "Content-Type: application/json" \
-d '{
"name": "read:reports",
"description": "Read financial reports",
"isDefault": false
}'
409 Conflict
{
"error": "conflict",
"message": "A scope with this name already exists",
"status": 409
}

Delete Scope​

Delete a Scope
curl -X DELETE "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/scopes/7" \
-H "Authorization: ApiKey lmk_abc123"