# L'API CoolRoom

Construis ton app, ton widget ou ton script sur les données de CoolRoom : devoirs, notes, emploi du temps, cours, exercices… Toujours avec l'accord de l'élève, en lecture seule, avec des limites qu'il choisit.

Adresse de l'API : `https://api.coolroom.app/api/open/v1` · https://coolroom.app/developpeurs/

## Démarrer

1. **Crée une clé** : Dans l'appli : Réglages › API et applications › Créer une clé. Coche les données, choisis l'expiration et les limites. Copie la clé : elle commence par crk_ et ne s'affiche qu'une fois.
2. **Appelle l'API** : Envoie-la dans l'en-tête Authorization de chaque requête. Les réponses sont en JSON, les dates en ISO 8601 (UTC).
3. **Pour une app publique** : Ne demande jamais de clé à tes utilisateurs : utilise le lien « Se connecter avec CoolRoom ». L'élève accepte en un clic et ton app reçoit sa clé.

### Ta première requête

```sh
curl "https://api.coolroom.app/api/open/v1/homework" \
  -H "Authorization: Bearer crk_…"
```

## Authentification

Chaque route de /api/open/v1 demande une clé, dans Authorization: Bearer crk_… (ou X-API-Key: crk_…). Une clé lit les données d'un seul compte, seulement ses scopes, et jamais rien d'autre de CoolRoom. L'API est en lecture seule : aucune route n'écrit, n'envoie ni ne supprime de données de l'élève.

```http
Authorization: Bearer crk_Xb7Qm2LpV0x9…
```

- Une clé, c'est un mot de passe : jamais dans le code d'une page web publique, jamais dans un dépôt Git.
- CoolRoom ne garde que l'empreinte (SHA-256) de la clé : si elle est perdue, l'élève en crée une autre.
- L'élève voit chaque clé dans Réglages › API et applications et peut la retirer à tout moment.
- Ton app reçoit exactement ce que l'élève voit dans l'appli : mêmes classes, mêmes droits. Jamais ses mots de passe ni ses identifiants Pronote.

---

# Se connecter avec CoolRoom

Le moyen le plus rapide pour un élève : ton app ouvre un lien, l'élève voit le nom de ton app, ce qu'elle veut lire et pour combien de temps, puis touche Autoriser. CoolRoom renvoie vers ton app avec un code à usage unique, que ton serveur (ou ton app, avec PKCE) échange contre la clé.

### GET /api/public/connect

**1. Le lien « Se connecter avec CoolRoom »** · sans clé

Ouvre-le dans le navigateur de l’élève. CoolRoom vérifie les paramètres puis montre l’écran d’autorisation (l’élève se connecte s’il faut). Un lien cassé répond 400 avec la raison.

| Paramètres | | |
|---|---|---|
| `app_name` | requête, **obligatoire** | Le nom de ton app (2 à 60 caractères), montré à l’élève. |
| `redirect_uri` | requête, **obligatoire** | Où renvoyer l’élève : https://…, http://localhost…, ou le schéma de ton app (monapp://callback). Sans #. |
| `scope` | requête, **obligatoire** | Les scopes, séparés par des virgules. |
| `state` | requête | Renvoyé tel quel : vérifie-le au retour (200 caractères au plus). |
| `code_challenge` | requête | PKCE : base64url(SHA-256(code_verifier)). Recommandé, obligatoire pour une app sans serveur. |
| `code_challenge_method` | requête | S256 (le seul accepté). |
| `expires_in` | requête | Durée de vie de la clé en secondes (3 600 à 31 622 400). Sans : jusqu’à ce que l’élève la retire. |
| `hourly_limit` | requête | Requêtes par heure (1 à 3 600, 600 par défaut). |
| `total_limit` | requête | Requêtes en tout (sans : illimité). |

Requête :

```sh
curl "https://api.coolroom.app/api/public/connect?app_name=Agenda%20Plus&redirect_uri=https%3A%2F%2Fagenda-plus.example%2Fcallback&scope=homework%2Ctimetable&state=k8Jd2&code_challenge=ITAt2F6NLydvh8G3CenAG9Ty-j_gUaFU1f886B2WqE0&code_challenge_method=S256&expires_in=2592000"
```

Réponse : 302 · Redirection vers l'écran d'autorisation.

### GET {redirect_uri}

**2. Le retour vers ton app** · sans clé

Autorisé : ?code=…&state=…. Refusé : ?error=access_denied&state=…. Le code marche une fois, pendant 10 minutes.

Requête :

```text
https://agenda-plus.example/callback?code=Qm9uam91ciBkw6l2ZWxvcHBldXI&state=k8Jd2
```

Réponse : 200 · Pas de contenu.

### POST /api/public/connect/token

**3. Échanger le code contre la clé** · sans clé

redirectUri doit être exactement celle du lien. codeVerifier est obligatoire si le lien avait un code_challenge. Garde apiKey en lieu sûr : elle n’est plus jamais redonnée.

| Paramètres | | |
|---|---|---|
| `code` | corps, **obligatoire** | Le code reçu. |
| `redirectUri` | corps, **obligatoire** | La même que dans le lien. |
| `codeVerifier` | corps | Le secret PKCE (43 à 128 caractères). |

Requête :

```sh
curl -X POST "https://api.coolroom.app/api/public/connect/token" \
  -H "Content-Type: application/json" \
  -d '{"code":"Qm9uam91ciBkw6l2ZWxvcHBldXI","redirectUri":"https://agenda-plus.example/callback","codeVerifier":"dBjftJeZ4CVP-mJ92K9eQ_Ft_3zsHpPhEbbsvh3JJPA"}'
```

Réponse (200) :

