Roles
GET /orgs/{orgId}/api/v1/admin/roles
POST /orgs/{orgId}/api/v1/admin/roles
GET /orgs/{orgId}/api/v1/admin/roles/{roleId}
PUT /orgs/{orgId}/api/v1/admin/roles/{roleId}
DELETE /orgs/{orgId}/api/v1/admin/roles/{roleId}
GET /orgs/{orgId}/api/v1/admin/roles/{roleId}/permissions
POST /orgs/{orgId}/api/v1/admin/roles/{roleId}/permissions
DELETE /orgs/{orgId}/api/v1/admin/roles/{roleId}/permissions/{permissionId}
GET /orgs/{orgId}/api/v1/admin/roles/{roleId}/users
POST /orgs/{orgId}/api/v1/admin/roles/{roleId}/users
DELETE /orgs/{orgId}/api/v1/admin/roles/{roleId}/users/{userId}
Roles group permissions together and can be assigned to users or groups. When a user authenticates, their active roles are embedded in the access token.
Authentication
All role management endpoints require an admin API key or a Bearer token with admin:roles:read / admin:roles:write.
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.
| Method | Endpoint | Permission | API-key / token scope |
|---|---|---|---|
| GET | /admin/roles | roles.view | admin:roles:read |
| GET | /admin/roles/{roleId} | roles.view | admin:roles:read |
| POST | /admin/roles | roles.create | admin:roles:write |
| PUT PATCH | /admin/roles/{roleId} | roles.edit | admin:roles:write |
| DELETE | /admin/roles/{roleId} | roles.delete | admin:roles:write |
| GET | /admin/roles/{roleId}/permissions | roles.view | admin:roles:read |
| PUT | /admin/roles/{roleId}/permissions | roles.assign-permissions | admin:roles:write |
| POST | /admin/roles/{roleId}/permissions | roles.assign-permissions | admin:roles:write |
| DELETE | /admin/roles/{roleId}/permissions/{permissionId} | roles.assign-permissions | admin:roles:write |
| GET | /admin/roles/{roleId}/users | roles.view | admin:roles:read |
| POST | /admin/roles/{roleId}/users | roles.edit | admin:roles:write |
| DELETE | /admin/roles/{roleId}/users/{userId} | roles.edit | admin:roles:write |
The list includes platform-wide (system) roles as well as the roles your organization created.
Only roles your organization owns can be changed: updating, deleting or editing the permissions
of a platform-wide role returns 404, the same as a missing role. For a platform-wide role,
userCount and GET /admin/roles/{roleId}/users count and list only your organization's
users.
The Role Object
Single-role responses are wrapped in a data envelope. permissions is an array of objects (not slug strings); it and updatedAt are returned only on the single-role GET /roles/{roleId} (detailed) response — list rows omit them. updatedAt is null until the role is first updated.
{
"data": {
"id": "17",
"name": "developer",
"slug": "developer",
"description": "Can read and write code repositories",
"isSystem": false,
"userCount": 12,
"createdAt": "2026-01-15T10:30:00Z",
"permissions": [
{
"id": "16",
"name": "View Users",
"slug": "users.view",
"resource": "users",
"action": "view"
}
],
"updatedAt": null
}
}
List Roles
- curl
- Node
- Python
- Go
curl "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles" \
-H "Authorization: ApiKey lmk_abc123"
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles", {
method: "GET",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" }
});
console.log(await resp.json());
import os, requests
resp = requests.get(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles",
headers={"Authorization": os.environ["LUMO_AUTH"]}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
req, _ := http.NewRequest("GET", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles", nil)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Query Parameters
| Parameter | Description |
|---|---|
page | Page number (default: 1) |
limit | Results per page (default: 25, max: 100) |
search | Filter by name or slug |
isSystem | true to list only platform-wide roles, false for your organization's own |
sortBy, sortDir | Sort field and direction (asc/desc) |
Create Role
- curl
- Node
- Python
- Go
curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles \
-H "Authorization: ApiKey lmk_abc123" \
-H "Content-Type: application/json" \
-d '{
"name": "developer",
"slug": "developer",
"description": "Can read and write code repositories"
}'
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles", {
method: "POST",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" },
body: JSON.stringify({
"name": "developer",
"slug": "developer",
"description": "Can read and write code repositories"
})
});
console.log(await resp.json());
import os, requests
resp = requests.post(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles",
headers={"Authorization": os.environ["LUMO_AUTH"], "Content-Type": "application/json"},
json={
"name": "developer",
"slug": "developer",
"description": "Can read and write code repositories"
}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
body := strings.NewReader(`{
"name": "developer",
"slug": "developer",
"description": "Can read and write code repositories"
}`)
req, _ := http.NewRequest("POST", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles", body)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Returns 201 Created with the new role object.
Update Role
- curl
- Node
- Python
- Go
curl -X PUT https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE... \
-H "Authorization: ApiKey lmk_abc123" \
-H "Content-Type: application/json" \
-d '{
"name": "developer",
"description": "Read and write code repositories and trigger pipelines"
}'
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE...", {
method: "PUT",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" },
body: JSON.stringify({
"name": "developer",
"description": "Read and write code repositories and trigger pipelines"
})
});
console.log(await resp.json());
import os, requests
resp = requests.put(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE...",
headers={"Authorization": os.environ["LUMO_AUTH"], "Content-Type": "application/json"},
json={
"name": "developer",
"description": "Read and write code repositories and trigger pipelines"
}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
body := strings.NewReader(`{
"name": "developer",
"description": "Read and write code repositories and trigger pipelines"
}`)
req, _ := http.NewRequest("PUT", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE...", body)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Manage Role Permissions
Get Role Permissions
- curl
- Node
- Python
- Go
curl "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions" \
-H "Authorization: ApiKey lmk_abc123"
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions", {
method: "GET",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" }
});
console.log(await resp.json());
import os, requests
resp = requests.get(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions",
headers={"Authorization": os.environ["LUMO_AUTH"]}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
req, _ := http.NewRequest("GET", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions", nil)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Add a Permission to a Role
Accepts a single permissionId or an array permissionIds for bulk assignment.
- curl
- Node
- Python
- Go
curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions \
-H "Authorization: ApiKey lmk_abc123" \
-H "Content-Type: application/json" \
-d '{"permissionId": 42}'
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions", {
method: "POST",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" },
body: JSON.stringify({"permissionId": 42})
});
console.log(await resp.json());
import os, requests
resp = requests.post(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions",
headers={"Authorization": os.environ["LUMO_AUTH"], "Content-Type": "application/json"},
json={"permissionId": 42}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
body := strings.NewReader(`{"permissionId": 42}`)
req, _ := http.NewRequest("POST", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions", body)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
- curl
- Node
- Python
- Go
curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions \
-H "Authorization: ApiKey lmk_abc123" \
-H "Content-Type: application/json" \
-d '{"permissionIds": [42, 43, 44]}'
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions", {
method: "POST",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" },
body: JSON.stringify({"permissionIds": [42, 43, 44]})
});
console.log(await resp.json());
import os, requests
resp = requests.post(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions",
headers={"Authorization": os.environ["LUMO_AUTH"], "Content-Type": "application/json"},
json={"permissionIds": [42, 43, 44]}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
body := strings.NewReader(`{"permissionIds": [42, 43, 44]}`)
req, _ := http.NewRequest("POST", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions", body)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Remove a Permission from a Role
- curl
- Node
- Python
- Go
curl -X DELETE "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions/42" \
-H "Authorization: ApiKey lmk_abc123"
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions/42", {
method: "DELETE",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" }
});
console.log(await resp.json());
import os, requests
resp = requests.delete(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions/42",
headers={"Authorization": os.environ["LUMO_AUTH"]}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
req, _ := http.NewRequest("DELETE", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../permissions/42", nil)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Manage Role Members
List Users in Role
- curl
- Node
- Python
- Go
curl "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users" \
-H "Authorization: ApiKey lmk_abc123"
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users", {
method: "GET",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" }
});
console.log(await resp.json());
import os, requests
resp = requests.get(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users",
headers={"Authorization": os.environ["LUMO_AUTH"]}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
req, _ := http.NewRequest("GET", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users", nil)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Add a User to a Role
- curl
- Node
- Python
- Go
curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users \
-H "Authorization: ApiKey lmk_abc123" \
-H "Content-Type: application/json" \
-d '{"userId": "01JF3KUSER..."}'
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users", {
method: "POST",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" },
body: JSON.stringify({"userId": "01JF3KUSER..."})
});
console.log(await resp.json());
import os, requests
resp = requests.post(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users",
headers={"Authorization": os.environ["LUMO_AUTH"], "Content-Type": "application/json"},
json={"userId": "01JF3KUSER..."}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
body := strings.NewReader(`{"userId": "01JF3KUSER..."}`)
req, _ := http.NewRequest("POST", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users", body)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Remove a User from a Role
- curl
- Node
- Python
- Go
curl -X DELETE "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users/01JF3KUSER..." \
-H "Authorization: ApiKey lmk_abc123"
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users/01JF3KUSER...", {
method: "DELETE",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" }
});
console.log(await resp.json());
import os, requests
resp = requests.delete(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users/01JF3KUSER...",
headers={"Authorization": os.environ["LUMO_AUTH"]}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
req, _ := http.NewRequest("DELETE", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE.../users/01JF3KUSER...", nil)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Delete Role
- curl
- Node
- Python
- Go
curl -X DELETE "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE..." \
-H "Authorization: ApiKey lmk_abc123"
const resp = await fetch("https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE...", {
method: "DELETE",
headers: { "Authorization": process.env.LUMO_AUTH, "Content-Type": "application/json" }
});
console.log(await resp.json());
import os, requests
resp = requests.delete(
"https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE...",
headers={"Authorization": os.environ["LUMO_AUTH"]}
)
print(resp.json())
import (
"io"
"net/http"
"os"
"strings"
)
req, _ := http.NewRequest("DELETE", "https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/roles/01JF3KABCDE...", nil)
req.Header.Set("Authorization", os.Getenv("LUMO_AUTH"))
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
Returns 200 on success. The role is removed from all users and groups that held it.
System roles (isSystem: true) cannot be deleted.