15 février 2026 · 12 min read

LTI AGS

Gérer le Gradebook, envoyer des notes, synchroniser les résultats

#LTI #e-learning #standard #Grade

LTI 1.3 définit le socle : sécuriser les échanges et identifier les utilisateurs. LTI Advantage va plus loin en ajoutant des services qui permettent une intégration plus profonde entre l’outil et le LMS. Cet article détaille le premier de ces services : Assignment and Grade Services (AGS).

L’AGS permet à un outil externe d’interagir avec le gradebook du LMS — créer des colonnes de notes, envoyer les résultats des apprenants, et récupérer les notes telles que le LMS les a enregistrées.

Tous les appels AGS sont sécurisés via le Security Framework OAuth 2.0 décrit en première section.

Framework de sécurité

Dans LTI1.3 il y a la notion de Message, c’est-à-dire un format standardisé pour échanger des informations entre le LMS et l’outil. Lti Advantage ajoute une notion de service qui partagent le même mécanisme pour sécuriser leurs appels HTTP. L’outil va pouvoir bénéficier de ces services en effectuant des requêtes vers le LMS en plus du flow LTI standard décrit dans l’article précédent.

Une fois que le flow standard LTI1.3 est terminé, l’échange entre le LMS et l’outil se fait à l’aide d’un access Token obtenu via le protocole OAuth 2.0. L’outil doit inclure ce token dans les en-têtes de ses requêtes HTTP pour accéder aux services proposés par le LMS. Les services disponibles dépendent des autorisations accordées à l’outil lors de la configuration de l’intégration.

Lors de la registration, l’outils doit demander l’url pour obtenir un access token, c’est à dire l’url du service d’autorisation du LMS. Cette url est ensuite utilisée pour obtenir un access token.

Le flux se déroule en deux étapes :

  1. L’outil construit et signe un JWT avec sa clé privée
  2. L’outil poste ce JWT au token endpoint du LMS et reçoit en retour un access_token

Étape 1 — Construire le client_assertion

L’outil crée un JWT qu’il signe lui-même avec sa clé privée RSA (la même que celle déclarée lors de la registration). Ce JWT s’appelle le client_assertion.

Header :
{
  "alg": "RS256",
  "typ" : "JWT",
}

Payload :
{
  "iss": "tool.com",
  "sub": "Sfc6HFnwHAx4Tuv",
  "aud": "http://localhost/mod/lti/token.php",
  "iat": 1775400958,
  "exp": 1775401018,
  "jti": "a3f1c2d4-e5b6-7890-abcd-ef1234567890"
}

Étape 2 — Obtenir le Bearer token

L’outil envoie une requête POST au token endpoint du LMS avec le client_assertion dans le corps de la requête. Si la requête est valide, le LMS renvoie un access_token que l’outil peut utiliser pour accéder aux services protégés.

Exemple de requête :

POST http://localhost/mod/lti/token.php
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsImtpZCI6Im1vbi1raWQifQ...
&scope=https://purl.imsglobal.org/spec/lti-ags/scope/score

Dans cet exemple http://localhost/mod/lti/token.php correspond à l’URL du service d’autorisation du LMS fournie au moment de la registration.

Nous verrons la notion de scope dans la section suivante, elle permet de définir les autorisations accordées à l’outil pour accéder aux services du LMS.

Vous trouverez plus de détails dans la spécification sur la sécurité ici.

Assignment and Grade Services (AGS)

la plupart des LMS proposent un GradeBook c’est-à-dire un endroit où sont centralisées toutes les notes de tous les apprenants. Il peut-être par cours, dans ce cas le gradebook présente les notes de toutes les activités du cours. Lorsqu’un outil externe propose du contenu tel qu’un quizz, il doit pouvoir envoyer les résultats des apprenants vers ce gradebook.

C’est ce que propose l’AGS :

claim AGS

La spécification 1EdTEch propose d’ajouter cette iri au message LTI Launch https://purl.imsglobal.org/spec/lti-ags/claim/endpoint.

Par exemple si l’administrateur à autorisé l’outil à utiliser l’AGS,

Activation AGS Moodle

l’id token devient

