DocumentationOuvrir Moon AI
Parcourir la documentation

Membres

Lister et filtrer les membres, lire une fiche, suspendre, réactiver ou retirer, agir en masse, exporter et importer un fichier CSV avec l’API d’administration.

Sur cette page

Toutes les routes sont sous https://chat.realmoon.ai/api/v1/orgs/{org_id}. Portées : members:read pour lire, members:write pour écrire. Les invitations et les codes d’accès ont leur page : Invitations et codes d’accès.

Une clé n’agit jamais comme le propriétaire : elle ne nomme, ne rétrograde, ne suspend ni ne retire un administrateur, et n’agit jamais sur son propre créateur (403 forbidden).

L’objet membreLien vers la section L’objet membre

JSON
{
  "user_id": "5b1e7c2a-3d4f-4e6a-8b9c-0d1e2f3a4b5c",
  "role": "member",
  "status": "active",
  "joined_at": 1767225600,
  "name": "Camille Durand",
  "email": "camille.durand@ecole.fr",
  "profile_image_url": "/user.png",
  "last_active_at": 1798200000,
  "never_active": false,
  "groups": [
    { "id": "2d4c6e8a-1b3d-4f5a-9c7e-0a2b4c6d8e1f", "name": "2nde 3", "kind": "static", "position": 0, "is_manager": false, "source": "import", "via": "manual" }
  ],
  "labels": [{ "id": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", "key": "promo", "value": "2026", "color": "blue", "kind": "member" }],
  "roles": [],
  "role_sources": [],
  "effective_roles": [],
  "license": { "pool_id": "4e5f6a7b-8c9d-4e0f-a1b2-c3d4e5f6a7b8", "plan_id": "<id du forfait>", "plan_name": "<nom du forfait>", "source": "rule" }
}
ChampDescription
roleRôle de base : owner, admin ou member.
statusactive, suspended ou pending.
emailnull quand la clé ne voit pas les adresses (son créateur ne lit que les membres des groupes qu’il gère).
never_activeLe compte n’a jamais été utilisé.
groupsGroupes du membre ; via vaut manual (ajouté) ou label (retenu par la règle d’un groupe dynamique).
rolesRôles attribués directement ; role_sources donne chaque rôle et son origine (direct, group ou label) ; effective_roles leur union.
licenseLicence du membre (pool_id, plan_id, plan_name, source), ou null. Présente dans la liste seulement.

Lister les membresLien vers la section Lister les membres

GET /members · members:read

ParamètreTypeDescription
qchaîneRecherche dans le nom et, si la clé voit les adresses, l’adresse. 128 caractères.
group_idchaîneMembres d’un groupe (ajoutés ou retenus par sa règle).
label_idchaîneMembres qui portent une étiquette.
rolechaîneowner, admin ou member (rôle de base), ou manager, billing, custom:<id> (rôle reçu directement, par un groupe ou une étiquette).
statuschaîneactive, suspended ou pending.
no_groupbooléentrue : membres d’aucun groupe.
never_activebooléentrue : comptes jamais utilisés.
licensechaîneIdentifiant d’un lot de licences, ou none (sans licence).
sortchaînename (par défaut), last_active_at ou joined_at.
dirchaîneasc ou desc ; par défaut asc pour le nom, desc sinon.
page, limitentierPagination.

La réponse ajoute emails_visible, license_pools (les lots : id, plan_id, plan_name, name, seats, used ; vide pour un workspace à licence unique), licenses_available, sort et dir. Une valeur de filtre inconnue répond 400 invalid_request ; license sur une organisation sans lots répond 400 licenses_unavailable.

cURL
curl "https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/members?status=active&never_active=true&limit=200" \
  -H "Authorization: Bearer $MOONAI_ADMIN_KEY"
Python
import os
import time
import requests

BASE = f"https://chat.realmoon.ai/api/v1/orgs/{os.environ['ORG_ID']}"
HEADERS = {"Authorization": f"Bearer {os.environ['MOONAI_ADMIN_KEY']}"}

def all_members(**filters):
    page = 1
    while True:
        r = requests.get(f"{BASE}/members", headers=HEADERS,
                         params={**filters, "page": page, "limit": 200}, timeout=30)
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", "5")))
            continue
        r.raise_for_status()
        body = r.json()
        yield from body["items"]
        if page * 200 >= body["total"]:
            break
        page += 1

for m in all_members(status="active", never_active="true"):
    print(m["user_id"], m["email"])
JavaScript
const BASE = `https://chat.realmoon.ai/api/v1/orgs/${process.env.ORG_ID}`;
const HEADERS = { Authorization: `Bearer ${process.env.MOONAI_ADMIN_KEY}` };

async function* allMembers(filters = {}) {
  for (let page = 1; ; page++) {
    const params = new URLSearchParams({ ...filters, page: String(page), limit: '200' });
    const r = await fetch(`${BASE}/members?${params}`, { headers: HEADERS });
    if (!r.ok) throw new Error(await r.text());
    const body = await r.json();
    yield* body.items;
    if (page * 200 >= body.total) break;
  }
}

for await (const m of allMembers({ status: 'active', never_active: 'true' })) console.log(m.user_id, m.email);

Lire la fiche d’un membreLien vers la section Lire la fiche d’un membre

GET /members/{user_id} · members:read

JSON
{
  "member": {
    "user_id": "5b1e7c2a-3d4f-4e6a-8b9c-0d1e2f3a4b5c",
    "name": "Camille Durand",
    "email": "camille.durand@ecole.fr",
    "role": "member",
    "status": "active",
    "joined_at": 1767225600,
    "last_active_at": 1798200000,
    "never_active": false,
    "invited_by": { "user_id": "a11f2b3c-4d5e-4f6a-8b7c-9d0e1f2a3b4c", "name": "Fabien Marie" }
  },
  "base_role_name": null,
  "roles": [{ "key": "manager", "via": "group", "via_id": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f", "via_name": "Professeurs", "name": "Enseignant", "removable": false }],
  "groups": [],
  "labels": [],
  "license": null,
  "license_pools": [],
  "usage": { "files": 14, "monthly_percent": 12.5, "week_percent": 30.0, "session_percent": 0.0, "period_end": 1800000000, "tier": "paid" },
  "activity": null,
  "emails_visible": true,
  "blocked": null,
  "can": { "suspend": true, "remove": true, "base_role": false, "admin_role": false, "groups": false, "labels": false, "roles": false, "license": false, "rules": false, "usage": false, "activity": false }
}
ChampDescription
usageUtilisation du membre en pourcentage de ses limites (mois, semaine, session) et nombre de fichiers ; null sans le droit de lire l’utilisation.
activityLes 15 dernières actions du journal qui le visent ; null sans le droit de lire le journal.
blockedPourquoi la clé ne peut pas agir sur ce membre : self, owner, admin ou stronger (il a plus de droits).
canCe que la clé peut faire sur ce membre.

Suspendre, réactiver ou changer le rôle de baseLien vers la section Suspendre, réactiver ou changer le rôle de base

PATCH /members/{user_id} · members:write

ChampTypeObligatoireDescription
statuschaînenonsuspended (membre actif) ou active (membre suspendu).
rolechaînenonadmin ou member. Réservé au propriétaire : refusé à une clé.

Envoyez au moins un champ. Réponse : {"member": {…}}, le membre après le changement.

cURL
curl -X PATCH https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/members/$USER_ID \
  -H "Authorization: Bearer $MOONAI_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "suspended"}'
Python
r = requests.patch(f"{BASE}/members/{user_id}", headers=HEADERS, json={"status": "suspended"}, timeout=30)
r.raise_for_status()
print(r.json()["member"]["status"])
JavaScript
const r = await fetch(`${BASE}/members/${userId}`, {
  method: 'PATCH',
  headers: { ...HEADERS, 'Content-Type': 'application/json' },
  body: JSON.stringify({ status: 'suspended' }),
});
if (!r.ok) throw new Error(await r.text());
console.log((await r.json()).member.status);

Suspendre ferme toutes les sessions du membre et envoie l’événement member.suspended. Réactiver occupe un siège : sur un workspace payé par carte, le siège est payé d’abord ; l’événement member.reactivated part ensuite.

StatutcodeCause
400invalid_requestRien à modifier ; suspendre un membre qui n’est pas actif ; réactiver un membre qui n’est pas suspendu.
402seat_payment_failedLe paiement du siège a échoué : le propriétaire doit mettre à jour son moyen de paiement.
403forbiddenLe créateur de la clé lui-même, le propriétaire, un administrateur, ou un membre qui a plus de droits que la clé.
404not_foundCe n’est pas un membre de l’organisation.
409seats_fullPlus de siège libre.
409past_dueLe dernier paiement du workspace a échoué.
409payment_method_requiredLe nouveau propriétaire doit ajouter un moyen de paiement.

Retirer un membreLien vers la section Retirer un membre

DELETE /members/{user_id} · members:write

Retire le membre de l’organisation ; son compte Moon AI reste. Ses sessions dans l’organisation sont fermées, ses groupes, rôles, étiquettes et connexions de l’organisation sont supprimés, ainsi que les invitations, codes d’accès et clés qu’il avait créés. L’événement member.removed part. Réponse : {"removed": true}. Mêmes refus que PATCH, plus 409 si l’organisation n’aurait plus d’administrateur actif.

Pour transférer d’abord ses ressources, utilisez le départ d’un membre.

Agir sur plusieurs membresLien vers la section Agir sur plusieurs membres

POST /members/bulk · members:write

ChampTypeObligatoireDescription
actionchaîneouiVoir ci-dessous.
user_idslisteuser_ids ou filter5 000 membres au plus. Les identifiants qui ne sont pas des membres sont ignorés.
filterobjetuser_ids ou filterLes filtres de la liste : q, group_id, label_id, role, status (active ou suspended), no_group, never_active, license. 20 000 membres au plus. Interdit avec remove.
valuechaîneselon l’actionGroupe, étiquette ou rôle visé.
is_managerbooléennonAvec add_group : nommer gestionnaires.
pool_idchaîneavec licenseUn lot, "none" (sans licence) ou null (retour aux règles).
actionvalueEffet
suspend, reactivateaucuneSuspendre, réactiver. Sur un workspace payé par carte, 50 réactivations au plus par requête.
set_base_roleadmin ou memberRéservé au propriétaire.
add_group, remove_groupidentifiant de groupeAjouter au groupe, en retirer.
add_label, remove_labelidentifiant d’étiquettePoser, retirer une étiquette de membre.
add_role, remove_rolemanager, billing ou custom:<id>Attribuer, retirer un rôle (portée roles:write en plus).
licenseaucune (pool_id)Attribuer un lot de licences (portée licenses:write en plus).
removeaucuneRetirer de l’organisation.
JSON
{
  "action": "suspend",
  "matched": 42,
  "done": 39,
  "skipped": { "admin": 1, "already": 2 },
  "failed": [],
  "failed_count": 0
}

skipped compte les membres laissés de côté par raison : self, owner, admin, stronger, already, not_member, out_of_scope, not_active. failed donne les 50 premiers échecs (user_id, code, message). Il n’y a pas de transaction globale : ce qui est fait reste fait si un membre suivant échoue.

Exporter les membresLien vers la section Exporter les membres

GET /members/export · members:read

Accepte les filtres de la liste (sauf la pagination et le tri) et lang=en pour des en-têtes en anglais. Rend un fichier CSV (UTF-8 avec BOM, séparateur ;), trié par nom, 50 000 lignes au plus :

Texte
email;nom;groupes;étiquettes;rôle de base;rôles;licence;statut;arrivé le;dernière activité
camille.durand@ecole.fr;Camille Durand;2nde 3;promo=2026;member;;<forfait>;active;2026-01-01 00:00;2026-12-23 08:40

Les valeurs multiples sont séparées par |, les dates en UTC. Une cellule qui commence par =, +, - ou @ est précédée d’une apostrophe.

Importer un fichier CSVLien vers la section Importer un fichier CSV

POST /members/import · members:write

Importe jusqu’à 5 000 lignes (2 Mo) : chaque ligne invite une personne, l’ajoute directement (adresse d’un domaine vérifié, organisation non payée par carte) ou met à jour un membre (groupes, étiquettes, rôle, licence ajoutés ; rien n’est jamais retiré).

ParamètreTypeDescription
dry_runbooléentrue : simulation, rien n’est écrit.
create_missingbooléentrue : crée les groupes et les étiquettes inconnus.

Le fichier s’envoie de trois façons : multipart/form-data avec un champ file, application/json avec {"csv": "<texte>"}, ou le CSV brut (text/csv).

ColonneContenu
emailObligatoire.
nameNom affiché.
groupsNoms de groupes, séparés par | ou ;.
labelsclé=valeur, séparées par | ou ;.
rolemember, admin (propriétaire seulement), manager, billing, ou la clé (key) d’un rôle créé.
licenseIdentifiant ou forfait d’un lot.

L’en-tête est reconnu en français ou en anglais (GET /members/import/columns donne les noms acceptés) ; sans en-tête, les colonnes sont prises dans cet ordre. Le séparateur (,, ; ou tabulation) est détecté.

cURL
curl "https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/members/import?dry_run=true" \
  -H "Authorization: Bearer $MOONAI_ADMIN_KEY" \
  -F "file=@eleves.csv"
Python
with open("eleves.csv", "rb") as f:
    r = requests.post(f"{BASE}/members/import", headers=HEADERS,
                      params={"dry_run": "true"}, files={"file": f}, timeout=120)
r.raise_for_status()
report = r.json()
print(report["summary"])
for line in report["lines"]:
    if line["errors"]:
        print(line["line"], line["email"], line["errors"])
JavaScript
import fs from 'node:fs';

const form = new FormData();
form.append('file', new Blob([fs.readFileSync('eleves.csv')], { type: 'text/csv' }), 'eleves.csv');
const r = await fetch(`${BASE}/members/import?dry_run=true`, { method: 'POST', headers: HEADERS, body: form });
if (!r.ok) throw new Error(await r.text());
const report = await r.json();
console.log(report.summary);

La réponse donne summary (lignes par action, erreurs, sièges restants), to_create (groupes et étiquettes à créer) et lines : pour chaque ligne, action (invite, add_domain, update, already_member, already_invited ou error), errors et warnings. Une erreur de ligne n’arrête pas l’import : les autres lignes sont traitées.

Hors simulation, une importation de 100 lignes au plus répond tout de suite avec le résultat et un job_id. Au-delà, elle répond 202 avec {"job_id", "status": "running", "total"} et continue en arrière-plan. Suivez-la avec GET /members/import/{job_id}, qui rend status (running, done, failed ou interrupted), total, done, phase et, à la fin, result (les lignes en erreur ou en avertissement, 2 000 au plus). Un suivi est gardé 24 heures.

Chaque invitation de l’import compte dans les limites d’invitations ; la simulation ne les vérifie pas.

Voir aussiLien vers la section Voir aussi

Moon AI

Moon AI en action

0:00

À découvrir

Captures de chat.realmoon.ai du 29 octobre 2026, avec des données fictives.

Moon AI

Vos préférences

Aucun suivi publicitaire.

Ce site ne dépose aucun cookie publicitaire ni cookie de mesure d’audience : notre serveur compte les visites sans cookie. Votre thème et la fermeture de l’information sur les cookies sont mémorisés sur cet appareil.

L’application Moon AI possède ses propres réglages de confidentialité.