A role is a set of permissions. Five roles are built in; the organization can create others and assign them to a member, to a group (all its members) or to a label (everyone who carries it). A member holds the union of their base role and the roles they receive.
All routes are under https://. Scopes: roles:read to read, roles:write to write. A write never grants more than the key’s own permissions (403 , with permission).
PermissionsLink to the Permissions section
A permission has a reach: org (the whole organization), managed_groups (the groups the person manages) or self (their own data).
| Permission | Reach | Allows |
|---|---|---|
org.settings | org | Change the organization’s settings. |
org.delete, org.transfer | org | Delete or transfer the organization. Reserved for the owner, never in a custom role. |
billing.read, billing.manage | org | Read or manage billing. |
members.read | org, managed_groups | See members (email addresses only with org). |
members.invite | org, managed_groups | Invite. |
members.import | org | Import a CSV file. |
members.suspend, members.remove | org, managed_groups | Suspend, remove. |
members.assign_roles | org | Assign roles. |
roles.manage | org | Create and edit roles. |
groups.create, groups.delete | org | Create or delete groups. |
groups.members | org, managed_groups | Add and remove group members. |
groups.rules | org | Change a group’s rules and a dynamic group’s rule. |
groups.instructions | org, managed_groups | Write a group’s instructions. |
labels.manage | org | Create and edit labels. |
labels.assign | org, managed_groups | Apply labels to members. |
policies.org | org | Change the organization’s rules. |
models.manage | org | Manage organization models. |
connectors.manage | org | Manage MCP servers. |
storage.manage | org | Manage storage. |
library.manage | org | Share knowledge bases. |
catalog.publish | org | Share agents. |
usage.read | org, managed_groups, self | Read usage. |
usage.export | org, managed_groups | Export usage. |
security.manage | org | Domains, single sign-on, SCIM, accounts. |
audit.read, audit.export | org | Read or export the audit log. |
licenses.read, licenses.manage | org | Read or assign licenses. |
api.manage | org | Manage admin keys and webhooks. Never through a key. |
A role’s permissions dictionary maps a permission to a reach:
{ "members.read": "managed_groups", "groups.members": "managed_groups", "usage.read": "self" }Built-in rolesLink to the Built-in roles section
The API returns the names of built-in roles in French; the English app shows the name in parentheses.
role_key | Name | Permissions | Assignable |
|---|---|---|---|
owner | Propriétaire (Owner) | All | no (base role) |
admin | Administrateur (Admin) | All except org.delete, org.transfer and billing.manage | yes, by the owner only |
manager | Depends on the organization type: Gestionnaire, Enseignant, Manager, Chef (Manager, Teacher, Manager, Head of department) | members.read, groups.members, groups.instructions, labels.assign, usage.read, with managed_groups | yes |
billing | Facturation (Billing contact) | billing.read, licenses.read | yes |
member | Depends on the type: Membre, Élève, Collaborateur, Agent (Member, Student, Employee, Agent) | usage.read with self | no (every member) |
The manager role acts on the groups the person manages (see Groups). A custom role has custom:<id> as its role_key.
The role objectLink to the The role object section
{
"id": "5c1e2f8a-7d4b-4c9e-9a1f-2b3c4d5e6f70",
"org_id": "8c1f2a4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b",
"key": "prof_principal",
"name": "Professeur principal",
"description": "Gère sa classe et ses étiquettes.",
"permissions": { "members.read": "managed_groups", "groups.members": "managed_groups", "labels.assign": "managed_groups" },
"created_by": "1f2e3d4c-5b6a-4987-8a6b-5c4d3e2f1a0b",
"created_at": 1793836800,
"updated_at": 1793836800,
"role_key": "custom:5c1e2f8a-7d4b-4c9e-9a1f-2b3c4d5e6f70",
"builtin": false,
"assignable": true
}key is a readable identifier, unique in the organization; assignments use role_key.
List rolesLink to the List roles section
GET /roles · roles:read
Parameters: page, limit (pagination of custom roles, sorted by name). The response also includes builtin (the five built-in roles, with their permissions) and permission_catalog (each permission: key, allowed scopes, owner_only).
Create a roleLink to the Create a role section
POST /roles · roles:write
| Field | Type | Required | Description |
|---|---|---|---|
key | string | yes | 2 to 40 characters: unaccented lowercase letters, digits and _. Not owner, admin, manager, billing or member. |
name | string | yes | 1 to 128 characters. |
description | string | no | Up to 500 characters. |
permissions | object | no | Permissions and their reach. {} by default. |
curl https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/roles \
-H "Authorization: Bearer $MOONAI_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"key": "lecture_utilisation",
"name": "Lecture de l’utilisation",
"permissions": {"members.read": "org", "usage.read": "org"}
}'import os
import requests
BASE = f"https://chat.realmoon.ai/api/v1/orgs/{os.environ['ORG_ID']}"
HEADERS = {"Authorization": f"Bearer {os.environ['MOONAI_ADMIN_KEY']}"}
role = requests.post(
f"{BASE}/roles",
headers=HEADERS,
json={
"key": "lecture_utilisation",
"name": "Lecture de l’utilisation",
"permissions": {"members.read": "org", "usage.read": "org"},
},
timeout=30,
)
role.raise_for_status()
print(role.json()["role_key"])const BASE = `https://chat.realmoon.ai/api/v1/orgs/${process.env.ORG_ID}`;
const r = await fetch(`${BASE}/roles`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.MOONAI_ADMIN_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
key: 'lecture_utilisation',
name: 'Lecture de l’utilisation',
permissions: { 'members.read': 'org', 'usage.read': 'org' },
}),
});
if (!r.ok) throw new Error(await r.text());
console.log((await r.json()).role_key);An organization can have up to 100 custom roles.
| Status | code | Cause |
|---|---|---|
| 400 | invalid_request | Invalid or reserved key, empty name, unknown or owner-only permission, reach not allowed, more than 100 roles. |
| 403 | role_escalation | The role would grant a permission the key does not have. |
| 409 | invalid_request | Un (a role with this key already exists) |
Read a roleLink to the Read a role section
GET / · roles:read
role_id is the ID of a custom role (id, not custom:<id>) or the key of a built-in role (owner, admin…). A custom role also carries assignment_count, its number of assignments.
Update a roleLink to the Update a role section
PATCH / · roles:write
Optional fields name, description (null clears it) and permissions (replaces everything; null removes every permission). key does not change. You must hold both the role’s current permissions and the new ones. Members who have the role get its new permissions right away. A built-in role returns 404.
Delete a roleLink to the Delete a role section
DELETE / · roles:write
Deletes the role, its assignments and its rules. Response: {"success": .
AssignmentsLink to the Assignments section
The assignment objectLink to the The assignment object section
{
"id": "0d9c8b7a-6f5e-4d3c-8b2a-190817263544",
"org_id": "8c1f2a4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b",
"role_key": "custom:5c1e2f8a-7d4b-4c9e-9a1f-2b3c4d5e6f70",
"subject_type": "group",
"subject_id": "3e4f5a6b-7c8d-4e9f-8a0b-1c2d3e4f5a6b",
"created_by": "1f2e3d4c-5b6a-4987-8a6b-5c4d3e2f1a0b",
"created_at": 1793836800,
"subject_name": "Classe de 3A",
"role_name": "Professeur principal"
}subject_type is user, group or label; subject_name gives the member’s name, the group’s name or the label’s key = value.
List assignmentsLink to the List assignments section
GET / · roles:read
| Parameter | Type | Description |
|---|---|---|
role_key | string | One role (manager, custom:<id>…). |
subject_type | string | user, group or label. |
subject_id | string | A member, a group or a label. |
page, limit | integer | Pagination. |
Only explicit assignments are listed, not members’ base roles.
Assign a roleLink to the Assign a role section
POST / · roles:write
| Field | Type | Required | Description |
|---|---|---|---|
role_key | string | yes | admin, manager, billing or custom:<id>. |
subject_type | string | yes | user, group or label. |
subject_id | string | yes | The ID of the member (active or suspended), the group or the label. |
The response is the assignment with created: false if it already existed (the request has no effect and also returns 200).
| Status | code | Cause |
|---|---|---|
| 400 | invalid_request | owner or member (they come from the base role); unknown role. |
| 400 | scim_group_role | A group synced from the SCIM directory does not grant roles. |
| 403 | forbidden | Appointing an admin (owner only, so never through a key); assigning a role to yourself; targeting the owner. |
| 403 | license_change_forbidden | The change would move members to another license pool; it requires the permission to manage licenses (licenses:write scope). Carries user_ids and count. |
| 404 | not_found | Member, group or label not found. |
| 409 | directory_group_admin | The admin role on a group filled by the directory or by single sign-on. |
Remove an assignmentLink to the Remove an assignment section
DELETE / · roles:write
Identify the assignment with ?id=<id>, or with a {role_key, body. Response: {"success": . Removing admin is reserved for the owner, and refused if no active admin would be left (409).