Aller au contenu

Pour les développeurs

Recevoir les résultats des évaluations sur votre plateforme

Quand une évaluation se termine, Echelio peut envoyer ses résultats à la plateforme de votre école : une plateforme d’apprentissage, un dossier des étudiants ou votre propre script. Cette page s’adresse au développeur qui met en place la réception.

Dernière mise à jour : 29 octobre 2026

Fonctionnement

Un administrateur de votre école indique une adresse dans Organisation → Webhook sur Echelio. Dès lors, chaque évaluation qui se termine est envoyée à cette adresse par une requête HTTPS POST au corps JSON. Cette fonction fait partie de l’offre Organisation.

  • Toutes les évaluations de l’école : évaluations de code, quiz et évaluations mixtes. L’entraînement n’est jamais envoyé.
  • Une copie complète à chaque fois : les résultats de chaque étudiant, pas seulement ce qui a changé. Gardez la dernière copie.
  • Signée selon la spécification Standard Webhooks, pour que vous puissiez vérifier que chaque requête vient d’Echelio.
  • Ni code ni réponses : les scores, la participation, le résultat de chaque question, l’heure de fin et les changements d’onglet.

Quand elle est envoyée

Une évaluation est envoyée environ une minute après sa fin, quand son heure de fin passe ou qu’un enseignant l’arrête. Elle est renvoyée, sous forme de nouvelle copie complète, chaque fois qu’un résultat change ensuite :

  • une soumission faite avant la fin finit d’être corrigée ;
  • un enseignant corrige une réponse, ou accepte une réponse pour tous les étudiants qui l’ont donnée ;
  • l’IA corrige une réponse ouverte ;
  • un étudiant est retiré de la classe, ou y est réintégré.

Les changements survenus à moins d’une minute d’intervalle partent dans une seule copie : un enseignant qui corrige 30 réponses envoie une ou deux copies, pas trente.

Chaque copie d’une évaluation porte un sequence supérieur d’un à la précédente. Tant que des réponses attendent d’être corrigées, session.final vaut false ; une copie suivante apporte les résultats définitifs.

Les évaluations terminées avant que l’adresse soit définie ne sont pas envoyées d’elles-mêmes ; un administrateur peut en envoyer une depuis sa page. Pour essayer votre côté, utilisez Envoyer un événement de test dans Organisation → Webhook : il envoie des résultats fictifs avec "type": "test".

La requête

Echelio envoie un POST à votre adresse avec ces en-têtes :

content-type
toujours application/json ;
user-agent
Echelio-Webhooks/1 ;
webhook-id
l’identifiant de l’envoi, le même à chaque nouvel essai ;
webhook-timestamp
le moment de l’envoi, en secondes depuis le 1er janvier 1970 (UTC) ;
webhook-signature
v1, suivi de la signature, expliquée plus bas.

Répondez par un statut 2xx en moins de 10 secondes. Toute autre réponse compte comme un échec et donne lieu à un nouvel essai, y compris une redirection : les redirections ne sont pas suivies. L’adresse doit utiliser HTTPS et être joignable depuis internet.

Ce qu’elle contient

Une évaluation mixte, envoyée alors qu’une réponse attend encore d’être corrigée :