```json
{
  "apiKey": "crk_Xb7Qm2LpV0x9…",
  "keyId": "0f3b2a1c-9e8d-4c7b-a6f5-e4d3c2b1a090",
  "scopes": [
    "homework",
    "timetable"
  ],
  "expiresAt": "2026-11-20T08:12:40Z",
  "hourlyLimit": 600,
  "totalLimit": null,
  "user": {
    "id": "7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15",
    "displayName": "Léa Martin",
    "accountType": "STUDENT"
  }
}
```

### GET /api/public/connect/scopes

**La liste des scopes** · sans clé

Chaque scope avec son nom et sa phrase en français, comme l’élève les voit.

Requête :

```sh
curl "https://api.coolroom.app/api/public/connect/scopes"
```

Réponse (200) :

```json
[
  {
    "key": "homework",
    "label": "Devoirs",
    "description": "Les devoirs de tes classes et ce que tu as fini"
  }
]
```

## PKCE en JavaScript

Pour une app mobile ou une page sans serveur : crée un secret au hasard, garde-le, envoie son empreinte dans le lien, puis le secret à l'échange. Même si quelqu'un vole le code, il ne peut rien en faire.

---

# Limites

Chaque clé a ses limites, choisies à la création (dans l'appli, ou par les paramètres du lien) :

- **Par heure** : 1 à 3 600 requêtes (600 par défaut). Au-delà : 429 avec Retry-After.
- **En tout** : Facultatif. Une fois atteint : 429 quota_exceeded, pour toujours.
- **Expiration** : 1 heure à 366 jours, ou jamais. Après : 401 key_expired.
- **Clés par compte** : 20 clés actives au plus.

Chaque réponse dit où tu en es : X-RateLimit-Limit, X-RateLimit-Remaining (dans l'heure) et X-Total-Remaining (si la clé a une limite totale).

Ton app peut aussi se limiter elle-même : PATCH /key pour réduire ses scopes, son expiration ou ses limites, DELETE /key pour rendre la clé (un bouton « Déconnecter » dans ton app). Jamais pour en avoir plus.

# Erreurs

Une erreur répond toujours avec la même forme. Teste code, affiche message (en français).

```json
{
  "status": 403,
  "code": "insufficient_scope",
  "message": "Cette clé n’a pas le scope « grades » (Notes)",
  "path": "/api/open/v1/grades",
  "timestamp": "2026-10-21T17:30:00Z"
}
```

| Statut | code | Quand |
|---|---|---|
| 400 | `invalid_request` | Un paramètre manque ou ne va pas (le message dit lequel). |
| 400 | `invalid_scope` | Un scope inconnu dans le lien de connexion. |
| 400 | `invalid_grant` | Code de connexion inconnu, déjà utilisé, expiré, ou redirectUri / codeVerifier différents. |
| 401 | `missing_key` | Pas de clé dans la requête. |
| 401 | `invalid_key` | Clé inconnue ou mal formée (une clé commence par crk_). |
| 401 | `key_revoked` | L’élève (ou ton app) a révoqué la clé. |
| 401 | `key_expired` | La clé a passé sa date d’expiration. |
| 403 | `insufficient_scope` | La clé n’a pas le scope de cette route. |
| 403 | `cannot_widen` | PATCH /key ne peut que réduire une clé. |
| 403 | `forbidden` | L’élève n’a pas accès à cette chose dans l’appli non plus. |
| 404 | `not_found` | Introuvable, ou pas à cet élève. |
| 405 | `method_not_allowed` | L’API est en lecture seule. |
| 409 | `too_many_keys` | L’élève a déjà 20 clés actives. |
| 429 | `rate_limited` | Limite par heure atteinte : attends Retry-After secondes. |
| 429 | `quota_exceeded` | La clé a utilisé toutes ses requêtes (limite totale). |
| 500 | `server_error` | Erreur de notre côté. Réessaie plus tard. |

# Scopes

Les données qu'une clé peut lire. Dans le lien, sépare-les par des virgules (scope=homework,grades) ; all les demande toutes.

| Scope | Donne accès à |
|---|---|
| `profile` | **Profil** · Nom, bio, type de compte, langue. |
| `email` | **Adresse email** · Ajoute l’adresse email au profil (avec profile). |
| `classes` | **Classes** · Les classes, leurs membres et leurs annonces. |
| `homework` | **Devoirs** · Les devoirs de chaque classe, faits ou non, votes et notes. |
| `grades` | **Notes** · Les notes, les moyennes de classe et les objectifs. |
| `timetable` | **Emploi du temps** · Les cours, salles, profs, annulations et retenues. |
| `school_life` | **Vie scolaire** · Absences et retards. |
| `mail` | **Messagerie** · Les mails de l’ENT (Pronote, Skolengo), en lecture. |
| `school_accounts` | **Comptes scolaires** · Les comptes reliés et leur synchro. Jamais les identifiants. |
| `chat` | **Chat de classe** · Les messages des chats de classe. |
| `cours` | **Cours** · La bibliothèque de cours, chaque cours en Markdown, le programme. |
| `exercises` | **Exercices** · Entraînements : résultats carte par carte, erreurs, points faibles. |
| `flashcards` | **Flash cards** · Les paquets de cartes et les parties jouées. |
| `progress` | **Niveau et CC** · Niveau, XP, CC, série, saison. |
| `notifications` | **Notifications** · Les 100 dernières notifications. |
| `ai` | **IA** · La conversation avec l’IA de CoolRoom. |
| `family` | **Famille** · Pour un compte parent : notes, devoirs et emploi du temps des enfants reliés. |

---

# Routes : La clé elle-même

Scope : toute clé

Sans scope : toute clé valide peut lire ses limites, les réduire, ou se révoquer (un bouton « Déconnecter » dans ton app).

### GET /api/open/v1/key

**Lire la clé**

Ses scopes, son expiration, ses limites et ce qui reste dans l’heure.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/key" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "id": "0f3b2a1c-9e8d-4c7b-a6f5-e4d3c2b1a090",
  "name": "Agenda Plus",
  "prefix": "crk_Xb7Qm2Lp",
  "scopes": [
    "homework",
    "timetable"
  ],
  "createdAt": "2026-10-21T08:12:40Z",
  "expiresAt": "2026-11-20T08:12:40Z",
  "hourlyLimit": 600,
  "hourlyRemaining": 597,
  "totalLimit": null,
  "usedRequests": 214
}
```

### PATCH /api/open/v1/key

**Réduire la clé**

Ton app se limite elle-même : moins de scopes, une expiration plus proche, des limites plus basses. Jamais l’inverse (403 cannot_widen). Ce que tu n’envoies pas ne change pas.

| Paramètres | | |
|---|---|---|
| `scopes` | corps | Une partie des scopes actuels. |
| `expiresAt` | corps | Date ISO 8601, dans le futur et avant l’expiration actuelle. |
| `hourlyLimit` | corps | De 1 à la limite actuelle. |
| `totalLimit` | corps | Requêtes en tout, au plus la limite actuelle. |

Requête :

```sh
curl -X PATCH "https://api.coolroom.app/api/open/v1/key" \
  -H "Authorization: Bearer $COOLROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scopes":["homework"],"hourlyLimit":100,"totalLimit":5000}'