{
  "nonce": "32d5072797ec4703a23433996f85c3ea61b3e5ff30f311f18c518cb87ec90090",
  "iat": 1775395730,
  "exp": 1775395790,
  "iss": "http://localhost",
  "aud": "Sfc6HFnwHAx4Tuv",
  "https://purl.imsglobal.org/spec/lti/claim/deployment_id": "1",
  "https://purl.imsglobal.org/spec/lti/claim/target_link_uri": "http://localhost:8000/lti/launch/",
  "sub": "2",
  "https://purl.imsglobal.org/spec/lti/claim/lis": {
    "person_sourcedid": "",
    "course_section_sourcedid": ""
  },
  "https://purl.imsglobal.org/spec/lti/claim/roles": [
    "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator",
    "http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor",
    "http://purl.imsglobal.org/vocab/lis/v2/system/person#Administrator"
  ],
  "https://purl.imsglobal.org/spec/lti/claim/context": {
    "id": "2",
    "label": "pyLTI",
    "title": "PYTHON LTI",
    "type": [
      "CourseSection"
    ]
  },
  "https://purl.imsglobal.org/spec/lti/claim/message_type": "LtiResourceLinkRequest",
  "https://purl.imsglobal.org/spec/lti/claim/resource_link": {
    "title": "Hello Django LTI",
    "description": "",
    "id": "1"
  },
  "given_name": "Admin",
  "family_name": "Utilisateur",
  "name": "Admin Utilisateur",
  "https://purl.imsglobal.org/spec/lti/claim/ext": {
    "user_username": "admin",
    "lms": "moodle-2"
  },
  "email": "sebastien@philippot.co",
  "https://purl.imsglobal.org/spec/lti/claim/launch_presentation": {
    "locale": "fr",
    "document_target": "frame",
    "return_url": "http://localhost/mod/lti/return.php?course=2&launch_container=5&instanceid=1&sesskey=M4moqhsjEQ"
  },
  "https://purl.imsglobal.org/spec/lti/claim/tool_platform": {
    "product_family_code": "moodle",
    "version": "2025100603.11",
    "guid": "aa85caf65877b6e8f2e5cfd314c9805e",
    "name": "local",
    "description": "localhost"
  },
  "https://purl.imsglobal.org/spec/lti/claim/version": "1.3.0",
  "https://purl.imsglobal.org/spec/lti/claim/custom": {
    "niveau": "Terminale",
    "debut_cours": "2026-04-03T23:00:00+00:00",
    "fin_cours": "2027-04-03T23:00:00+00:00"
  },
  "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint": {
    "scope": [
      "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly",
      "https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly",
      "https://purl.imsglobal.org/spec/lti-ags/scope/score"
    ],
    "lineitems": "http://localhost/mod/lti/services.php/2/lineitems?type_id=1"
  }
}

On constate alors qu’a été ajouté

"https://purl.imsglobal.org/spec/lti-ags/claim/endpoint": {
    "scope": [
      "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly",
      "https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly",
      "https://purl.imsglobal.org/spec/lti-ags/scope/score"
    ],
    "lineitems": "http://localhost/mod/lti/services.php/2/lineitems?type_id=1"
  }

Il y a deux propriétés importantes à noter dans cette section :

  1. scope : Il s’agit d’un tableau de chaînes qui définit les autorisations accordées à l’outil. Dans cet exemple, l’outil a la permission de lire les éléments de ligne, les résultats. L”outil peut aussi envoyer des scores, c’est à dire des notes brutes. Ces scopes déterminent les actions que l’outil peut effectuer sur les données du gradebook du LMS. Par exemple, si l’outil a le scope lineitem.readonly, il peut lire les éléments de ligne mais ne peut pas les modifier. Si l’outil a le scope score, il peut envoyer des scores (notes) pour les étudiants.

  2. lineitems : C’est l’URL proposée par le LMS, qui permet à l’outil d’accéder aux éléments de ligne spécifiques pour le cours. Cela permet à l’outil de récupérer et de modifier les notes des étudiants dans le gradebook du LMS. c’est la collection de toutes les colonnes du gradebook pour ce cours. L’outil peut y lister ou créer des colonnes.

Si l’enseignant autorise à son tour l’outil à utiliser l’AGS, la propriété lineItem est ajoutée.

Activation deployment AGS Moodle

