đŠ CrĂ©er son export cmi5 pour LMS
Le cmi5 est un profil xAPI standardisé pour remplacer le SCORM dans les LMS modernes : il garde le lancement encadré par un LMS (comme le SCORM), mais fait circuler des statements xAPI vers un LRS pour un suivi beaucoup plus fin.
Définition du format cmi5
Contrairement au SCORM (API globale simple) et au xAPI "libre" (statements envoyés à volonté), le cmi5 impose une séquence stricte de statements, envoyés dans cet ordre :
- Initialized : au lancement du module
- Completed : quand le contenu a été parcouru
- Passed / Failed : selon le score obtenu
- Terminated : Ă la fermeture, toujours en tout dernier
Caractéristiques du format cmi5
- NĂ©cessite un LMS et un LRS compatibles cmi5 : le LMS fournit un jeton d'authentification Ă rĂ©cupĂ©rer dynamiquement au lancement (contrairement au xAPI "libre", oĂč les accĂšs peuvent ĂȘtre codĂ©s en dur)
- Suivi aussi précis que le xAPI classique (score détaillé, progression, réussite/échec)
- Structure du cours définie par un fichier cmi5.xml (l'équivalent de l'imsmanifest.xml du SCORM)
- â ïž Ne peut pas ĂȘtre testĂ© en dehors d'un LMS/LRS compatible cmi5 : les identifiants de session sont fournis dynamiquement au lancement, impossible de les coder en dur comme pour un xAPI autonome
- â ïž Comme pour le xAPI, les statements sont envoyĂ©s en HTTP direct : le LRS doit autoriser le CORS depuis l'origine de votre export
Ressource d'exemple
Le placement des blocs dans le graphe suit la mĂȘme logique que le tuto SCORM : reprenez le graphe d'exemple SCORM (https://creator.celestory.io/project/phHUew7e6) et remplacez les blocs SCORM par les blocs cmi5 ci-dessous, aux mĂȘmes emplacements (dĂ©but, Ă©tapes intermĂ©diaires, fin).
Note importante : Seuls les blocs commençant par cmi5 sont Ă rajouter oĂč vous le souhaitez dans votre graphe.
Ătape 1 : Ajouter les blocs Javascript
Bloc 1 : cmi5 - Récupération des paramÚtres de lancement
function cmi5_getParam(name) {
const url = new URL(window.location.href);
return url.searchParams.get(name);
}
â
window.CMI5 = {
endpoint: cmi5_getParam("endpoint"),
fetchUrl: cmi5_getParam("fetch"),
actor: JSON.parse(cmi5_getParam("actor")),
registration: cmi5_getParam("registration"),
activityId: cmi5_getParam("activityId"),
authToken: null
};
â
if (window.CMI5.endpoint && !window.CMI5.endpoint.endsWith("/")) {
window.CMI5.endpoint += "/";
}
Ces paramÚtres (endpoint, fetch, actor, registration, activityId) sont ajoutés automatiquement à l'URL de votre module par le LMS au moment du lancement : rien à configurer en dur ici.
Bloc 2 : cmi5 - Fonction d'envoi + récupération du jeton + Initialize
function cmi5_sendStatement(verbId, verbDisplay, resultObj) {
const category = [{ id: "https://w3id.org/xapi/cmi5/context/categories/cmi5" }];
â
const moveOnVerbs = [
"http://adlnet.gov/expapi/verbs/completed",
"http://adlnet.gov/expapi/verbs/passed",
"http://adlnet.gov/expapi/verbs/failed"
];
if (moveOnVerbs.includes(verbId)) {
category.push({ id: "https://w3id.org/xapi/cmi5/context/categories/moveOn" });
}
â
const statement = {
actor: window.CMI5.actor,
verb: {
id: verbId,
display: { "fr-FR": verbDisplay }
},
object: {
id: window.CMI5.activityId,
objectType: "Activity"
},
context: {
registration: window.CMI5.registration,
contextActivities: { category: category }
}
};
â
if (resultObj) {
statement.result = resultObj;
}
â
fetch(window.CMI5.endpoint + "statements", {
method: "POST",
headers: {
"Authorization": window.CMI5.authToken,
"Content-Type": "application/json",
"X-Experience-API-Version": "1.0.3"
},
body: JSON.stringify(statement)
}).then(() => console.log("cmi5 : statement envoyé -", verbDisplay))
.catch(error => console.log("cmi5 erreur envoi", error));
}
â
fetch(window.CMI5.fetchUrl, { method: "POST" })
.then(response => response.json())
.then(data => {
window.CMI5.authToken = "Basic " + data["auth-token"];
cmi5_sendStatement("http://adlnet.gov/expapi/verbs/initialized", "initialisé");
})
.catch(error => console.log("cmi5 erreur récupération du jeton", error));
Le jeton récupéré via fetch est valable pour toute la session : c'est lui qui sert d'authentification pour tous les statements suivants.
Bloc 3 : cmi5 - Step
const step = 70;
â
cmi5_sendStatement(
"http://adlnet.gov/expapi/verbs/progressed",
"progression",
{
extensions: {
"https://w3id.org/xapi/cmi5/result/extensions/progress": step
}
}
);
Vous pouvez rajouter autant d'étapes que vous le souhaitez en dupliquant ce bloc avec différents pourcentages, comme pour les steps SCORM.
Bloc 4 : cmi5 - Completed + Passed
const scorePercent = 100;
â
cmi5_sendStatement("http://adlnet.gov/expapi/verbs/completed", "complété", {
completion: true,
duration: "PT0H5M0S"
});
â
cmi5_sendStatement("http://adlnet.gov/expapi/verbs/passed", "réussi", {
score: {
scaled: scorePercent / 100,
raw: scorePercent,
min: 0,
max: 100
},
success: true
});
duration suit le format ISO 8601 (ex : PT0H5M0S = 5 minutes) â Ă ajuster ou calculer dynamiquement selon le temps rĂ©ellement passĂ©. En cas d'Ă©chec, envoyez le verbe failed (http://adlnet.gov/expapi/verbs/failed) avec success: false Ă la place de passed.
Bloc 5 : cmi5 - Finish Session (toujours en dernier)
cmi5_sendStatement("http://adlnet.gov/expapi/verbs/terminated", "clÎturé");
console.log("cmi5 : session terminée");
Le statement Terminated doit impĂ©rativement ĂȘtre le dernier envoyĂ© : aucun autre statement ne doit partir aprĂšs celui-ci.
Ătape 2 : Exporter le projet
Exportez votre projet au format Web ou PWA.
Ătape 3 : CrĂ©er la structure des dossiers
Créez un dossier avec en titre le nom de votre module (ex : module1) et déplacez-y l'ensemble du contenu exporté.
Ătape 4 : Ajouter le fichier cmi5.xml
à la racine de votre package (à cÎté du dossier module1), ajoutez un fichier cmi5.xml décrivant la structure du cours :
<?xml version="1.0" encoding="UTF-8"?>
<courseStructure xmlns="https://w3id.org/xapi/profiles/cmi5/v1/CourseStructure.xsd">
<course id="https://votredomaine.com/courses/nom-du-cours">
<title><langstring lang="fr-FR">Nom du cours</langstring></title>
<description><langstring lang="fr-FR">Description du cours</langstring></description>
</course>
<au id="https://votredomaine.com/courses/nom-du-cours/module1" moveOn="CompletedOrPassed">
<title><langstring lang="fr-FR">Nom du module</langstring></title>
<description><langstring lang="fr-FR">Description du module</langstring></description>
<url>module1/index.html</url>
</au>
</courseStructure>
L'id de l'<au> doit correspondre à l'activityId que le LMS transmettra au lancement : c'est ce qui permet de relier les statements reçus au bon module. moveOn définit la condition de complétion (Completed, Passed, CompletedAndPassed, CompletedOrPassed ou NotApplicable).
Ătape 5 : Compresser en ZIP
Compressez le dossier parent (contenant cmi5.xml et le dossier module1) en fichier zip.
Ătape 6 : Tester le fichier cmi5
Importez le zip dans un LMS ou un testeur compatible cmi5, par exemple https://app.cloud.scorm.com/sc/user/Home (SCORM Cloud gÚre aussi le cmi5). Lancez le module puis vérifiez dans le LRS la réception des statements dans l'ordre : Initialized, Completed, Passed (ou Failed), Terminated.
Mis Ă jour le : 02/07/2026
Merci !