```

Réponse (200) :

```json
{
  "id": "0f3b2a1c-9e8d-4c7b-a6f5-e4d3c2b1a090",
  "name": "Agenda Plus",
  "prefix": "crk_Xb7Qm2Lp",
  "scopes": [
    "homework"
  ],
  "createdAt": "2026-10-21T08:12:40Z",
  "expiresAt": "2026-11-20T08:12:40Z",
  "hourlyLimit": 100,
  "hourlyRemaining": 97,
  "totalLimit": 5000,
  "usedRequests": 214
}
```

### DELETE /api/open/v1/key

**Révoquer la clé**

La clé s’arrête tout de suite, pour toujours.

Requête :

```sh
curl -X DELETE "https://api.coolroom.app/api/open/v1/key" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse : 204 · Pas de contenu.

---

# Routes : Profil

Scope : `profile`

Le compte derrière la clé.

### GET /api/open/v1/me

**Le compte**

accountType vaut STUDENT ou PARENT. email et emailVerified ne sont remplis qu’avec le scope email (null sinon).

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/me" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "id": "7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15",
  "displayName": "Léa Martin",
  "bio": "Seconde 4, team SVT",
  "accountType": "STUDENT",
  "locale": "fr",
  "avatarIconId": "fox",
  "memberSince": "2026-09-02T17:21:05Z",
  "email": "lea.martin@example.fr",
  "emailVerified": true
}
```

---

# Routes : Classes

Scope : `classes`

Les classes de l’élève. Le code d’invitation n’est jamais donné.

### GET /api/open/v1/classes

**Mes classes**

myRole : MEMBER, CO_ADMIN ou CLASS_MASTER. status : APPROVED, LOCKED, ARCHIVED…

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/classes" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c",
    "name": "2nde 4",
    "description": "La meilleure classe du lycée",
    "etablissementId": "e7ab1100-4c3d-4e2f-9a8b-7c6d5e4f3a2b",
    "etablissementName": "Lycée Jean Zay",
    "classMasterName": "Tom Durand",
    "status": "APPROVED",
    "memberCount": 28,
    "myRole": "MEMBER",
    "subjectColors": {
      "Mathématiques": "#3B82F6",
      "SVT": "#22C55E"
    },
    "languages": {
      "LV2": [
        "ESPAGNOL LV2",
        "ALLEMAND LV2"
      ]
    }
  }
]
```

### GET /api/open/v1/classes/{classId}

**Une classe**

Même forme qu’un élément de /classes.

| Paramètres | | |
|---|---|---|
| `classId` | chemin, **obligatoire** | Une classe de l’élève. |

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "id": "c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c",
  "name": "2nde 4",
  "memberCount": 28,
  "myRole": "MEMBER",
  "…": "…"
}
```

### GET /api/open/v1/classes/{classId}/members

**Les membres**

status : ONLINE, FOCUS, IN_LESSON, AWAY ou OFFLINE. lastSeenAt est arrondi à 5 minutes, null après une semaine.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c/members" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "userId": "a2b3c4d5-e6f7-4a8b-9c0d-1e2f3a4b5c6d",
    "displayName": "Tom Durand",
    "avatarIconId": "owl",
    "avatarPhotoVersion": null,
    "bio": "Délégué",
    "role": "CLASS_MASTER",
    "online": true,
    "muted": false,
    "inFocus": false,
    "status": "ONLINE",
    "lastSeenAt": "2026-10-21T15:40:00Z",
    "frame": null
  }
]
```

### GET /api/open/v1/classes/{classId}/announcements

**Les annonces**

Les annonces de la classe, les plus récentes d’abord.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c/announcements" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
    "title": "Sortie au musée jeudi",
    "body": "Rendez-vous 8 h devant le lycée, prévoyez un pique-nique.",
    "authorName": "Tom Durand",
    "authorRole": "CLASS_MASTER",
    "createdAt": "2026-10-20T18:05:00Z",
    "seen": true,
    "canDelete": false
  }
]
```

---

# Routes : Chat de classe

Scope : `chat`

Les messages, du plus ancien au plus récent, par pages.

### GET /api/open/v1/classes/{classId}/chat

**Les messages**

type : TEXT, LESSON, FLASHCARDS, GIVEAWAY, MEDIA, STICKER, ASSISTANT… Les photos et vidéos ne sont pas servies par l’API (media donne seulement leurs infos).

| Paramètres | | |
|---|---|---|
| `before` | requête | Date ISO : les messages avant (page précédente). |
| `limit` | requête | 1 à 100. |

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c/chat?limit=50" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "pinned": null,
  "messages": [
    {
      "id": "d4e5f6a7-b8c9-4d0e-8f1a-2b3c4d5e6f7a",
      "type": "TEXT",
      "author": {
        "id": "a2b3c4d5-e6f7-4a8b-9c0d-1e2f3a4b5c6d",
        "displayName": "Tom Durand",
        "avatarIconId": "owl",
        "avatarPhotoVersion": null,
        "frame": null
      },
      "body": "Quelqu’un a compris l’exo 14 ?",
      "createdAt": "2026-10-21T16:02:33Z",
      "quote": null,
      "reactions": [
        {
          "emoji": "👀",
          "count": 3,
          "names": [
            "Léa",
            "Inès",
            "Hugo"
          ],
          "reactedByMe": true
        }
      ],
      "mentionedRaw": null,
      "pinned": false,
      "mine": false,
      "deck": null,
      "giveaway": null,
      "lesson": null,
      "sources": null,
      "media": null,
      "sticker": null
    }
  ],
  "typing": []
}
```