"https://purl.imsglobal.org/spec/lti-ags/claim/endpoint": {
    "scope": [
      "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem",
      "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly",
      "https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly",
      "https://purl.imsglobal.org/spec/lti-ags/scope/score"
    ],
    "lineitems": "http://localhost/mod/lti/services.php/2/lineitems?type_id=1",
    "lineitem": "http://localhost/mod/lti/services.php/2/lineitems/2/lineitem?type_id=1"
  }

LineItem est l’URL d’une colonne spécifique du gradebook, c’est à dire d’un élément de ligne. L’outil peut y accéder pour récupérer ou modifier les notes des étudiants pour cette colonne spécifique.

Pour manipuler des LineItems ou des LineItem l’outil doit envoyer une requete HTTP avec un header d’autorisation contenant un token d’accès (access token) obtenu via le protocole OAuth 2.0. Le token d’accès doit inclure les scopes appropriés pour les actions que l’outil souhaite effectuer sur les LineItems ou LineItem. Les méthode HTTP autorisée dépendent des scopes accordés à l’outil.

Lorsque le scope contient readonly, l’outil ne peut effectuer que des requêtes de type GET. Si readonly n’est pas présent, l’outil peut effectuer des requêtes de type POST, PUT et DELETE selon les besoins.

Par exemple pour les scopes concernant les LineItems :

ScopeMéthode HTTP
https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonlyGET
https://purl.imsglobal.org/spec/lti-ags/scope/lineitemPOST
https://purl.imsglobal.org/spec/lti-ags/scope/lineitemPUT
https://purl.imsglobal.org/spec/lti-ags/scope/lineitemDELETE

Pour avoir plus d’information sur les requêtes à effectuer 1EdTTech met a disposition une documentation au format OpenAPI avec Swagger ui , disponible ici.

Pour plus de détails sur chaque requêtes la spécification est disponible ici

Score service

Dans cette partie nous allons voir comment envoyer un score au LMS, soit après avoir créer une colonne supplémentaire à l’aide de lineItems soit en utilisant une colonne existante via lineItem. Un score est plus complexe qu’une simple valeur numérique. En effet, si l’outil envoie seulement une note alors le LMS peut ne pas être en mesure de l’interpréter correctement. Que signifie avoir eu 18 à un devoir ? Si le devoir est noté sur 20 alors 18 correspond à une très bonne note, mais si le devoir est noté sur 100 alors 18 correspond à une note très faible. C’est pour cela que le service de score permet d’envoyer des informations supplémentaires telles que la note maximale, le progrès de l’activité et d’autres propriétés permettant de contextualiser la note.

modèle de données

1EdTech propose les propriétés suivantes pour le service de score :

{
    "timestamp": "2023-03-15T12:00:00Z",
    "scoreGiven": 15,
    "scoreMaximum": 20,
    "comment" : "Très bon travail !",
    "activityProgress": "Completed",
    "userId": "12345",
    "gradingProgress": "FullyGraded",
    "scoringUserId": "4567890",
    "submission": {
        "submittedAt": "2023-03-15T12:00:00Z",
        "startedAt": "2023-03-15T12:00:00Z"
    }
}

activityProgress et gradingProgress fonctionnent ensemble et donne du sens sur les interaction de l’apprenant avec l’activité.

SituationactivityProgressgradingProgress
L’apprenant commence le quizzStartedNotReady
L’apprenant soumet, correction auto en coursSubmittedPending
Correction auto terminée, note affichableCompletedFullyGraded
L’apprenant soumet un devoir à corriger manuellementSubmittedPendingManual
L’enseignant a corrigé le devoirCompletedFullyGraded

LTI offre ici un début de solution pour envoyer des traces d’apprentissage. En effet, grace à la propriété activityProgress l’outil peut envoyer des informations sur l’avancement de l’activité. Par exemple, un outil de lecture de vidéo peut envoyer une trace lorsque l’apprenant commence à regarder la vidéo (Started), lorsqu’il regarde la moitié de la vidéo (InProgress) et lorsqu’il termine de regarder la vidéo (Completed). Ces traces d’apprentissage peuvent ensuite être utilisées par le LMS pour suivre l’engagement des apprenants avec le contenu et pour fournir des analyses sur les activités d’apprentissage. C’est moins complet que des standards tels que XAPI ou Caliper mais c’est un début pour permettre aux outils d’envoyer des données d’engagement au LMS.

