# API d’administration

> Créer une clé d’administration moai_adm_, l’envoyer, choisir ses portées, et les règles communes de l’API : pagination, erreurs, débit et idempotence.

L’API d’administration fait par programme ce que fait l’administration d’une organisation : votre annuaire, votre logiciel RH ou un script invite des membres, tient les groupes à jour, attribue des licences ou lit l’utilisation. Elle est ouverte à tout workspace d’équipe ; les groupes, les rôles, les règles et la sécurité ne servent que dans une organisation gérée.

```text
https://chat.realmoon.ai/api/v1/orgs/{org_id}
```

L’identifiant de l’organisation (`org_id`) figure dans l’administration › « API et clés », carte « Démarrage », dans l’exemple « Premier appel ».

## Créer une clé d’administration

1. Ouvrez l’administration › « API et clés », carte « Clés d’administration », puis cliquez sur « Nouvelle clé ».
2. Saisissez un « Nom » qui dit qui l’utilise, par exemple « Logiciel RH » (80 caractères au plus).
3. Dans « Droits », choisissez pour chaque domaine « Aucun accès », « Lecture » ou « Lecture et écriture ». Choisissez au moins un droit.
4. Choisissez l’« Expiration » : « Jamais » ou « Dans … jours » (730 jours au plus).
5. Facultatif : dans « Adresses autorisées », une adresse IP ou une plage par ligne, par exemple `203.0.113.0/24` (50 au plus). Laissez vide pour accepter toutes les adresses.
6. Cliquez sur « Créer la clé », copiez-la, puis cliquez sur « Je l’ai copié ». Elle commence par `moai_adm_` et ne sera plus affichée.

La page est visible du propriétaire, des administrateurs et des rôles qui ont la permission de gérer l’API. Une organisation a 25 clés actives au plus. La liste affiche le début de chaque clé (`moai_adm_` et six caractères), son créateur, sa date d’expiration et sa dernière utilisation.

Les droits d’une clé ne changent plus après sa création : « Modifier » ne change que son nom et ses adresses autorisées. Pour d’autres droits, créez une nouvelle clé. « Révoquer » arrête une clé tout de suite et définitivement.

## Authentifier les requêtes

Envoyez la clé dans l’en-tête `Authorization`, après `Bearer`. Toutes les requêtes passent en HTTPS.

```text
Authorization: Bearer moai_adm_…
```

- Une clé ne fonctionne que pour les routes `/api/v1/orgs/{org_id}/…` de son organisation. Avec l’identifiant d’une autre organisation, la réponse est `404 not_found`, comme pour une organisation qui n’existe pas.
- Hors de ces routes (chat, autres API), la clé est refusée : `403 admin_key_scope_forbidden`.
- Une clé personnelle `sk-…` est refusée sur ces routes : `403 personal_api_key_forbidden`.
- Avec des adresses autorisées, une requête venue d’une autre adresse reçoit `403 admin_key_ip_forbidden`.

### Lire l’accès de la clé

`GET /me` · aucune portée requise

Le premier appel conseillé : il confirme que la clé fonctionne et donne ses permissions effectives.

```bash title="cURL"
curl https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/me \
  -H "Authorization: Bearer $MOONAI_ADMIN_KEY"
```

```python title="Python"
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']}"}

r = requests.get(f"{BASE}/me", headers=HEADERS, timeout=30)
r.raise_for_status()
me = r.json()
print(me["org"]["name"], sorted(me["permissions"]))
```

```javascript title="JavaScript"
const BASE = `https://chat.realmoon.ai/api/v1/orgs/${process.env.ORG_ID}`;
const HEADERS = { Authorization: `Bearer ${process.env.MOONAI_ADMIN_KEY}` };