---

# Routes : Devoirs

Scope : `homework`

source : OFFICIAL (Pronote, ENT) ou MANUAL (ajouté par un élève). status : OFFICIEL, CONFIRME ou A_VERIFIER. done et snoozed sont propres à l’élève.

### GET /api/open/v1/homework

**Les devoirs, par classe**

Toutes les classes, ou une seule avec ?classId=.

| Paramètres | | |
|---|---|---|
| `classId` | requête | Une seule classe. |

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/homework" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "classes": [
    {
      "classId": "c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c",
      "className": "2nde 4",
      "homework": [
        {
          "id": "9d2f7a10-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
          "subject": "Mathématiques",
          "title": "Exercices 12 à 15 p. 84",
          "description": "Faire les exercices sur les fonctions affines.",
          "dueAt": "2026-10-23T06:00:00Z",
          "source": "OFFICIAL",
          "status": "OFFICIEL",
          "isExam": false,
          "done": false,
          "completedAt": null,
          "snoozed": false,
          "submitterId": null,
          "submitterName": null,
          "confirmations": 0,
          "disagreements": 0,
          "myVote": null,
          "latestNote": "Le 15 b) est corrigé en classe",
          "latestNoteAuthor": "Tom",
          "photoVersion": null
        }
      ]
    }
  ]
}
```

### GET /api/open/v1/homework/{homeworkId}

**Un devoir**

Le devoir et toutes ses notes.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/homework/9d2f7a10-3b4c-4d5e-8f6a-7b8c9d0e1f2a" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "homework": {
    "id": "9d2f7a10-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
    "subject": "Mathématiques",
    "title": "Exercices 12 à 15 p. 84",
    "description": "Faire les exercices sur les fonctions affines.",
    "dueAt": "2026-10-23T06:00:00Z",
    "source": "OFFICIAL",
    "status": "OFFICIEL",
    "isExam": false,
    "done": false,
    "completedAt": null,
    "snoozed": false,
    "submitterId": null,
    "submitterName": null,
    "confirmations": 0,
    "disagreements": 0,
    "myVote": null,
    "latestNote": "Le 15 b) est corrigé en classe",
    "latestNoteAuthor": "Tom",
    "photoVersion": null
  },
  "notes": [
    {
      "id": "e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b",
      "authorId": "a2b3c4d5-e6f7-4a8b-9c0d-1e2f3a4b5c6d",
      "authorName": "Tom",
      "authorAvatarIconId": "owl",
      "authorPhotoVersion": null,
      "body": "Le 15 b) est corrigé en classe",
      "createdAt": "2026-10-21T12:30:00Z",
      "mine": false,
      "canDelete": false
    }
  ],
  "canEdit": false,
  "canVote": false,
  "confirmationThreshold": 3
}
```

---

# Routes : Notes

Scope : `grades`

Les notes de l’élève, les plus récentes d’abord. Les nombres sont des décimaux.

### GET /api/open/v1/grades

**Les notes**

revealed : déjà ouverte dans l’appli. La valeur est toujours donnée.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/grades" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "3a4b5c6d-7e8f-4a1b-9c2d-3e4f5a6b7c8d",
    "subject": "Physique-Chimie",
    "title": "Contrôle : les ondes",
    "value": 15.5,
    "outOf": 20,
    "classAverage": 12.25,
    "classMin": 6,
    "classMax": 18.5,
    "coefficient": 2,
    "period": "Trimestre 1",
    "date": "2026-10-17",
    "revealed": true,
    "masked": false
  }
]
```

### GET /api/open/v1/grades/goals

**Les objectifs**

subject vide = la moyenne générale.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/grades/goals" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "subject": "",
    "target": 14
  },
  {
    "subject": "Mathématiques",
    "target": 15
  }
]
```

---

# Routes : Emploi du temps

Scope : `timetable`

Les heures sont en UTC (Z). detention : une retenue.

### GET /api/open/v1/timetable

**Les cours entre deux dates**

De from à to inclus, 62 jours au plus (heure de Paris).

| Paramètres | | |
|---|---|---|
| `from` | requête, **obligatoire** | AAAA-MM-JJ. |
| `to` | requête, **obligatoire** | AAAA-MM-JJ. |

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/timetable?from=2026-10-19&to=2026-10-25" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "6b7c8d9e-0f1a-4b2c-8d3e-4f5a6b7c8d9e",
    "subject": "Histoire-Géographie",
    "teacher": "M. Laurent",
    "room": "B204",
    "startAt": "2026-10-22T07:00:00Z",
    "endAt": "2026-10-22T08:00:00Z",
    "canceled": false,
    "note": null,
    "detention": false
  },
  {
    "id": "7c8d9e0f-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
    "subject": "Anglais",
    "teacher": "Mme Smith",
    "room": "A12",
    "startAt": "2026-10-22T08:00:00Z",
    "endAt": "2026-10-22T09:00:00Z",
    "canceled": true,
    "note": "Prof absente",
    "detention": false
  }
]
```

---

# Routes : Vie scolaire

Scope : `school_life`

kind : ABSENCE ou DELAY. Les retenues sont dans l’emploi du temps.

### GET /api/open/v1/school-life

**Absences et retards**

Les plus récents d’abord.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/school-life" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "f6a7b8c9-d0e1-4f2a-9b3c-4d5e6f7a8b9c",
    "kind": "DELAY",
    "title": "Retard",
    "reason": "Transport",
    "detail": null,
    "giver": null,
    "startAt": "2026-10-20T07:05:00Z",
    "endAt": null,
    "scheduledAt": null,
    "durationMinutes": 10,
    "justified": true
  }
]
```

