Lire les cours, déposer un emploi du temps, annuler un cours. Ces routes s'appellent avec une clé d'intégration : voir Conventions. Toutes sont sous /api/institutions/:institutionId, où :institutionId est l'identifiant de l'établissement de la clé, rendu par GET /api/me.
L'objet Cours
{
"id": "9214f192-5c1e-4a0e-9b6e-1c2d3e4f5a6b",
"roomId": "0b6a1f4e-2c3d-4e5f-8a9b-7c6d5e4f3a2b",
"roomShortId": "ABCD2345",
"roomName": "Salle 204",
"occurrenceId": null,
"groupId": "3c2b1a09-8f7e-4d6c-9b5a-4e3d2c1b0a9f",
"groupName": "BTS SIO 1",
"audiences": [],
"teacherId": null,
"title": "Mathématiques",
"startsAt": "2026-10-14T08:00:00.000Z",
"endsAt": "2026-10-14T10:00:00.000Z",
"opensAt": "2026-10-14T07:50:00.000Z",
"closesAt": "2026-10-14T10:15:00.000Z",
"status": "scheduled",
"lastActiveAt": null,
"externalRef": "ENT-12345"
}
| Champ | Type | Description |
|---|---|---|
id | uuid | Identifiant du cours dans MeetingHub, le :lessonId des adresses. C'est lui que porte le webhook attendance.closed, dans sessionId |
roomId | uuid | La salle du cours |
roomShortId | string ou null | L'identifiant court de la salle, 8 caractères. Les participants rejoignent la salle à l'adresse /app/?room= suivie de cet identifiant |
roomName | string ou null | Le nom de la salle ; null si la salle a été supprimée |
occurrenceId | uuid ou null | La série dont le cours est issu ; null pour un cours isolé, dont tous ceux déposés par l'API |
groupId | uuid ou null | La classe attendue ; null si aucune |
groupName | string ou null | Le nom de cette classe |
audiences | object[] | Les classes d'autres centres à qui le cours est aussi diffusé, chacune avec son id et son name ; vide le plus souvent |
teacherId | uuid ou null | L'enseignant désigné pour le cours ; null si aucun |
title | string ou null | L'intitulé du cours |
startsAt | datetime | L'heure de début du cours |
endsAt | datetime | L'heure de fin du cours |
opensAt | datetime | L'heure d'ouverture de la salle, avant le début. Voir Fenêtre d'accès |
closesAt | datetime | L'heure de fermeture de la salle, après la fin |
status | string | L'état du cours. Voir Statuts |
lastActiveAt | datetime ou null | La dernière activité observée dans la salle pendant ce cours ; null si personne n'est venu |
externalRef | string ou null | Votre ref pour ce cours ; null s'il a été créé dans MeetingHub et non par un dépôt |
Statuts
| Valeur | Signification |
|---|---|
scheduled | À venir : la salle n'est pas encore ouverte |
dormant | La salle est ouverte, mais personne n'y est connecté, ou plus personne depuis 90 secondes |
live | Au moins un participant est connecté |
held | Terminé : la fenêtre d'accès est passée, et la feuille de présence a été arrêtée s'il y avait de quoi l'établir |
cancelled | Annulé |
Le statut suit les connexions et un balayage qui passe toutes les 15 minutes. Un cours auquel personne ne s'est connecté peut donc rester scheduled jusqu'à ce que ce balayage le passe held, au plus tard 15 minutes après closesAt.
Fenêtre d'accès
La salle s'ouvre avant le début du cours et se ferme après sa fin. Les deux marges dépendent du type de la salle ; elles ne se fixent pas dans un dépôt.
| Type de salle | Ouverture avant le début | Fermeture après la fin |
|---|---|---|
interview — entretien | 5 minutes | 10 minutes |
meeting — réunion | 10 minutes | 15 minutes |
class — cours | 10 minutes | 15 minutes |
lecture — amphithéâtre | 20 minutes | 20 minutes |
conference — conférence | 30 minutes | 30 minutes |
Une salle créée par un dépôt est toujours de type class.
GET /api/institutions/:institutionId/lessons
Les cours d'une période. Exige schedule.read — la portée schedule:read.
| Paramètre de requête | Type | Défaut | Description |
|---|---|---|---|
from | datetime | maintenant moins 7 jours | Début de la période |
to | datetime | maintenant plus 30 jours | Fin de la période |
groupId | uuid | absent | Ne rend que les cours de cette classe |
La période ne peut pas dépasser 400 jours, et to ne peut pas précéder from. Un cours est rendu s'il chevauche la période : sa fin est postérieure ou égale à from, et son début antérieur ou égal à to. Tous les statuts sont rendus, cours annulés compris.
Réponse 200 :
{
"lessons": [ { "id": "9214f192-…", "status": "scheduled", "…": "…" } ],
"from": "2026-10-12T00:00:00.000Z",
"to": "2026-10-19T00:00:00.000Z"
}
| Champ | Type | Description |
|---|---|---|
lessons | Cours[] | Les cours, triés par heure de début. Voir L'objet Cours |
from | datetime | Le début de la période retenue |
to | datetime | La fin de la période retenue |
| Code | Corps | Cause |
|---|---|---|
400 | date de début illisible | from n'est pas une date |
400 | date de fin illisible | to n'est pas une date |
400 | la fin précède le début | to est avant from |
400 | fenêtre trop large — 400 jours au maximum | La période dépasse 400 jours |
403 | cet accès ne permet pas cela — il faut : schedule.read | Portée absente |
PUT /api/institutions/:institutionId/lessons
Dépose un emploi du temps. Exige schedule.write — la portée schedule:write.
Le dépôt est rejouable : chaque cours est reconnu par votre ref, et un même corps envoyé deux fois ne change rien. Un cours absent du dépôt n'est PAS annulé.
| Paramètre de requête | Type | Défaut | Description |
|---|---|---|---|
dryRun | string | absent | Absent, false ou 0 : le dépôt est appliqué. Toute autre valeur — true, 1, vide — fait un essai à blanc, qui n'écrit rien |
Corps de la requête
{
"sessions": [
{
"ref": "ENT-12345",
"room": { "ref": "salle-204", "name": "Salle 204" },
"group": { "ref": "bts-sio-1", "name": "BTS SIO 1" },
"title": "Mathématiques",
"startsAt": "2026-10-14T08:00:00Z",
"durationMinutes": 120,
"cancelled": false
}
]
}
| Champ | Type | Obligatoire | Règles |
|---|---|---|---|
sessions | object[] | oui | Les cours, de 1 à 5 000 entrées |
Chaque entrée de sessions :
| Champ | Type | Obligatoire | Règles |
|---|---|---|---|
ref | string | oui | Votre identifiant du cours. 1 à 200 caractères, espaces de bord retirés, tronqué au-delà |
room | object ou string | oui | La salle. Une chaîne vaut { "ref": … } sans nom |
room.ref | string | oui | Votre identifiant de la salle, mêmes règles que ref |
room.name | string | non | Le nom de la salle, 200 caractères au plus. Utilisé seulement si la salle est créée par ce dépôt ; ignoré sinon |
room.centre | string | non | Le centre de la salle : sa référence dans l'établissement, à défaut son nom. Donné à une salle créée, ou à une salle qui n'a pas encore de centre ; une salle déjà rangée dans un centre n'en change pas |
group | object ou null | non | La classe attendue. Absent ou null : aucune classe |
group.ref | string | oui, si group est fourni | Votre identifiant de la classe, mêmes règles que ref |
group.name | string | non | Le nom de la classe, 200 caractères au plus. Utilisé seulement si la classe est créée par ce dépôt |
group.centre | string | non | Le centre de la classe, mêmes règles que room.centre |
title | string ou null | non | L'intitulé, 200 caractères au plus. Vide : null |
startsAt | datetime | oui | L'heure de début. Au plus 90 jours dans le passé, au plus 400 jours dans le futur |
durationMinutes | number | oui | La durée en minutes, de 1 à 1 440, arrondie à l'entier |
cancelled | boolean | non | true annule le cours. Toute autre valeur est ignorée |
Traitement
- Chaque entrée est lue et vérifiée seule. Une entrée fautive est refusée, les autres continuent.
- Le dépôt est confronté à lui-même : deux entrées de même
ref, ou deux cours non annulés qui se chevauchent dans la même salle, sont refusés TOUS LES DEUX. Deux créneaux qui se touchent — fin de l'un égale au début de l'autre — ne se chevauchent pas. Une entrée dontroom.centreougroup.centrene désigne aucun centre de l'établissement est refusée. - Les salles et les classes sont cherchées par leur
ref. Celles qui n'existent pas sont créées, sauf en essai à blanc : une salle de typeclasset de capacité 80, nomméeroom.name, ouroom.refà défaut ; une classe nomméegroup.name, ougroup.refà défaut. Chacune reçoit le centre donné par le dépôt, s'il y en a un. - Chaque entrée restante est appliquée. Une entrée
cancelled: trueannule le cours s'il existe. Sinon, le cours est refusé si un AUTRE cours non annulé occupe déjà la salle sur ce créneau ; il est laissé tel quel si rien n'a changé ; il est déplacé s'il existe ; il est créé sinon.
Règles qui en découlent :
- Un cours
heldne se déplace plus : l'entrée est refusée. - Le plafond de simultanéité de votre offre n'est vérifié qu'à la création d'un cours, pas à son déplacement.
- Un cours
cancelledne se rétablit pas par un dépôt : l'entrée est rapportéeupdated, mais le cours reste annulé. - En essai à blanc, un cours dont la salle n'existe pas encore est rapporté
createdsans que l'occupation de la salle puisse être vérifiée. - Une annulation par cette route n'émet pas l'événement
session.cancelled.
Réponse
201 si le dépôt est appliqué, 200 si c'est un essai à blanc — dans les deux cas même si des entrées sont refusées.
{
"dryRun": false,
"read": 3,
"created": 1,
"updated": 1,
"unchanged": 0,
"cancelled": 0,
"rejected": 1,
"roomsCreated": 1,
"groupsCreated": 0,
"lines": [
{ "ref": "ENT-12345", "outcome": "created", "sessionId": "9214f192-…" },
{ "ref": "ENT-12346", "outcome": "updated", "sessionId": "7a1b2c3d-…" },
{
"ref": "ENT-12347",
"outcome": "rejected",
"sessionId": null,
"reason": "salle « salle-204 » déjà occupée de 2026-10-14T08:00:00.000Z à 2026-10-14T10:00:00.000Z"
}
]
}
| Champ | Type | Description |
|---|---|---|
dryRun | boolean | true pour un essai à blanc |
read | integer | Le nombre d'entrées reçues |
created | integer | Cours créés, ou qui le seraient en essai à blanc |
updated | integer | Cours déplacés ou modifiés |
unchanged | integer | Cours déjà à jour, et annulations sans effet |
cancelled | integer | Cours annulés par ce dépôt |
rejected | integer | Entrées refusées |
roomsCreated | integer | Salles créées ; toujours 0 en essai à blanc |
groupsCreated | integer | Classes créées ; toujours 0 en essai à blanc |
lines | LigneDuRapport[] | Une ligne par entrée reçue, triées par ref dans l'ordre alphabétique |
Chaque ligne du rapport :
| Champ | Type | Description |
|---|---|---|
ref | string | Votre ref. Pour une entrée refusée sans ref lisible : ligne N, où N est sa position dans sessions, en comptant à partir de 0 |
outcome | string | created, updated, unchanged, cancelled ou rejected |
sessionId | uuid ou null | Le cours dans MeetingHub — son :lessonId. null pour une création en essai à blanc et pour la plupart des refus |
reason | string | Présent seulement quand outcome vaut rejected : la cause du refus |
Causes de refus d'une entrée
reason | Cause |
|---|---|
ligne illisible | L'entrée n'est pas un objet |
« ref » manquante — c’est elle qui rend le dépôt rejouable | ref absente ou vide |
« room.ref » manquante | Salle absente, ou sans ref |
« group.ref » manquante | group fourni sans ref |
centre « … » inconnu de l’établissement | room.centre ou group.centre ne désigne aucun centre, ni par sa référence ni par son nom |
« startsAt » absente ou illisible | startsAt absente ou pas une date |
cours vieux de plus de 90 jours — … | startsAt plus de 90 jours dans le passé |
cours au-delà de 400 jours — année scolaire dépassée | startsAt plus de 400 jours dans le futur |
« durationMinutes » absente ou nulle | Durée absente, pas un nombre, ou inférieure à 1 |
un cours de plus de vingt-quatre heures est une erreur de saisie | Durée supérieure à 1 440 minutes |
« … » apparaît N fois dans ce dépôt | Plusieurs entrées portent la même ref |
chevauchement dans « … » entre « … » et « … » | Deux entrées du dépôt se chevauchent dans la même salle |
salle « … » déjà occupée de … à … | Un cours déjà enregistré occupe le créneau ; les heures sont en ISO 8601 |
cours déjà tenu — sa feuille de présence porte l’heure réelle | Le cours est held |
N salle(s) de ce type déjà retenue(s) sur ce créneau — le plafond est de M | Le plafond de simultanéité de votre offre est atteint |
Erreurs de la requête entière
| Code | Corps | Cause |
|---|---|---|
400 | attendu : { "sessions": [...] } | Le corps n'a pas de tableau sessions |
400 | dépôt vide | sessions est vide |
400 | N cours — le maximum est 5000 par dépôt | Plus de 5 000 entrées |
403 | cet accès ne permet pas cela — il faut : schedule.write | Portée absente |
503 | dépôt indisponible pour le moment | L'instance tourne sans base de données |
DELETE /api/institutions/:institutionId/lessons/by-ref/:ref
Annule un cours déposé, par votre ref. Exige schedule.write — la portée schedule:write.
| Paramètre de chemin | Type | Description |
|---|---|---|
ref | string | Votre ref du cours, encodée pour une URL |
Réponse 200 :
{ "ref": "ENT-12345", "outcome": "cancelled", "sessionId": "9214f192-5c1e-4a0e-9b6e-1c2d3e4f5a6b"
}
| Champ | Type | Description |
|---|---|---|
ref | string | La ref demandée |
outcome | string | cancelled si le cours vient d'être annulé ; unchanged s'il l'était déjà |
sessionId | uuid | Le cours dans MeetingHub — son :lessonId |
L'annulation libère la place que le cours retenait dans le plafond de simultanéité. Elle n'émet pas l'événement session.cancelled.
| Code | Corps | Cause |
|---|---|---|
400 | référence manquante | ref vide |
403 | cet accès ne permet pas cela — il faut : schedule.write | Portée absente |
404 | cours « … » inconnu | Aucun cours ne porte cette ref dans l'établissement |
409 | cours déjà tenu — sa feuille de présence porte l’heure réelle | Le cours est held |
Les autres routes des cours
La même clé atteint aussi, selon ses portées, GET …/lessons/:lessonId (un cours), GET …/lessons/conflicts (les chevauchements d'une période) et les écritures à l'unité : poser un cours, une série, désigner l'enseignant, diffuser à d'autres classes, annuler par DELETE …/lessons/:lessonId. Cette dernière annulation, elle, émet session.cancelled. Chaque route, et ce qu'elle exige, est dans Conventions.