Toutes les routes sont sous https:// et demandent la portée members:write, lecture comprise. Une invitation est valable 7 jours ; la personne qui l’accepte rejoint l’organisation avec ce qui l’attendait, et l’événement webhook member.joined part alors.
Une clé ne donne jamais le rôle d’administrateur : une invitation ou un code d’accès admin est réservé au propriétaire de l’organisation (403 ).
L’objet invitationLien vers la section L’objet invitation
{
"id": "7e2a4c6b-8d0f-4a1b-9c3d-5e7f9a1b3c5d",
"email": "tom.bernard@ecole.fr",
"role": "member",
"created_by": "a11f3c5e-7b9d-4f1a-8c2e-4d6f8a0b2c4e",
"created_by_name": "Camille Martin",
"created_at": 1762339200,
"expires_at": 1762944000,
"expired": false,
"groups": [{ "id": "<id de groupe>", "name": "Seconde 3" }],
"labels": [],
"roles": [{ "key": "manager", "name": "Responsable" }],
"license": null
}groups, labels, roles et license sont ce que la personne recevra à son arrivée. Une invitation renvoyée a pour auteur la personne ou la clé qui l’a renvoyée.
Lire les limitesLien vers la section Lire les limites
GET / · members:write
{
"under_contract": false,
"paid": true,
"seats_left": 12,
"pending": 4,
"pending_cap": 200,
"daily": { "workspace": 50, "sender": 50, "recipient": 3 },
"managed": false,
"email_configured": true,
"max_emails": 200
}| Champ | Description |
|---|---|
seats_left | Sièges libres moins les invitations en attente ; null sans plafond. |
pending, pending_cap | Invitations en attente et leur plafond ; null sous contrat. |
daily | Invitations par jour : pour le workspace, pour un expéditeur et pour une même adresse. null sous contrat. |
email_configured | L’envoi d’e-mails est configuré ; sinon, transmettez vous-même le lien de chaque invitation. |
max_emails | Adresses au plus par requête. |
Les limites du jour comptent sur 24 heures glissantes. Pour une clé, l’expéditeur est la personne qui l’a créée. Une même adresse reçoit au plus 3 invitations par jour d’une organisation, même sous contrat.
InviterLien vers la section Inviter
POST / · members:write
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
emails | liste | oui | 1 à 200 adresses. |
base_role | chaîne | non | member (par défaut) ou admin (propriétaire seulement). |
role_keys | liste | non | 20 rôles au plus, donnés à l’arrivée ; demande roles:write. |
group_ids | liste | non | 50 groupes statiques au plus ; demande groups:write. |
label_ids | liste | non | 50 étiquettes de membre au plus ; demande labels:write. |
pool_id | chaîne | non | Lot de licences de la personne ; demande licenses:write. |
Les mêmes rôles, groupes, étiquettes et lot valent pour toutes les adresses de la requête.
curl https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/invitations \
-H "Authorization: Bearer $MOONAI_ADMIN_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: rentree-2026-seconde-3" \
-d '{"emails": ["tom.bernard@ecole.fr", "lea.dupont@ecole.fr"], "group_ids": ["<id de groupe>"]}'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']}",
"Idempotency-Key": "rentree-2026-seconde-3",
}
r = requests.post(
f"{BASE}/invitations",
headers=HEADERS,
json={"emails": ["tom.bernard@ecole.fr", "lea.dupont@ecole.fr"], "group_ids": [os.environ["GROUP_ID"]]},
timeout=60,
)
r.raise_for_status()
for line in r.json()["results"]:
print(line["email"], line["status"], line["code"])const BASE = `https://chat.realmoon.ai/api/v1/orgs/${process.env.ORG_ID}`;
const r = await fetch(`${BASE}/invitations`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MOONAI_ADMIN_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'rentree-2026-seconde-3',
},
body: JSON.stringify({ emails: ['tom.bernard@ecole.fr', 'lea.dupont@ecole.fr'], group_ids: [process.env.GROUP_ID] }),
});
if (!r.ok) throw new Error(await r.text());
for (const line of (await r.json()).results) console.log(line.email, line.status, line.code);{
"results": [
{ "email": "tom.bernard@ecole.fr", "status": "invited", "code": null, "message": null, "invitation_id": "7e2a4c6b-8d0f-4a1b-9c3d-5e7f9a1b3c5d", "expires_at": 1762944000, "link": "https://chat.realmoon.ai/invite/<jeton>" },
{ "email": "lea.dupont@ecole.fr", "status": "already_member", "code": null, "message": null, "invitation_id": null }
],
"summary": { "invited": 1, "reinvited": 0, "already_member": 1, "errors": 0 },
"email_sent": true
}Chaque adresse a sa ligne :
status | Sens |
|---|---|
invited | Invitation créée. |
reinvited | Une invitation en attente existait : nouveau lien (l’ancien ne fonctionne plus) et 7 jours de plus. Elle ne prend pas de siège de plus. |
already_member | La personne est déjà membre, active ou suspendue. |
error | Rien n’a été créé pour cette adresse ; code et message disent pourquoi. |
code | Cause |
|---|---|
invalid_email | Adresse invalide. |
duplicate | Adresse en double dans la requête. |
seats_full | Tous les sièges sont occupés ou réservés par des invitations en attente. |
invite_pending_limit | Trop d’invitations en attente : révoquez-en ou attendez qu’elles soient acceptées. |
invite_limit_workspace, invite_limit_sender, invite_limit_recipient | Limite du jour atteinte pour le workspace, l’expéditeur ou l’adresse. |
error | L’invitation n’a pas pu être créée. |
link est le lien d’invitation de la personne : traitez-le comme un secret. email_sent dit que l’envoi d’e-mails est configuré, pas que l’e-mail a été reçu.
Erreurs de la requête entière :
| Statut | code | Cause |
|---|---|---|
| 400 | invalid_request | Rôle admin, owner ou member dans role_keys, rôle ou étiquette inconnus, groupe dynamique, venu de l’annuaire ou donnant le rôle d’administrateur, lot de licences inconnu. |
| 400 | licenses_unavailable | pool_id alors que l’organisation n’a pas de licences. |
| 403 | owner_only_admin_role | base_role: . |
| 403 | role_escalation | Un rôle, groupe ou étiquette donne plus que les permissions de la clé. |
| 403 | license_change_forbidden | Les groupes, étiquettes ou rôles donnent une autre licence, sans pool_id ni droit sur les licences. |
Lister les invitationsLien vers la section Lister les invitations
GET / · members:write
| Paramètre | Description |
|---|---|
q | Partie de l’adresse. |
status | pending (par défaut) ou expired (expirées depuis 30 jours au plus). |
page, limit | Pagination. |
Les plus récentes d’abord. Les invitations acceptées ou révoquées n’apparaissent pas.
Renvoyer une invitationLien vers la section Renvoyer une invitation
POST / · members:write
Crée un nouveau lien (l’ancien est désactivé) valable 7 jours, avec les mêmes groupes, rôles, étiquettes et licence. Une invitation expirée se renvoie s’il reste un siège.
{ "invitation_id": "7e2a4c6b-8d0f-4a1b-9c3d-5e7f9a1b3c5d", "expires_at": 1762944000, "link": "https://chat.realmoon.ai/invite/<jeton>", "email_sent": true }| Statut | code | Cause |
|---|---|---|
| 404 | not_found | Invitation inconnue, acceptée ou révoquée. |
| 409 | seats_full | Invitation expirée et plus aucun siège libre. |
| 429 | invite_limit_recipient | Trop d’invitations envoyées à cette adresse aujourd’hui. |
Révoquer une invitationLien vers la section Révoquer une invitation
DELETE / · members:write
Réponse : {"success": . Le lien est désactivé et ce qui attendait la personne est supprimé. Une seconde révocation répond 404.
Codes d’accèsLien vers la section Codes d’accès
Un code d’accès permet de rejoindre l’organisation sans invitation nominative : 8 caractères parmi les lettres majuscules et les chiffres, sans I, O, 0 ni 1.
GET / · members:write
{ "items": [{ "id": "c3b2a1f0-9e8d-4c7b-a6f5-e4d3c2b1a0f9", "code": "K7MZ4QPA", "role": "member", "max_uses": 30, "uses_count": 12, "expires_at": 1762944000, "created_at": 1762339200 }] }Seuls les codes utilisables (ni révoqués, ni expirés, ni épuisés) sont listés, les plus récents d’abord.
POST / · members:write
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
role | chaîne | non | member (par défaut) ou admin (propriétaire seulement). |
max_uses | entier | non | De 1 à 1 000, ou -1 sans limite ; 1 par défaut. |
expires_in_days | entier | non | De 1 à 365 ; 7 par défaut. |
Un corps vide {} crée un code d’une utilisation valable 7 jours. La réponse est le code créé. Un code est révoqué automatiquement quand son créateur quitte l’organisation ou perd le rôle d’administrateur.
DELETE / · members:write
Réponse : {"success": , aussi pour un code déjà révoqué ou expiré.