---

# Routes : Messagerie

Scope : `mail`

Les mails de l’ENT, en lecture. Les lire ici ne les marque pas comme lus.

### GET /api/open/v1/mail

**La boîte de réception**

type : TEACHER, ADMIN, SYSTEM ou OTHER. q cherche dans le sujet, l’expéditeur et le texte.

| Paramètres | | |
|---|---|---|
| `q` | requête | Recherche. |
| `type` | requête | Filtre par expéditeur. |

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/mail?type=TEACHER" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "messages": [
    {
      "id": "a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d",
      "provider": "PRONOTE",
      "providers": [
        "PRONOTE"
      ],
      "subject": "Conseil de classe",
      "snippet": "Bonjour, le conseil de classe aura lieu le…",
      "senderName": "Mme Bernard",
      "senderType": "TEACHER",
      "receivedAt": "2026-10-21T10:15:00Z",
      "read": false,
      "messageCount": 1,
      "sharedAt": null
    }
  ],
  "unreadCount": 1,
  "countsByType": {
    "TEACHER": 4,
    "ADMIN": 2,
    "SYSTEM": 0,
    "OTHER": 1
  }
}
```

### GET /api/open/v1/mail/{mailId}

**Un mail et sa conversation**

Peut prendre quelques secondes la première fois (CoolRoom va chercher le texte chez Pronote ou Skolengo).

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/mail/a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "mail": {
    "id": "a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d",
    "subject": "Conseil de classe",
    "senderName": "Mme Bernard",
    "…": "…"
  },
  "thread": [
    {
      "author": "Mme Bernard",
      "sentAt": "2026-10-21T10:15:00Z",
      "content": "Bonjour, le conseil de classe aura lieu le 4 décembre.",
      "fromMe": false
    }
  ]
}
```

---

# Routes : Comptes scolaires

Scope : `school_accounts`

Les comptes reliés et leur synchro. Les identifiants ne sortent jamais du serveur.

### GET /api/open/v1/school-accounts

**Les comptes reliés**

provider : PRONOTE, SKOLENGO ou PRONOTE_ENT_AURA. status : PENDING, CONNECTED, ERROR ou DISCONNECTED.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/school-accounts" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "b8c9d0e1-f2a3-4b4c-9d5e-6f7a8b9c0d1e",
    "provider": "PRONOTE",
    "status": "CONNECTED",
    "schoolLabel": "Lycée Jean Zay",
    "lastSyncAt": "2026-10-21T16:00:00Z",
    "lastLogonRefreshAt": "2026-10-21T16:04:00Z",
    "lastError": null,
    "syncMail": true,
    "syncHomework": true,
    "syncGrades": true,
    "syncTimetable": true,
    "available": [
      "GRADES",
      "HOMEWORK",
      "MAIL",
      "SCHOOL_LIFE",
      "TIMETABLE"
    ],
    "unavailable": []
  }
]
```

---

# Routes : Cours

Scope : `cours`

La bibliothèque de cours de chaque classe. Chaque cours est du Markdown.

### GET /api/open/v1/cours/classes/{classId}

**La bibliothèque**

Matières, dossiers, cours (sans leur texte), liens et tags.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/cours/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "classId": "c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c",
  "className": "2nde 4",
  "subjects": [
    {
      "key": "svt",
      "name": "SVT",
      "icon": "microscope",
      "lessonCount": 7,
      "folderCount": 2,
      "fromTimetable": true,
      "lastUpdated": "2026-10-15T09:40:00Z"
    }
  ],
  "folders": [
    {
      "id": "1d2e3f4a-5b6c-4d7e-8f9a-0b1c2d3e4f5a",
      "subjectKey": "svt",
      "parentId": null,
      "title": "Chapitre 2",
      "icon": null,
      "aiMade": false,
      "position": 0
    }
  ],
  "lessons": [
    {
      "id": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
      "subjectKey": "svt",
      "subjectName": "SVT",
      "folderId": null,
      "title": "La cellule, unité du vivant",
      "tags": [
        "chapitre-2",
        "cellule"
      ],
      "visibility": "CLASS",
      "status": "COMPLETE",
      "givenOn": "2026-10-14",
      "createdAt": "2026-10-14T16:02:11Z",
      "updatedAt": "2026-10-15T09:40:00Z",
      "revision": 3,
      "readingMinutes": 6,
      "author": {
        "id": "7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15",
        "displayName": "Léa Martin",
        "avatarIconId": "fox",
        "avatarPhotoVersion": null
      },
      "badges": [
        "NEW"
      ]
    }
  ],
  "links": [],
  "tags": [
    {
      "path": "cellule",
      "count": 3
    }
  ],
  "canManage": false
}
```

### GET /api/open/v1/cours/classes/{classId}/search

**Chercher dans les cours**

Les cours qui parlent de q, avec un extrait.

