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. jit.approve 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, that is, any slug starting with a system resource such as users., billing., gdpr. or abac. (for example users.impersonate);
  • does not start with tenants., which is reserved for platform administration;
  • is not a retired slug that 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 the authoring of AI policies. 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 creating in advance a permission that 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"