A group brings members together. A static group contains the members you add to it; a dynamic group also contains every member who carries the labels in its rule. A group receives roles, rules and instructions; its managers act on its members.
All routes are under https://. Scopes: groups:read to read, groups:write to write.
The group objectLink to the The group object section
{
"id": "3e4f5a6b-7c8d-4e9f-8a0b-1c2d3e4f5a6b",
"org_id": "8c1f2a4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b",
"name": "Classe de 3A",
"description": "Élèves de 3A, année 2026-2027.",
"kind": "dynamic",
"label_rule": { "all": [{ "key": "classe", "value": "3A" }] },
"position": 10,
"source": "manual",
"created_by": "1f2e3d4c-5b6a-4987-8a6b-5c4d3e2f1a0b",
"created_at": 1793836800,
"updated_at": 1793836800
}| Field | Description |
|---|---|
kind | static or dynamic. Cannot change after creation. |
label_rule | The rule of a dynamic group; null for a static group. |
position | Display order (0 to 100,000). |
source | manual, or scim for a group created by the directory: it is then edited in the directory (409 ). |
In lists and in GET /, each group also carries member_count (active members, managers not counted), manager_count, has_policy (it has rules or instructions) and is_managed_by_me. GET / also returns roles, the roles assigned to the group.
The label ruleLink to the The label rule section
{
"all": [{ "key": "site", "value": "Lyon" }],
"any": [{ "key": "service", "value": "Ventes" }, { "key": "service", "value": "Support" }]
}all: the member carries all of these labels.any: at least one. Both together: both conditions.- Each condition is exactly
{"key",; the key is lowercased, the value is compared case-insensitively."value"} - Up to 20 conditions. Each label named must exist (otherwise
400, with the messageÉtiquettes, unknown labels).inconnues : … {"all": []}is accepted on creation: the group then includes no one.
List groupsLink to the List groups section
GET /groups · groups:read
| Parameter | Type | Description |
|---|---|---|
q | string | Searches the name, 128 characters. |
kind | string | static or dynamic. |
page, limit | integer | Pagination. Sorted by position, then name. |
If the key’s creator only manages some groups, only those are listed.
Create a groupLink to the Create a group section
POST /groups · groups:write
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | 1 to 128 characters, unique in the organization (case-insensitive). |
description | string | no | Up to 500 characters. |
kind | string | no | static (default) or dynamic. |
label_rule | object | for dynamic | Label rule. Not allowed on a static group. |
position | integer | no | 0 by default. |
curl https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/groups \
-H "Authorization: Bearer $MOONAI_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Site de Lyon",
"kind": "dynamic",
"label_rule": {"all": [{"key": "site", "value": "Lyon"}]}
}'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']}"}
group = requests.post(
f"{BASE}/groups",
headers=HEADERS,
json={"name": "Site de Lyon", "kind": "dynamic", "label_rule": {"all": [{"key": "site", "value": "Lyon"}]}},
timeout=30,
)
group.raise_for_status()
print(group.json()["id"])const BASE = `https://chat.realmoon.ai/api/v1/orgs/${process.env.ORG_ID}`;
const r = await fetch(`${BASE}/groups`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.MOONAI_ADMIN_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Site de Lyon', kind: 'dynamic', label_rule: { all: [{ key: 'site', value: 'Lyon' }] } }),
});
if (!r.ok) throw new Error(await r.text());
console.log((await r.json()).id);An organization can have up to 1,000 groups.
| Status | code | Cause |
|---|---|---|
| 400 | invalid_request | Empty name, rule missing for a dynamic group or present for a static one, invalid rule (fields points to label_rule), unknown labels, more than 1,000 groups. |
| 409 | invalid_request | Un (a group already has this name) |
Read a groupLink to the Read a group section
GET / · groups:read
Returns the group with its counters and roles. 404 for an unknown group or one outside the groups the key can read.
Update a groupLink to the Update a group section
PATCH / · groups:write
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | New name (not on a group synced from the directory). |
description | string | no | null clears it. |
position | integer | no | New order. |
label_rule | object | no | New rule, for a dynamic group only. |
Changing the rule requires the permission to set groups: the key must also have a rules:read or rules:write scope. The new rule applies right away: members join or leave the group, along with its roles and rules. If it moves members to another license pool, licenses:write is also required (403 ).
Delete a groupLink to the Delete a group section
DELETE / · groups:write
Deletes the group, its memberships, rules, instructions, roles and labels. The members stay in the organization. Response: {"success": . 409 for a group synced from the directory.
Group membersLink to the Group members section
List membersLink to the List members section
GET / · groups:read
Parameters: q (name, and address if the key can see addresses), page, limit. Lists active and suspended members, whether added by hand or included by the rule. The response also includes emails_visible.
{
"items": [
{
"user_id": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
"role": "member",
"status": "active",
"joined_at": 1793836800,
"name": "Camille Durand",
"email": "camille.durand@ecole.fr",
"last_active_at": 1793923200,
"never_active": false,
"is_manager": false,
"membership_source": "label",
"dynamic": true
}
],
"total": 1,
"page": 1,
"limit": 50,
"emails_visible": true
}membership_source is manual, scim, sso, import, or label for a member included only by the rule (dynamic: ). Each member also carries their groups, labels, roles, effective_roles and role_sources.
Find members to addLink to the Find members to add section
GET / · groups:write
Parameters: q (name; an exact address if it contains “@” and the key can see addresses), limit (1 to 50, 20 by default). Returns items (user_id, name, profile_image_url), total and limit, without addresses. Excluded: inactive members, the owner, people already in the group and people the key cannot act on.
Add membersLink to the Add members section
POST / · groups:write
| Field | Type | Required | Description |
|---|---|---|---|
user_ids | list | yes | 1 to 500 active members. |
is_manager | boolean | no | true makes these people managers of the group, false takes it away; missing: unchanged (false for a new person). Requires the permission across the whole organization. |
Response: {"success": . Members already present are skipped (updated counts those whose is_manager changed). 404 (with the message Membres , members not found) if an ID is not an active member.
Remove membersLink to the Remove members section
DELETE / · groups:write
One member with ?, or several with a {"user_ids": body (up to 500). Response: {"success": . A member included by a dynamic group’s rule stays in it: remove their label instead.
Preview a ruleLink to the Preview a rule section
GET / · groups:read
Lists the active members a rule would include, without saving anything. rule parameter: the rule as URL-encoded JSON (up to 8,192 characters, at least one condition); without rule, the group’s own rule. Returns items (user_id, name, email), pagination and rule, the rule as it was read.
curl -G "https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/groups/$GROUP_ID/preview" \
-H "Authorization: Bearer $MOONAI_ADMIN_KEY" \
--data-urlencode 'rule={"all":[{"key":"site","value":"Lyon"}]}'