| Paramètres | | |
|---|---|---|
| `q` | requête, **obligatoire** | Les mots cherchés. |
| `subject` | requête | Une matière (subjectKey). |

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/cours/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c/search?q=mitochondrie" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "lesson": {
      "id": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
      "subjectKey": "svt",
      "subjectName": "SVT",
      "folderId": null,
      "title": "La cellule, unité du vivant",
      "tags": [
        "chapitre-2",
        "cellule"
      ],
      "visibility": "CLASS",
      "status": "COMPLETE",
      "givenOn": "2026-10-14",
      "createdAt": "2026-10-14T16:02:11Z",
      "updatedAt": "2026-10-15T09:40:00Z",
      "revision": 3,
      "readingMinutes": 6,
      "author": {
        "id": "7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15",
        "displayName": "Léa Martin",
        "avatarIconId": "fox",
        "avatarPhotoVersion": null
      },
      "badges": [
        "NEW"
      ]
    },
    "excerpt": "…la **mitochondrie** produit l’énergie de la cellule…",
    "anchor": "les-organites",
    "score": 12
  }
]
```

### GET /api/open/v1/cours/classes/{classId}/programme

**Le programme**

Chaque chapitre du programme officiel : COMPLETE, PARTIAL ou MISSING selon les cours de la classe.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/cours/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c/programme" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "classId": "c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c",
  "level": "SECONDE",
  "levelGuessed": false,
  "canManage": false,
  "levels": [
    "SIXIEME",
    "CINQUIEME",
    "QUATRIEME",
    "TROISIEME",
    "SECONDE",
    "PREMIERE",
    "TERMINALE"
  ],
  "subjects": [
    {
      "subjectKey": "svt",
      "subjectName": "SVT",
      "programmeKey": "SVT",
      "total": 8,
      "complete": 2,
      "partial": 1,
      "chapters": [
        {
          "id": "2e3f4a5b-6c7d-4e8f-9a0b-1c2d3e4f5a6b",
          "title": "La cellule",
          "state": "COMPLETE",
          "lessons": [
            {
              "id": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
              "title": "La cellule, unité du vivant",
              "pinned": false
            }
          ],
          "requestId": null
        }
      ]
    }
  ]
}
```

### GET /api/open/v1/cours/lessons/{lessonId}

**Un cours**

Le cours en Markdown (blocs CoolRoom compris), son chemin, ses auteurs et ses liens.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/cours/lessons/5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "classId": "c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c",
  "lesson": {
    "id": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
    "subjectKey": "svt",
    "subjectName": "SVT",
    "folderId": null,
    "title": "La cellule, unité du vivant",
    "tags": [
      "chapitre-2",
      "cellule"
    ],
    "visibility": "CLASS",
    "status": "COMPLETE",
    "givenOn": "2026-10-14",
    "createdAt": "2026-10-14T16:02:11Z",
    "updatedAt": "2026-10-15T09:40:00Z",
    "revision": 3,
    "readingMinutes": 6,
    "author": {
      "id": "7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15",
      "displayName": "Léa Martin",
      "avatarIconId": "fox",
      "avatarPhotoVersion": null
    },
    "badges": [
      "NEW"
    ]
  },
  "markdown": "# La cellule, unité du vivant\n\n## Le noyau\n\nLe noyau contient l’ADN…",
  "path": [
    "Chapitre 2"
  ],
  "contributors": [
    {
      "id": "7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15",
      "displayName": "Léa Martin",
      "avatarIconId": "fox",
      "avatarPhotoVersion": null
    }
  ],
  "links": [],
  "canEdit": false,
  "canDelete": false,
  "canManage": false,
  "social": {
    "thanks": 4,
    "thanked": true,
    "reported": false,
    "reports": 0,
    "hidden": false
  }
}
```

---

# Routes : Exercices

Scope : `exercises`

S’entraîner et Apprendre : chaque carte d’entraînement, ce que l’élève a réussi ou raté, ses erreurs et ses points faibles.

### GET /api/open/v1/exercises/classes/{classId}/results

**Résultats carte par carte**

Chaque carte jouée : vue, réussie, ratée (lapses), dans le carnet d’erreurs ou non, prochaine révision (dueOn). Les plus récentes d’abord.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/exercises/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c/results" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "classId": "c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c",
  "total": 42,
  "inMistakes": 3,
  "due": 5,
  "results": [
    {
      "itemKey": "svt-cellule-q3",
      "lessonId": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
      "lessonTitle": "La cellule, unité du vivant",
      "seen": 4,
      "correct": 2,
      "lapses": 2,
      "inMistakes": true,
      "reps": 0,
      "intervalDays": 1,
      "dueOn": "2026-10-22",
      "lastAnswerAt": "2026-10-21T17:10:00Z"
    }
  ]
}
```

### GET /api/open/v1/exercises/classes/{classId}/mistakes

**Le carnet d’erreurs**

Les cartes ratées (20 au plus), avec la question et la bonne réponse. kind : FLASH, QCM, VF, CLOZE, ORDER, MATCH…

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/exercises/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c/mistakes" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "scope": "errors",
  "title": "Mes erreurs",
  "subtitle": "Les cartes ratées reviennent jusqu’à deux bonnes réponses d’affilée",
  "timed": false,
  "secondsPerItem": 0,
  "items": [
    {
      "key": "svt-cellule-q3",
      "lessonId": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
      "lessonTitle": "La cellule, unité du vivant",
      "kind": "QCM",
      "anchor": "le-noyau",
      "source": "LESSON",
      "prompt": "Où se trouve l’ADN dans une cellule animale ?",
      "answer": null,
      "options": [
        "Dans le noyau",
        "Dans la membrane",
        "Dans le cytoplasme seulement"
      ],
      "correct": [
        0
      ],
      "explanation": "L’ADN est dans le noyau (et un peu dans les mitochondries).",
      "sequence": null,
      "pairs": null,
      "truth": null,
      "game": "quiz"
    }
  ]
}
```

### GET /api/open/v1/exercises/classes/{classId}/weak

**Points faibles**

Les cours où l’élève rate le plus.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/exercises/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c/weak" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "lessonId": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
    "seen": 12,
    "missed": 5,
    "errors": 3
  }
]
```