{
  "type": "session.results",
  "sequence": 2,
  "sentAt": "2026-11-03T10:15:02.000Z",
  "organization": {
    "id": "cmo1x8r2k0000",
    "name": "Example School"
  },
  "class": {
    "id": "cmc4b7t9p0002",
    "name": "Web development · Group A"
  },
  "session": {
    "id": "cms9d2v6h0004",
    "name": "Week 3 test",
    "format": "MIXED",
    "startsAt": "2026-11-03T08:30:00.000Z",
    "endsAt": "2026-11-03T10:00:00.000Z",
    "closedAt": "2026-11-03T10:00:00.000Z",
    "stoppedEarly": false,
    "final": false,
    "codeWeight": 60,
    "resultsUrl": "https://echelio.com/teacher/sessions/cms9d2v6h0004",
    "questions": [
      {
        "key": "q1",
        "part": "QUIZ",
        "points": 2
      },
      {
        "key": "q2",
        "part": "QUIZ",
        "points": 3
      },
      {
        "key": "c1",
        "part": "CODE",
        "title": "Sum of digits",
        "language": "PYTHON",
        "weight": 1
      }
    ]
  },
  "students": [
    {
      "id": "cmu2f5k8n0006",
      "name": "Ada Example",
      "email": "[email protected]",
      "removed": false,
      "participation": "SUBMITTED",
      "provisional": true,
      "rank": 1,
      "score": 82.5,
      "quiz": {
        "points": 2,
        "totalPoints": 5,
        "score": 40
      },
      "code": {
        "score": 100,
        "passed": 1
      },
      "completedAt": "2026-11-03T09:41:30.000Z",
      "completionMinutes": 71.5,
      "tabSwitches": 2,
      "timeAwayMinutes": 0.8,
      "questions": [
        {
          "key": "q1",
          "outcome": "CORRECT"
        },
        {
          "key": "q2",
          "outcome": "TO_REVIEW"
        },
        {
          "key": "c1",
          "score": 100
        }
      ]
    },
    {
      "id": "cmu2f5k8n0007",
      "name": "Bo Example",
      "email": "[email protected]",
      "removed": false,
      "participation": "MISSED",
      "provisional": false,
      "rank": null,
      "score": null,
      "quiz": {
        "points": null,
        "totalPoints": 5,
        "score": null
      },
      "code": {
        "score": null,
        "passed": null
      },
      "completedAt": null,
      "completionMinutes": null,
      "tabSwitches": 0,
      "timeAwayMinutes": 0,
      "questions": [
        {
          "key": "q1",
          "outcome": "NOT_REACHED"
        },
        {
          "key": "q2",
          "outcome": "NOT_REACHED"
        },
        {
          "key": "c1",
          "score": null
        }
      ]
    }
  ]
}

Le premier niveau

type
session.results, ou test pour un événement de test.
sequence
Augmente d’un à chaque copie de la même évaluation, à partir de 1. Un événement de test a 0.
sentAt
Le moment où cette copie a été construite, en ISO 8601 (UTC).
organization
Votre école : id et name.
class
La classe à qui l’évaluation a été donnée : id et name.

session

id
L’identifiant de l’évaluation dans Echelio, le même dans chaque copie.
name
Le nom de l’évaluation, tel que l’enseignant l’a écrit.
format
CODE, QUIZ ou MIXED.
startsAt, endsAt
Le début et la fin prévus de l’évaluation.
closedAt
Le moment où elle s’est terminée : son heure de fin, ou l’arrêt par un enseignant.
stoppedEarly
Si un enseignant l’a arrêtée avant son heure de fin.
final
Vrai dès qu’aucun résultat d’étudiant ne peut plus changer par une correction.
codeWeight
Évaluations mixtes : la part de la partie code dans le score, en pourcentage. Null sinon.
resultsUrl
La page des résultats de l’évaluation dans Echelio, pour les enseignants.
questions
Les questions et les exercices, dans l’ordre où les étudiants les rencontrent.

session.questions[]

key
q1, q2… pour les questions de quiz, c1, c2… pour les exercices de code. Les résultats des étudiants utilisent les mêmes clés.
part
QUIZ ou CODE.
points
Questions de quiz : ce que vaut la question.
title, language, weight
Exercices de code : le titre de l’exercice, son langage et son poids dans le score.

students[]

id
L’identifiant de l’étudiant dans Echelio.
name, email
Le nom de l’étudiant, et l’adresse e-mail avec laquelle il se connecte.
removed
Vrai quand un enseignant a retiré l’étudiant de la classe après l’évaluation ; ses résultats restent.
participation
La façon dont l’étudiant a participé (valeurs ci-dessous).
provisional
Vrai tant qu’une réponse ou une soumission de l’étudiant attend d’être corrigée.
rank
La place de l’étudiant dans la classe ; null sans score.
score
Le score de l’étudiant sur toute l’évaluation ; null s’il ne l’a pas passée.
quiz
Quiz et évaluations mixtes : points, totalPoints et le score de la partie quiz. Null dans une évaluation de code.
code
Évaluations de code et mixtes : le score de la partie code et le nombre d’exercices entièrement réussis (passed). Null dans un quiz.
completedAt, completionMinutes
Le moment où l’étudiant a terminé, et combien de minutes après le début ; null s’il n’a jamais terminé.
tabSwitches, timeAwayMinutes
Combien de fois l’étudiant a quitté l’onglet de l’évaluation, et combien de temps en tout ; null quand l’évaluation n’enregistre pas les changements d’onglet.
questions
Pour chaque question, son outcome (quiz) ou son meilleur score (code), par clé.