const r = await fetch(`${BASE}/me`, { headers: HEADERS });
if (!r.ok) throw new Error(await r.text());
const me = await r.json();
console.log(me.org.name, Object.keys(me.permissions));
```

```json
{
  "org": {
    "id": "8c1f2a4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b",
    "name": "Lycée Jean Moulin",
    "slug": "lycee-jean-moulin",
    "is_personal": false,
    "org_kind": "education",
    "managed": true,
    "policy_version": 14
  },
  "my_role": "api_key",
  "roles": [],
  "permissions": {
    "members.read": "org",
    "members.invite": "org",
    "members.suspend": "org",
    "members.remove": "org",
    "usage.read": "self"
  },
  "managed_group_ids": [],
  "group_ids": [],
  "kind": "education",
  "managed": true,
  "platform_override": false,
  "labels_display": { "manager": "Enseignant", "member": "Élève" },
  "labels_display_en": { "manager": "Teacher", "member": "Student" },
  "my_groups": [],
  "role_names": {},
  "can_leave": false
}
```

| Champ | Description |
| --- | --- |
| `org` | L’organisation : identifiant, nom, `slug`, `is_personal`, type (`org_kind` : `team`, `education`, `company`, `public_sector` ou `other`), `managed` (organisation gérée) et `policy_version` (augmente à chaque changement des règles). |
| `my_role` | `api_key` pour une clé. |
| `permissions` | Les permissions de la clé et leur étendue : `org` (toute l’organisation), `managed_groups` (les groupes que gère le créateur) ou `self` (soi seulement). |
| `labels_display` | Les noms affichés des rôles fournis selon le type d’organisation, en français et en anglais (`labels_display_en`). |

Les autres champs décrivent la personne qui a créé la clé (`joined_at`, `license`, `can_leave`) ou restent vides pour une clé.

## Portées

Chaque domaine de l’écran « Droits » correspond à deux portées. Une portée `…:read` n’ouvre que les lectures (`GET`) ; une portée `…:write` ouvre aussi les lectures de son domaine.

| Domaine dans l’administration | Lecture | Écriture | Routes |
| --- | --- | --- | --- |
| « Membres » | `members:read` | `members:write` | `/members`, `/invitations`, `/access-codes`, lecture de `/directory` et de `/policies/effective/{user_id}` |
| « Groupes » | `groups:read` | `groups:write` | `/groups` |
| « Rôles » | `roles:read` | `roles:write` | `/roles`, `/role-assignments` |
| « Étiquettes » | `labels:read` | `labels:write` | `/labels` |
| « Règles » | `rules:read` | `rules:write` | `/policies`, `/models` |
| « Licences » | `licenses:read` | `licenses:write` | `/licenses`, `/contract` |
| « Utilisation » | `usage:read` | aucune | `/usage` |
| « Journal d’audit » | `audit:read` | aucune | `/audit` |
| « Réglages de l’organisation » | `org:read` | `org:write` | `/general`, `/settings`, `/overview`, `/alerts`, écriture de `/directory` |
| « Ressources » | `resources:read` | `resources:write` | `/resources` |
| « Sécurité » | `security:read` | `security:write` | `/domains`, `/security`, actions sur le compte d’un membre, lecture de `/sso` et de `/scim-token` |
| « Webhooks » | `webhooks:read` | `webhooks:write` | `/webhooks` |

Quelques routes demandent une portée différente de celle de leur domaine :

| Route | Portée demandée |
| --- | --- |
| `GET /me`, `GET /me/requirements` | aucune |
| `GET /invitations`, `GET /access-codes` | `members:write` (elles affichent des adresses et des codes) |
| `GET /members/import/columns` | `members:write` |
| `GET /groups/{group_id}/candidates` | `groups:write` |
| `GET /policies/effective/{user_id}` | `members:read` ou `members:write` |
| `GET /directory` | `members:read` (`members:write` ne suffit pas) |
| `PATCH /members/{user_id}/account`, `POST /members/{user_id}/account/reset-2fa`, `…/password-reset`, `…/disable`, `…/enable` | `security:write` |
| `PUT /directory` | `org:write` |

Une route sans la portée voulue répond `403 admin_key_scope_missing`, avec la liste `required_scopes` des portées qui conviennent.

### Les droits du créateur

La portée ouvre une route ; la permission décide de l’action. À chaque requête, la clé reçoit les permissions de la personne qui l’a créée, limitées à celles de ses portées. Une clé n’a donc jamais plus de droits que son créateur :

- si son créateur ne lit que les membres des groupes qu’il gère, la clé ne lit qu’eux aussi, sans leur adresse e-mail ;
- vous ne pouvez donner à une clé que des domaines où vous avez au moins une permission (sinon `403 role_escalation` à la création) ;
- une action qui donnerait plus que les permissions de la clé est refusée : `403 role_escalation`, avec la `permission` en cause.

La clé est révoquée définitivement quand son créateur quitte l’organisation ou perd la permission de gérer l’API. Elle est refusée tant que son compte ou son adhésion est suspendu (`403 admin_key_creator_inactive`).

### Ce qu’une clé ne fait jamais

- Supprimer l’organisation ou en transférer la propriété.
- Payer ou changer la facturation.
- Créer, modifier ou révoquer des clés d’administration.
- Configurer la connexion unique ou le jeton de l’annuaire SCIM (`403 owner_only`) ; elle peut seulement les lire.
- Nommer un administrateur, ou agir sur le propriétaire, un administrateur ou son propre créateur.
- Utiliser le chat ou toute autre API de Moon AI.

## Pagination

Les listes acceptent deux paramètres de requête :

| Paramètre | Type | Description |
| --- | --- | --- |
| `page` | entier | Numéro de page, à partir de 1. Par défaut 1. |
| `limit` | entier | Éléments par page, de 1 à 200. Par défaut 50. |

Une valeur illisible est remplacée par la valeur par défaut, une valeur hors bornes par la borne la plus proche. Chaque liste répond :

```json
{ "items": [], "total": 0, "page": 1, "limit": 50 }
```

`total` compte tous les éléments qui répondent aux filtres. Une liste peut ajouter des champs à côté de `items`, décrits sur sa page.

## Formats

- Les corps de requête sont en JSON (`Content-Type: application/json`), 512 Ko au plus (sinon `413`). Seul l’import de membres reçoit un fichier CSV.
- Un champ inconnu dans un corps est refusé : `400 invalid_request`, avec `fields` qui nomme le champ (« Champ inconnu. »).
- Les dates sont des entiers en secondes Unix (UTC).
- Les identifiants (organisation, membre, groupe, rôle créé, étiquette…) sont des chaînes opaques ; ne les construisez pas vous-même.
- Les messages d’erreur sont en français ; fiez-vous au champ `code`, qui ne change pas.

## Erreurs

Toutes les erreurs ont la même forme :

```json
{
  "detail": {
    "code": "admin_key_scope_missing",
    "message": "Cette clé n’a pas la portée nécessaire pour cette route.",
    "required_scopes": ["members:write"]
  }
}
```

| Statut | `code` | Cause |
| --- | --- | --- |
| 400 | `invalid_request` | Corps ou paramètre invalide ; `fields` liste les champs en cause (`field`, `message`). |
| 400 | `invalid_idempotency_key` | En-tête `Idempotency-Key` vide, trop long ou avec des caractères non visibles. |
| 401 | `invalid_admin_key` | Clé inconnue ou mal formée. La réponse porte `WWW-Authenticate: Bearer`. |
| 401 | `admin_key_expired` | Clé expirée. |
| 401 | `admin_key_revoked` | Clé révoquée, à la main ou parce que son créateur a quitté l’organisation ou perdu la permission de gérer l’API. |
| 403 | `admin_key_scope_missing` | Portée manquante ; `required_scopes` donne celles qui conviennent. |
| 403 | `admin_key_scope_forbidden` | Route hors de l’administration de l’organisation. |
| 403 | `admin_key_ip_forbidden` | Adresse IP hors des adresses autorisées de la clé. |
| 403 | `admin_key_creator_inactive` | Le compte ou l’adhésion du créateur de la clé est suspendu. |
| 403 | `personal_api_key_forbidden` | Une clé personnelle `sk-…` a été envoyée. |
| 403 | `admin_api_disabled` | L’API d’administration est désactivée sur le serveur. |
| 403 | `forbidden` | La clé n’a pas la permission de cette action, ou l’action vise une personne protégée (le propriétaire, un administrateur). |
| 403 | `role_escalation` | L’action donnerait plus que les permissions de la clé ; `permission` dit laquelle. |
| 404 | `not_found` | Ressource inconnue, ou d’une autre organisation. |
| 409 | `seats_full` | Plus de siège libre dans l’organisation. |
| 409 | `idempotency_in_progress` | Une requête avec la même `Idempotency-Key` est encore en cours. |
| 413 | `invalid_request` | Corps trop volumineux. |
| 422 | `idempotency_key_reused` | Même `Idempotency-Key` avec un autre corps. |
| 429 | `rate_limited` | Trop de requêtes ; attendez `Retry-After` secondes. |
| 503 | `org_policy_unavailable` | Données momentanément illisibles ; réessayez après `Retry-After`. |
| 503 | `idempotency_unavailable` | L’`Idempotency-Key` ne peut pas être vérifiée pour l’instant ; réessayez. |

Les codes propres à une route sont listés sur sa page.

## Limites de débit

Une clé a 600 requêtes par minute, dont 120 écritures (tout ce qui n’est pas `GET`). Chaque réponse porte trois en-têtes :

| En-tête | Valeur |
| --- | --- |
| `X-RateLimit-Limit` | Requêtes permises dans la minute. |
| `X-RateLimit-Remaining` | Requêtes restantes. |
| `X-RateLimit-Reset` | Secondes avant la remise à zéro. |

Au-delà, la réponse est `429 rate_limited` avec `Retry-After`. Au-delà de 30 tentatives avec une clé fausse en 10 minutes depuis la même adresse IP, cette adresse reçoit `429` jusqu’à la fin de la fenêtre.

Les tests qui font contacter une adresse extérieure (webhook, serveur MCP, stockage) ont leur propre limite : 20 tests en 10 minutes par personne (pour une clé, son créateur), puis `429 rate_limited`.

## Requêtes rejouables

Un `POST` accepte l’en-tête `Idempotency-Key` (1 à 255 caractères visibles, par exemple un UUID). Renvoyez la même requête avec la même clé après une coupure réseau : l’action n’est faite qu’une fois.

- La première réponse est gardée 24 heures et rendue à l’identique, avec l’en-tête `Idempotent-Replayed: true`.
- La même clé avec un autre corps répond `422 idempotency_key_reused`.
- Tant que la première requête tourne, la même clé répond `409 idempotency_in_progress` avec `Retry-After: 1`.
- Les réponses `401`, `403`, `429`, `5xx` et celles de plus de 2 Mo ne sont pas gardées : un nouvel essai refait la requête.

La clé d’idempotence vaut pour une clé d’administration : deux clés différentes peuvent utiliser la même valeur sans se gêner.

## Description OpenAPI

La description complète des routes est publique, sans authentification :

```text
https://chat.realmoon.ai/api/v1/orgs/openapi.json
```

Elle suit OpenAPI 3.1 et porte la version `2026-10-30`. Chaque opération indique ses portées dans `x-moon-scopes` (`null` quand elle n’est pas ouverte aux clés), son groupe (`tags`) et, pour les routes qui lisent un corps, le schéma de ce corps. Le schéma `Error` décrit la forme des erreurs et `WebhookEvent` celle des événements. Le fichier peut être gardé en cache 5 minutes.

## Journal d’audit

Chaque écriture faite avec une clé est inscrite au journal d’audit de l’organisation au nom de la clé : l’acteur vaut `api_key:<id de la clé>` et son libellé « Clé d’API « nom » ». Lisez-le avec [`GET /audit`](https://realmoon.ai/documentation/api-organisations/utilisation-et-journal/#audit).

## Routes de l’administration seulement

Ces routes existent sous `/api/v1/orgs/{org_id}/` mais ne répondent qu’à une session ouverte dans l’administration, jamais à une clé (`403 admin_key_scope_missing`, `required_scopes` vide) : `admin-keys` (gestion des clés), `api-settings`, `setup` (carte « Mise en route »), `transfer` et `delete`.

## Référence par domaine

| Page | Routes |
| --- | --- |
| [Organisation et réglages](https://realmoon.ai/documentation/api-organisations/organisation/) | `/overview`, `/alerts`, `/general`, `/settings`, `/directory` |
| [Membres](https://realmoon.ai/documentation/api-organisations/membres/) | `/members`, import, export, opérations en masse |
| [Invitations et codes d’accès](https://realmoon.ai/documentation/api-organisations/invitations/) | `/invitations`, `/access-codes` |
| [Comptes des membres](https://realmoon.ai/documentation/api-organisations/comptes/) | `/members/{user_id}/account`, départ d’un membre, `/security` |
| [Groupes](https://realmoon.ai/documentation/api-organisations/groupes/) | `/groups` |
| [Rôles](https://realmoon.ai/documentation/api-organisations/roles/) | `/roles`, `/role-assignments` |
| [Étiquettes](https://realmoon.ai/documentation/api-organisations/etiquettes/) | `/labels` |
| [Règles](https://realmoon.ai/documentation/api-organisations/regles/) | `/policies` |
| [Modèles de l’organisation](https://realmoon.ai/documentation/api-organisations/modeles/) | `/models` |
| [Licences et contrat](https://realmoon.ai/documentation/api-organisations/licences/) | `/licenses`, `/contract` |
| [Ressources](https://realmoon.ai/documentation/api-organisations/ressources/) | `/resources` |
| [Domaines](https://realmoon.ai/documentation/api-organisations/domaines/) | `/domains` |
| [Utilisation et journal](https://realmoon.ai/documentation/api-organisations/utilisation-et-journal/) | `/usage`, `/audit` |
| [Webhooks](https://realmoon.ai/documentation/api-organisations/webhooks/) | `/webhooks` |
| [Connexion unique](https://realmoon.ai/documentation/api-organisations/sso/#configurer-par-l-api) | Lecture de `/sso` ; l’écriture se fait avec la session du propriétaire ou d’un administrateur |
| [SCIM 2.0](https://realmoon.ai/documentation/api-organisations/scim/#jeton) | Lecture de `/scim-token` ; la création et la révocation se font avec la session du propriétaire ou d’un administrateur |

## Voir aussi

- [Organisations gérées](https://realmoon.ai/documentation/organisations/organisations-gerees/)
- [Webhooks](https://realmoon.ai/documentation/api-organisations/webhooks/)
- [API développeur](https://realmoon.ai/documentation/api/), pour appeler les modèles depuis vos programmes

---

Documentation Moon AI › API des organisations › Authentification et conventions. Page : https://realmoon.ai/documentation/api-organisations/administration/. Modifiée le 5 novembre 2026.

© 2026 Stellarr Studio SAS. Tous droits réservés. Moon AI, Moon Blur et Stellarr Studio sont des marques déposées à l’INPI.