Envoie du score

Nous avons vu précédement que lorsque un administrateur autorise un outil à envoyer des scores, des propriétés supplémentaire étaient envoyés dans l’id token dont cet exemple :

"https://purl.imsglobal.org/spec/lti-ags/claim/endpoint": {
    "scope": [
      "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem",
      "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly",
      "https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly",
      "https://purl.imsglobal.org/spec/lti-ags/scope/score"
    ],
    "lineitems": "http://localhost/mod/lti/services.php/2/lineitems?type_id=1",
    "lineitem": "http://localhost/mod/lti/services.php/2/lineitems/2/lineitem?type_id=1"
  }

on voit que le LMS renvoie une url pour lineitem et une url pour lineitems. L’outil peut choisir d’envoyer le score sur une colonne spécifique du gradebook en utilisant l’url de lineitem ou d’envoyer le score sur une nouvelle colonne en utilisant l’url de lineitems.

Le scope https://purl.imsglobal.org/spec/lti-ags/scope/score doit être présent. Le scope d’un score autorise seulement les requête en POST, il n’est donc pas permis à un outils de lire (Http Get) un score depuis le LMS.

Le score s’envoie via un POST sur l’URL du lineitem suivi de /scores :

POST http://localhost/mod/lti/services.php/2/lineitems/2/lineitem/scores?type_id=1
Authorization: Bearer <access_token>
Content-Type: application/vnd.ims.lis.v1.score+json

Le content-type est un peu particulier car il doit être au format application/vnd.ims.lis.v1.score+json.

Le Score Service se complète avec le Submission Review Message, une spec complémentaire qui permet à l’enseignant de lancer l’outil directement depuis le gradebook pour corriger une soumission PendingManual. Ce mécanisme est détaillé dans un article dédié.

Result service

Dans la section précédente, nous avons vu que c’est l’outils qui envoie le score. Cependant, le LMS doit avoir le contrôle de ce score car ça reste lui qui gère les résultats des apprenants. Le LMS peut décider d’accepter ou de rejeter le score envoyé par l’outil, et il peut également mettre à jour le score ultérieurement si nécessaire.

Prenons l’exemple d’un apprenant qui travaille sur un devoir d’anglais, l’outil envoie un score de 15/20 pour ce devoir. Cependant, l’enseignant peut ne pas être d’accord avec ce score et décider de le modifier. Par exemple, après avoir pris connaissance du devoir, l’enseignant peut estimer que les erreurs de l’apprenants sont sur des questions non abordés a son cours. Il peut alors décider de ne pas prendre en compte ces erreurs et de mettre à jour le score à 18/20. Il y a donc un déphasage : l’outil conserve 15/20 dans sa base de données alors que le LMS, source de vérité, affiche 18/20 à l’apprenant

Pour résoudre ce problème, 1EdTech propose un service de résultats (Result service) qui permet à l’outil de récupérer les résultats des apprenants depuis le LMS. L’outil peut ainsi synchroniser sa base de données avec les notes réelles du LMS et éviter les problèmes de déphasage.

Modèle de données

{
  "id": "http://localhost/mod/lti/services.php/2/lineitems/2/lineitem/results/2",
  "scoreOf": "http://localhost/mod/lti/services.php/2/lineitems/2/lineitem?type_id=1",
  "userId": "2",
  "resultScore": 15,
  "resultMaximum": 20,
  "comment": "Très bon travail !"
}

récupération des résultats

L’URL du service résulte directement de l’URL du lineitem — il suffit d’ajouter /results :

GET http://localhost/mod/lti/services.php/2/lineitems/2/lineitem/results?type_id=1
Authorization: Bearer <access_token>
Accept: application/vnd.ims.lis.v2.resultcontainer+json

Pour filtrer sur un seul apprenant, on ajoute le paramètre user_id :

GET http://localhost/mod/lti/services.php/2/lineitems/2/lineitem/results?type_id=1&user_id=2
Authorization: Bearer <access_token>
Accept: application/vnd.ims.lis.v2.resultcontainer+json

Le LMS retourne un tableau de résultats, un par apprenant ayant un score enregistré.

Pour plus de détails la spécification est disponible ici.

Partager