Les scores sont des pourcentages de 0 à 100, arrondis à une décimale, et les durées sont en minutes, comme dans l’export CSV d’Echelio.

participation vaut SUBMITTED, TIMED_OUT, STOPPED et MISSED.

L’outcome d’une question de quiz vaut CORRECT, WRONG, TO_REVIEW, SKIPPED et NOT_REACHED ; TO_REVIEW attend un enseignant.

Vérifier la signature

Votre administrateur trouve le secret de signature dans Organisation → Webhook. Il commence par whsec_ ; gardez-le uniquement sur votre serveur. Vérifiez chaque requête sur son corps brut, avant de lire le JSON : la signature porte sur les octets exacts envoyés par Echelio.

Avec une bibliothèque

Standard Webhooks propose des bibliothèques pour de nombreux langages, listées sur standardwebhooks.com. En JavaScript :

import { Webhook } from "standardwebhooks";

const webhook = new Webhook(process.env.ECHELIO_WEBHOOK_SECRET);

// rawBody is the request's body as received, before any JSON parsing.
// verify() throws when the signature or the timestamp is wrong.
const payload = webhook.verify(rawBody, {
  "webhook-id": request.headers["webhook-id"],
  "webhook-timestamp": request.headers["webhook-timestamp"],
  "webhook-signature": request.headers["webhook-signature"],
});

Sans bibliothèque

La signature est le HMAC-SHA256 de webhook-id, webhook-timestamp et du corps, joints par des points, avec pour clé les octets du secret après whsec_ (base64), écrit en base64. En Node.js :

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, headers, secret) {
  const id = headers["webhook-id"];
  const timestamp = headers["webhook-timestamp"];
  // Refuse a request more than five minutes old, so an old one cannot be replayed.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const expected = createHmac("sha256", key).update(`${id}.${timestamp}.${rawBody}`).digest();
  // The header can hold several signatures, separated by spaces.
  return headers["webhook-signature"].split(" ").some((part) => {
    const [version, signature] = part.split(",");
    const given = Buffer.from(signature ?? "", "base64");
    return version === "v1" && given.length === expected.length && timingSafeEqual(given, expected);
  });
}

Quand un administrateur crée un nouveau secret, l’ancien cesse aussitôt de fonctionner ; les envois qui échouent entre-temps sont réessayés, signés avec le nouveau.

Traiter les envois

  • Répondez d’abord, traitez ensuite. Enregistrez le corps et répondez 200, puis traitez-le, pour qu’un traitement lent ne fasse jamais échouer un envoi.
  • Gardez le sequence le plus élevé. Pour chaque session.id, gardez la copie au sequence le plus élevé et ignorez une copie inférieure.
  • Ignorez ce que vous avez déjà vu. Un envoi peut arriver deux fois, par exemple quand votre réponse s’est perdue ; ignorez un webhook-id déjà traité.
  • Écartez les événements de test. Les requêtes avec "type": "test" contiennent des étudiants fictifs.
  • Retrouvez les étudiants par leur email, l’adresse avec laquelle ils se connectent, ou par l’id d’Echelio.

Quand l’envoi échoue

Echelio essaie un envoi huit fois sur environ deux jours : tout de suite, puis après 1 minute, 5 minutes, 30 minutes, 2 heures, 6 heures, 12 heures et 24 heures. Une copie plus récente de la même évaluation remplace une copie qui attend encore un nouvel essai.

Si tous les essais échouent, les administrateurs de l’école reçoivent un e-mail, au plus un par jour. Le journal des envois, dans Organisation → Webhook, montre chaque envoi, ses essais et la réponse de votre plateforme pendant 30 jours. Corrigez votre côté, puis cliquez sur Renvoyer.