### GET /api/open/v1/exercises/classes/{classId}

**Résumé de l’entraînement**

Cartes à revoir, erreurs, plans de révision avant un contrôle et défi de classe.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/exercises/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "due": 5,
  "errors": 3,
  "weekLessons": 4,
  "challenge": {
    "goal": 300,
    "done": 212,
    "players": 17,
    "reached": false,
    "rewardCc": 20
  },
  "plans": [
    {
      "id": "3f4a5b6c-7d8e-4f9a-0b1c-2d3e4f5a6b7c",
      "title": "Contrôle SVT",
      "examOn": "2026-10-28",
      "daysLeft": 7,
      "lessons": [
        {
          "id": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
          "title": "La cellule, unité du vivant",
          "subjectKey": "svt"
        }
      ],
      "total": 30,
      "seen": 18,
      "mastered": 9,
      "doneToday": false,
      "examDay": false,
      "sessionDays": [
        "2026-10-20"
      ]
    }
  ],
  "suggestions": []
}
```

### GET /api/open/v1/exercises/classes/{classId}/learn

**Apprendre**

Chaque cours avec des jeux, et ce qui est su dans chacun.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/exercises/classes/c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c/learn" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "lessons": [
    {
      "lessonId": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
      "title": "La cellule, unité du vivant",
      "subjectKey": "svt",
      "subjectName": "SVT",
      "givenOn": "2026-10-14",
      "total": 24,
      "seen": 20,
      "mastered": 11,
      "due": 3,
      "games": [
        {
          "game": "quiz",
          "label": "Quiz",
          "count": 8,
          "mastered": 5
        }
      ],
      "decks": []
    }
  ]
}
```

### GET /api/open/v1/exercises/lessons/{lessonId}

**Les cartes d’un cours**

Toutes les cartes d’entraînement du cours et les compteurs de l’élève.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/exercises/lessons/5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "lessonId": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
  "items": [
    {
      "key": "svt-cellule-q3",
      "lessonId": "5e550a11-8c2d-4b7a-9f3e-1d2c3b4a5f60",
      "lessonTitle": "La cellule, unité du vivant",
      "kind": "QCM",
      "anchor": "le-noyau",
      "source": "LESSON",
      "prompt": "Où se trouve l’ADN dans une cellule animale ?",
      "answer": null,
      "options": [
        "Dans le noyau",
        "Dans la membrane",
        "Dans le cytoplasme seulement"
      ],
      "correct": [
        0
      ],
      "explanation": "L’ADN est dans le noyau (et un peu dans les mitochondries).",
      "sequence": null,
      "pairs": null,
      "truth": null,
      "game": "quiz"
    }
  ],
  "sections": [
    {
      "anchor": "le-noyau",
      "title": "Le noyau",
      "ai": true
    }
  ],
  "sectionsToGenerate": 0,
  "errors": 1,
  "due": 2,
  "seen": 20,
  "mastered": 11
}
```

---

# Routes : Flash cards

Scope : `flashcards`

Les paquets de l’élève, ceux partagés avec lui, et ses parties.

### GET /api/open/v1/flashcards/decks

**Les paquets**

kind : VOCAB ou DEFINITION. myBest : meilleure partie, null si jamais joué.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/flashcards/decks" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "mine": [
    {
      "id": "4a5b6c7d-8e9f-4a0b-1c2d-3e4f5a6b7c8d",
      "title": "Verbes irréguliers",
      "subject": "Anglais",
      "emoji": "🇬🇧",
      "cardCount": 40,
      "owner": {
        "id": "7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15",
        "displayName": "Léa Martin",
        "avatarIconId": "fox",
        "avatarPhotoVersion": null
      },
      "mine": true,
      "sharedIn": [
        {
          "classId": "c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c",
          "className": "2nde 4"
        }
      ],
      "myBest": {
        "correct": 37,
        "total": 40
      },
      "myPlays": 6,
      "updatedAt": "2026-10-18T19:00:00Z",
      "lessonId": null,
      "kind": "VOCAB"
    }
  ],
  "shared": [],
  "stats": {
    "points": 1240,
    "runs": 18,
    "cardsReviewed": 610,
    "accuracy": 87,
    "perfectRuns": 2,
    "decksCreated": 3
  }
}
```

### GET /api/open/v1/flashcards/decks/{deckId}

**Un paquet et ses cartes**

Les images des cartes ne sont pas servies par l’API.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/flashcards/decks/4a5b6c7d-8e9f-4a0b-1c2d-3e4f5a6b7c8d" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "deck": {
    "id": "4a5b6c7d-8e9f-4a0b-1c2d-3e4f5a6b7c8d",
    "title": "Verbes irréguliers",
    "…": "…"
  },
  "cards": [
    {
      "id": "5b6c7d8e-9f0a-4b1c-2d3e-4f5a6b7c8d9e",
      "position": 0,
      "frontText": "to begin",
      "backText": "began, begun",
      "frontImageVersion": null,
      "backImageVersion": null
    }
  ]
}
```

### GET /api/open/v1/flashcards/runs

**Les parties jouées**

Les 200 dernières, les plus récentes d’abord.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/flashcards/runs" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "6c7d8e9f-0a1b-4c2d-3e4f-5a6b7c8d9e0f",
    "deckId": "4a5b6c7d-8e9f-4a0b-1c2d-3e4f5a6b7c8d",
    "deckTitle": "Verbes irréguliers",
    "correct": 37,
    "total": 40,
    "durationMs": 182000,
    "playedAt": "2026-10-18T19:04:00Z"
  }
]
```

---

# Routes : Niveau et CC

Scope : `progress`

Comptes élèves seulement.

### GET /api/open/v1/progress

**La progression**

level.into / level.needed : XP dans le niveau en cours / pour le suivant.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/progress" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
{
  "level": {
    "level": 9,
    "xp": 4210,
    "into": 310,
    "needed": 600
  },
  "cc": 185,
  "loginStreak": 12,
  "bestLoginStreak": 21,
  "season": {
    "season": {
      "id": "7d8e9f0a-1b2c-4d3e-4f5a-6b7c8d9e0f1a",
      "name": "Saison d’automne",
      "beta": false,
      "startsAt": "2026-09-01T00:00:00Z",
      "endsAt": "2026-12-20T00:00:00Z",
      "colorA": "#31AAA9",
      "colorB": "#A82020",
      "backgroundVersion": null
    },
    "level": {
      "level": 14,
      "xp": 2100,
      "into": 100,
      "needed": 200
    },
    "seenLevel": 14,
    "passMax": 50
  },
  "stats": {
    "questsDone": 34,
    "homeworkVerified": 12,
    "homeworkDone": 88,
    "flashGames": 18,
    "cosmeticsOwned": 7,
    "ccEarned": 640
  }
}
```

---

# Routes : Notifications

Scope : `notifications`

Les 100 dernières, les plus récentes d’abord.

### GET /api/open/v1/notifications

**Les notifications**

type : NEW_HOMEWORK, NEW_GRADE, NEW_MAIL, LESSON_CHANGED, MENTION, CHAT_MESSAGE… relatedId : la chose concernée (devoir, note…).

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/notifications" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "8e9f0a1b-2c3d-4e4f-5a6b-7c8d9e0f1a2b",
    "type": "NEW_HOMEWORK",
    "category": "DEVOIRS",
    "title": "Nouveau devoir en Mathématiques",
    "body": "Exercices 12 à 15 p. 84, pour jeudi",
    "relatedId": "9d2f7a10-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
    "classId": "c1a55e00-2b7d-4f1e-8a3c-6d9e0f1a2b3c",
    "read": false,
    "silent": false,
    "createdAt": "2026-10-21T16:00:05Z"
  }
]
```

---

# Routes : IA

Scope : `ai`

La conversation de l’élève avec l’IA de CoolRoom.

### GET /api/open/v1/ai/chat

**La conversation**

role : user ou assistant. degree : le degré d’aide choisi (1 à 3).

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/ai/chat" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "9f0a1b2c-3d4e-4f5a-6b7c-8d9e0f1a2b3c",
    "role": "user",
    "content": "Explique-moi la mitose simplement",
    "degree": 2,
    "createdAt": "2026-10-21T17:20:00Z"
  },
  {
    "id": "0a1b2c3d-4e5f-4a6b-7c8d-9e0f1a2b3c4d",
    "role": "assistant",
    "content": "La mitose, c’est quand une cellule se divise en deux cellules identiques…",
    "degree": 2,
    "createdAt": "2026-10-21T17:20:09Z"
  }
]
```

---

# Routes : Famille (comptes parents)

Scope : `family`

Pour un compte parent : les enfants reliés et leurs données. 403 pour un compte élève.

### GET /api/open/v1/family/children

**Les enfants reliés**

status : APPROVED quand l’enfant a accepté.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/family/children" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "linkId": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
    "userId": "7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15",
    "displayName": "Léa Martin",
    "avatarIconId": "fox",
    "avatarPhotoVersion": null,
    "status": "APPROVED",
    "createdAt": "2026-09-05T18:00:00Z",
    "approvedAt": "2026-09-05T18:03:00Z",
    "classNames": [
      "2nde 4"
    ]
  }
]
```

### GET /api/open/v1/family/children/{studentId}/grades

**Les notes d’un enfant**

Même forme que /grades.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/family/children/7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15/grades" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "3a4b5c6d-7e8f-4a1b-9c2d-3e4f5a6b7c8d",
    "subject": "Physique-Chimie",
    "title": "Contrôle : les ondes",
    "value": 15.5,
    "outOf": 20,
    "classAverage": 12.25,
    "classMin": 6,
    "classMax": 18.5,
    "coefficient": 2,
    "period": "Trimestre 1",
    "date": "2026-10-17",
    "revealed": true,
    "masked": false
  }
]
```

### GET /api/open/v1/family/children/{studentId}/homework

**Les devoirs d’un enfant**

Avec la classe de chaque devoir.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/family/children/7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15/homework" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "9d2f7a10-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
    "subject": "Mathématiques",
    "title": "Exercices 12 à 15 p. 84",
    "description": "Faire les exercices sur les fonctions affines.",
    "dueAt": "2026-10-23T06:00:00Z",
    "status": "OFFICIEL",
    "isExam": false,
    "done": true,
    "completedAt": "2026-10-21T19:30:00Z",
    "className": "2nde 4"
  }
]
```

### GET /api/open/v1/family/children/{studentId}/timetable

**L’emploi du temps d’un enfant**

Comme /timetable : from et to, 62 jours au plus.

Requête :

```sh
curl "https://api.coolroom.app/api/open/v1/family/children/7b1e4c2a-5d3f-4e8a-9c61-2f0d8b3a4e15/timetable?from=2026-10-19&to=2026-10-25" \
  -H "Authorization: Bearer $COOLROOM_KEY"
```

Réponse (200) :

```json
[
  {
    "id": "6b7c8d9e-0f1a-4b2c-8d3e-4f5a6b7c8d9e",
    "subject": "Histoire-Géographie",
    "teacher": "M. Laurent",
    "room": "B204",
    "startAt": "2026-10-22T07:00:00Z",
    "endAt": "2026-10-22T08:00:00Z",
    "canceled": false,
    "note": null,
    "detention": false
  }
]
```

---

# Conditions d'utilisation

En utilisant l'API, tu respectes les élèves : n'utilise leurs données que pour ce que ton app leur annonce, ne les revends jamais, ne les garde pas plus longtemps qu'il ne faut, et supprime-les quand l'élève retire l'accès. Les conditions d'utilisation de CoolRoom s'appliquent.

https://coolroom.app/conditions/
