API Skilzy pour les organismes de formation
L'API REST Skilzy permet à ton organisme de récupérer automatiquement les données de tes apprenants (suivi, émargement, évaluations, documents, satisfaction) dans tes propres outils : Airtable, tableurs, ETL, BI. Principalement en lecture ; une opération d'écriture permet aussi d'inscrire un apprenant (clé avec scope « write »). Scopée à ton organisation. Ce sont les mêmes chiffres que les dossiers PDF.
Démarrage rapide
Trois étapes pour ton premier appel :
- Génère une clé API depuis la page « API & intégrations » de ton dashboard (elle commence par
sk_live_). - Fais ton premier appel en passant la clé dans l'en-tête Authorization :
curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/learners?limit=100"
- Tu reçois une réponse JSON : les lignes sont dans
data, avec la pagination danspagination.{ "resource": "learners", "meta": { "org_name": "Mon OF", "year": 2026, "generated_at": "..." }, "pagination": { "limit": 100, "offset": 0, "count": 42, "total": 42, "has_more": false }, "data": [ { "nom": "Jean Dupont", "email": "jean@…", "temps_actif_min": 184, "evaluations_reussies": 12, … } ] }
Authentification
Chaque requête doit porter une clé API d'organisme dans l'en-tête HTTP. Génère et gère tes clés depuis la page « API & intégrations » de ton dashboard.
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
- La clé n'est affichée qu'une seule fois à sa création ; copie-la immédiatement.
- Une clé ne donne accès qu'aux données de ton organisation.
- Tu peux révoquer une clé à tout moment ; elle renvoie alors une erreur 401.
- Une clé peut avoir une date d'expiration (définie à sa création). Passé cette date, elle renvoie 401 « Clé API expirée » ; génère-en une nouvelle.
- Ne partage jamais une clé côté navigateur / front-end public.
Endpoints
URL de base : https://skilzy.io/api/v1
Toutes les ressources s'interrogent en GET :
| Endpoint | Contenu |
|---|---|
| GET /api/v1/learners | Synthèse par apprenant (1 ligne / apprenant) |
| GET /api/v1/users | Apprenants de l'org + licence associée (1 ligne / apprenant) |
| GET /api/v1/summary | Compteurs : apprenants + licences par type (objet unique dans data, pas un tableau) |
| GET /api/v1/attendance | Émargement tutorat (1 ligne / apprenant × séance) |
| GET /api/v1/evaluations | Tentatives de quiz (1 ligne / tentative) |
| GET /api/v1/quiz-answers | Réponses de quiz question par question (1 ligne / tentative × question) |
| GET /api/v1/documents | PIF & attestations émis (1 ligne / document) |
| GET /api/v1/satisfaction | Enquêtes de satisfaction (1 ligne / enquête) |
| GET /api/v1/enrollments | Avancement par apprenant × programme (1 ligne / inscription) |
| GET /api/v1/progress | Progression détaillée (1 ligne / apprenant × programme × leçon) |
| GET /api/v1/logins | Connexions horodatées (1 ligne / connexion) |
| GET /api/v1/tutoring-ratings | Notes de tutorat (séance + formateur) (1 ligne / note) |
| GET /api/v1/lesson-ratings | Notes de leçon milestone / fin de module (1 ligne / note) |
Paramètres de requête
| Paramètre | Description | Défaut |
|---|---|---|
| format | Format de sortie : json ou csv | json |
| year | Année civile filtrée (ex. 2026) | Année en cours |
| limit | Nombre de lignes par page (max 1000, JSON) | 100 |
| offset | Décalage de pagination (JSON) | 0 |
| updated_since | Ne renvoie que les lignes dont la date ≥ valeur (YYYY-MM-DD). Sync incrémentale. Ignoré pour learners. | — |
| Restreint à UN apprenant (email exact, insensible à la casse). 404 si inconnu dans ton organisation. | — | |
| user_id | Comme email, mais par identifiant technique de l'apprenant. | — |
| programme | Restreint à UNE formation (slug de programme, ex. marketing-contenus-rs7439). Ignoré sur les ressources sans programme (learners, logins). | — |
Les requêtes mono-apprenant (email ou user_id) sont servies par un chemin direct : elles ne sont pas concernées par le plafond d'agrégation org-wide (meta.truncated) et renvoient un bloc meta.learner.
# Un seul apprenant (toutes les ressources acceptent ?email= ou ?user_id=) curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/learners?email=jean@orga.fr" # Son avancement sur UNE formation (première/dernière connexion incluses) curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/enrollments?email=jean@orga.fr&programme=marketing-contenus-rs7439" # Ses réponses de quiz, question par question curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/quiz-answers?email=jean@orga.fr"
Format de réponse
Les réponses JSON contiennent les métadonnées, la pagination et les lignes dans data :
{
"resource": "learners",
"meta": { "org_name": "Mon OF", "year": 2026, "generated_at": "..." },
"pagination": { "limit": 100, "offset": 0, "count": 42, "total": 42, "has_more": false },
"data": [
{ "nom": "Jean Dupont", "email": "jean@…", "temps_actif_min": 184, "evaluations_reussies": 12, … }
]
}Avec ?format=csv, la réponse est un fichier CSV (UTF-8 avec BOM, séparateur virgule) téléchargeable, sans pagination.
Exception : la ressource summary renvoie un objet unique dans data (pas un tableau) et n'a pas de pagination :
{
"resource": "summary",
"meta": { "org_name": "Mon OF", "generated_at": "2026-06-10T09:00:00.000Z" },
"data": {
"utilisateurs": { "total": 38, "actifs": 31 },
"licences": {
"standard": { "total": 50, "disponibles": 12, "utilisees": 38 },
"tutorat": { "total": 10, "disponibles": 4, "utilisees": 6 }
}
}
}Référence des champs
Les clés JSON sont identiques en français quelle que soit la langue. Les valeurs booléennes sont renvoyées « Oui » / « Non ».
/api/v1/learners
| Champ | Description |
|---|---|
| nom | Nom de l'apprenant |
| invite_le | Date d'invitation (YYYY-MM-DD) |
| inscrit_le | Date d'inscription |
| connexions | Nombre de connexions sur la période |
| temps_actif_min | Temps actif total (minutes, sans double comptage) |
| temps_cours_min | Dont temps sur les cours |
| temps_labia_min | Dont temps Lab IA |
| temps_tutorat_min | Dont temps en tutorat live |
| programmes_suivis | Nombre de programmes suivis |
| programmes_completes | Nombre de programmes complétés à 100 % |
| evaluations_reussies | Évaluations réussies |
| evaluations_total | Évaluations tentées (total) |
| score_moyen | Score moyen des évaluations réussies (/100) |
| seances_tutorat | Séances de tutorat suivies |
| seances_present | Séances où la présence est validée |
| pif_emis | Nombre de PIF émis |
| attestations_emises | Nombre d'attestations émises |
| satisfaction_note | Dernière note de satisfaction (/5) |
| nps | Dernier NPS (/10) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/learners"
# data[0] :
{
"nom": "Jean Dupont",
"email": "jean@orga.fr",
"invite_le": "2026-01-05",
"inscrit_le": "2026-01-06",
"connexions": 46,
"temps_actif_min": 184,
"temps_cours_min": 150,
"temps_labia_min": 22,
"temps_tutorat_min": 12,
"programmes_suivis": 1,
"programmes_completes": 1,
"evaluations_reussies": 12,
"evaluations_total": 14,
"score_moyen": 87,
"seances_tutorat": 2,
"seances_present": 2,
"pif_emis": 1,
"attestations_emises": 1,
"satisfaction_note": 5,
"nps": 9
}/api/v1/users
| Champ | Description |
|---|---|
| nom | Nom de l'apprenant |
| statut | Statut du compte (actif / invité / suspendu) |
| licence_type | Type de licence (standard / tutorat, vide si aucune) |
| licence_statut | Statut de la licence (disponible / attribuée / activée / révoquée) |
| inscrit_le | Date d'inscription ou d'invitation (YYYY-MM-DD) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/users"
# data[0] :
{
"nom": "Jean Dupont",
"email": "jean@orga.fr",
"statut": "actif",
"licence_type": "standard",
"licence_statut": "activée",
"inscrit_le": "2026-01-06"
}/api/v1/summary
| Champ | Description |
|---|---|
| utilisateurs.total | Nombre total d'apprenants |
| utilisateurs.actifs | Apprenants au statut actif |
| licences.standard.total | Licences standard (total) |
| licences.standard.disponibles | Licences standard disponibles |
| licences.standard.utilisees | Licences standard utilisées |
| licences.tutorat.total | Licences tutorat (total) |
| licences.tutorat.disponibles | Licences tutorat disponibles |
| licences.tutorat.utilisees | Licences tutorat utilisées |
/api/v1/attendance
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| seance | Titre de la séance |
| programme | Programme rattaché (titre) |
| programme_slug | Identifiant stable du programme (clé de jointure) |
| date | Date et heure de début (YYYY-MM-DD HH:MM) |
| duree_prevue_min | Durée prévue de la séance (minutes) |
| present | Présence validée (Oui / Non) |
| temps_connecte_min | Temps réellement connecté (minutes) |
| signe_apprenant | Émargement signé par l'apprenant (Oui / Non) |
| signe_formateur | Émargement signé par le formateur (Oui / Non) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/attendance"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"seance": "Tutorat : bien démarrer",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"date": "2026-03-12 14:00",
"duree_prevue_min": 60,
"present": "Oui",
"temps_connecte_min": 58,
"signe_apprenant": "Oui",
"signe_formateur": "Oui"
}/api/v1/evaluations
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| programme | Programme (titre) |
| programme_slug | Identifiant stable du programme (clé de jointure) |
| module | Module |
| lecon | Leçon évaluée |
| tentative | Numéro de la tentative |
| score | Score obtenu (/100) |
| reussi | Tentative réussie (Oui / Non) |
| date | Date de la tentative (YYYY-MM-DD) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/evaluations"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"module": "La méthode RMBC",
"lecon": "Pourquoi RMBC",
"tentative": 2,
"score": 85,
"reussi": "Oui",
"date": "2026-03-15"
}/api/v1/quiz-answers
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| programme | Programme (titre) |
| programme_slug | Identifiant stable du programme (clé de jointure) |
| module | Module |
| lecon | Leçon contenant le quiz |
| tentative | Numéro de la tentative |
| score_tentative | Score de la tentative (/100) |
| reussi | Tentative réussie (Oui / Non) |
| question | Intitulé de la question posée |
| options | Options proposées, séparées par « | » |
| reponse_choisie | Réponse choisie par l'apprenant |
| bonne_reponse | Bonne réponse attendue |
| correct | Réponse correcte (Oui / Non) |
| repondu_le | Horodatage de la réponse (YYYY-MM-DD HH:MM) |
| date | Date de la tentative (YYYY-MM-DD) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/quiz-answers"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"module": "La méthode RMBC",
"lecon": "Pourquoi RMBC",
"tentative": 2,
"score_tentative": 85,
"reussi": "Oui",
"question": "Que garantit la méthode RMBC ?",
"options": "Un angle clair | Des vues immédiates | Un script long",
"reponse_choisie": "Un angle clair",
"bonne_reponse": "Un angle clair",
"correct": "Oui",
"repondu_le": "2026-03-15 17:38",
"date": "2026-03-15"
}/api/v1/documents
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| type | Type de document (PIF | Attestation) |
| programme | Programme concerné (titre) |
| programme_slug | Identifiant stable du programme (clé de jointure) |
| emis_le | Date d'émission (YYYY-MM-DD) |
| heures | Volume horaire (PIF : estimé ; attestation : mesuré) |
| score_completion | Score de complétion (attestations uniquement) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/documents"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"type": "Attestation",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"emis_le": "2026-04-02",
"heures": 21,
"score_completion": 100
}/api/v1/satisfaction
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| programme | Programme évalué (titre) |
| programme_slug | Identifiant stable du programme (clé de jointure) |
| note | Note de satisfaction (/5) |
| nps | Net Promoter Score (/10) |
| verbatim | Commentaire libre de l'apprenant |
| repondu_le | Date de réponse (YYYY-MM-DD) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/satisfaction"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"note": 5,
"nps": 9,
"verbatim": "Formation très concrète, je recommande.",
"repondu_le": "2026-04-02"
}/api/v1/enrollments
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| programme | Programme (titre) |
| programme_slug | Identifiant stable du programme (clé de jointure) |
| pourcentage | Avancement du programme (%) |
| lecons_vues | Nombre de leçons vues |
| lecons_total | Nombre total de leçons |
| complete | Programme complété (Oui / Non) |
| complete_le | Date de complétion (= émission de l'attestation, YYYY-MM-DD) |
| derniere_activite_le | Date de dernière activité sur le programme (YYYY-MM-DD) |
| premiere_connexion_le | Premier lancement du cours par l'apprenant, date et heure (YYYY-MM-DD HH:MM) |
| derniere_connexion_le | Dernière connexion au cours, date et heure (YYYY-MM-DD HH:MM) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/enrollments"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"pourcentage": 100,
"lecons_vues": 48,
"lecons_total": 48,
"complete": "Oui",
"complete_le": "2026-04-02",
"derniere_activite_le": "2026-04-01",
"premiere_connexion_le": "2026-01-08 09:12",
"derniere_connexion_le": "2026-04-01 18:47"
}/api/v1/progress
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| programme | Programme (titre) |
| programme_slug | Identifiant stable du programme (clé de jointure) |
| pourcentage_programme | Avancement global du programme (%) |
| module | Module |
| lecon | Leçon |
| vue | Leçon vue (Oui / Non) |
| visites | Nombre de visites de la leçon |
| temps_min | Temps actif sur la leçon (minutes) |
| derniere_visite_le | Date de dernière visite (YYYY-MM-DD) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/progress"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"pourcentage_programme": 100,
"module": "La méthode RMBC",
"lecon": "Pourquoi RMBC",
"vue": "Oui",
"visites": 3,
"temps_min": 14,
"derniere_visite_le": "2026-03-15"
}/api/v1/logins
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| debut | Début de connexion (YYYY-MM-DD HH:MM) |
| fin | Fin de session (YYYY-MM-DD HH:MM) |
| duree_min | Durée de la session (minutes) |
| ip | Adresse IP de connexion |
| navigateur | Navigateur / appareil (user-agent) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/logins"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"debut": "2026-03-15 17:02",
"fin": "2026-03-15 17:58",
"duree_min": 56,
"ip": "92.184.100.10",
"navigateur": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)…"
}/api/v1/tutoring-ratings
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| seance | Titre de la séance de tutorat |
| programme | Programme rattaché |
| programme_slug | Identifiant stable du programme (clé de jointure) |
| formateur | Nom du formateur noté |
| note_seance | Note de la séance (/5, vide pour imports legacy) |
| note_formateur | Note du formateur (/5) |
| commentaire | Commentaire libre |
| soumis_le | Date de la note (YYYY-MM-DD) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/tutoring-ratings"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"seance": "Tutorat : bien démarrer",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"formateur": "Camille",
"note_seance": 5,
"note_formateur": 5,
"commentaire": "Très clair.",
"soumis_le": "2026-03-12"
}/api/v1/lesson-ratings
| Champ | Description |
|---|---|
| apprenant | Nom de l'apprenant |
| programme | Programme (titre) |
| programme_slug | Identifiant stable du programme (clé de jointure) |
| module | Module |
| lecon | Leçon notée (milestone / fin de module) |
| note | Note de la leçon (/5) |
| commentaire | Commentaire libre |
| soumis_le | Date de la note (YYYY-MM-DD) |
Voir un exemple d'appel et de réponse
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/lesson-ratings"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"module": "La méthode RMBC",
"lecon": "Bilan du module",
"note": 5,
"commentaire": "Super module.",
"soumis_le": "2026-03-16"
}Rapport d'activité PDF
L'endpoint /api/v1/activity-report renvoie le dossier d'audit PDF d'un apprenant (le même document que le dashboard : connexions, temps par leçon, évaluations, émargement, documents). Réponse binaire application/pdf.
curl -H "Authorization: Bearer sk_live_xxx" \ -o rapport-jean.pdf \ "https://skilzy.io/api/v1/activity-report?email=jean@orga.fr&year=2026" # Optionnel : limiter le rapport à une formation # &programme=marketing-contenus-rs7439
| Paramètre | Description |
|---|---|
| email / user_id | L'apprenant ciblé (requis, l'un des deux) |
| year | Année du rapport (défaut : année en cours) |
| programme | Limite le rapport à une formation (slug, optionnel) |
Inscrire un apprenant
En plus de la lecture, l'API permet d'inscrire un apprenant dans ton organisation. Cette opération est réservée aux clés disposant du scope « write » (à cocher « Autoriser l'écriture » à la création de la clé). Elle attribue une licence disponible de ton organisation.
curl -X POST \
-H "Authorization: Bearer sk_live_write_xxx" \
-H "Content-Type: application/json" \
-d '{"email":"apprenant@orga.fr","programme_slugs":["marketing-contenus-rs7439"]}' \
"https://skilzy.io/api/v1/users"Corps de la requête (JSON)
| Champ | Description |
|---|---|
| Email de l'apprenant (requis) | |
| programme_slugs | Tableau de slugs de programmes à attribuer (optionnel ; ex. ["marketing-contenus-rs7439"]) |
Réponses
| Code | Signification |
|---|---|
| 201 | Inscrit (status active si le compte existait déjà, pending sinon, invitation envoyée) |
| 403 | Clé en lecture seule (scope write requis) |
| 409 | Aucune licence disponible, ou apprenant déjà membre |
| 400 | Email manquant, programme inconnu, ou email hors domaine autorisé |
Inscription en lot (bulk)
Envoie un tableau d'apprenants via le champ users (max 50 par appel). Le traitement est best-effort : chaque ligne réussit ou échoue indépendamment (déjà membre, plus de licence…), et la réponse renvoie un récapitulatif summary + un détail results ligne par ligne. Réexécuter le même lot est sans danger : les apprenants déjà inscrits sont ignorés sans reconsommer de licence.
Option atomic: true → l'API pré-compte les licences disponibles et rejette tout le lot (409) si elles sont insuffisantes, sans inscrire personne.
# Inscription en lot (max 50 / appel)
curl -X POST \
-H "Authorization: Bearer sk_live_write_xxx" \
-H "Content-Type: application/json" \
-d '{
"users": [
{ "email": "a@orga.fr", "programme_slugs": ["marketing-contenus-rs7439"] },
{ "email": "b@orga.fr" }
],
"atomic": false
}' \
"https://skilzy.io/api/v1/users"
# Réponse : { "summary": { "requested": 2, "enrolled": 2, "failed": 0 },
# "results": [ { "email": "a@orga.fr", "status": "active", "member_id": "…" }, … ] }Webhooks
Plutôt que d'interroger l'API en boucle, tu peux recevoir les événements importants en temps quasi réel (moins d'une minute) sur ton serveur. Configure tes endpoints (URL https + choix des événements) depuis la page « API & intégrations » de ton dashboard. Un secret de signature (whsec_…) est affiché une seule fois à la création.
Événements disponibles
| Type | Déclencheur |
|---|---|
| quiz.completed | Quiz terminé : score, réussite et détail question par question (answers) |
| course.started | Premier lancement d'une formation par un apprenant (première connexion au cours) |
| programme.completed | Formation complétée : l'attestation de fin vient d'être émise |
| learner.enrolled | Apprenant inscrit dans ton organisation |
| learner.activated | L'apprenant a activé son compte (première connexion à l'app) |
| satisfaction.submitted | Enquête de satisfaction reçue (note, NPS, verbatim) |
Format de livraison
Chaque événement est POSTé en JSON sur ton URL, avec trois en-têtes : X-Skilzy-Event (type), X-Skilzy-Delivery (id stable de la livraison, identique à chaque nouvel essai : sers-t'en pour dédupliquer) et X-Skilzy-Signature (signature HMAC).
POST https://ton-serveur.fr/webhooks/skilzy
Content-Type: application/json
X-Skilzy-Event: quiz.completed
X-Skilzy-Delivery: 5f0c… # id stable, identique à chaque retry
X-Skilzy-Signature: t=1751980800,v1=a1b2c3…
{
"id": "5f0c…",
"type": "quiz.completed",
"created_at": "2026-07-09T14:32:11.000Z",
"data": {
"learner": { "user_id": "…", "email": "jean@orga.fr", "name": "Jean Dupont" },
"programme": { "slug": "marketing-contenus-rs7439", "title": "Marketing & contenus…" },
"lesson": { "key": "…", "title": "…", "module_title": "…", "course_id": "…" },
"attempt_number": 1,
"score": 85,
"passed": true,
"completed_at": "2026-07-09T14:32:08.000Z",
"answers": [
{
"question": "Que fait la méthode RMBC ?",
"options": ["…", "…", "…"],
"chosen_label": "…",
"correct_label": "…",
"is_correct": true,
"attempt_no": 1,
"answered_at": "2026-07-09T14:31:52.000Z"
}
]
}
}Vérifier la signature
La signature couvre l'horodatage et le corps brut : v1 = HMAC-SHA256(secret, "t.corps"). Recalcule-la côté serveur et compare (comparaison en temps constant), en rejetant les horodatages trop anciens.
// Node / Express : vérifier X-Skilzy-Signature
import crypto from 'node:crypto'
function verifySkilzySignature(req, secret) {
const header = req.get('X-Skilzy-Signature') || '' // t=1751980800,v1=abc…
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')))
if (!t || !v1) return false
// Anti-rejeu : refuse les livraisons de plus de 5 minutes.
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
const expected = crypto
.createHmac('sha256', secret) // secret whsec_…
.update(`${t}.${req.rawBody}`) // corps BRUT, pas re-sérialisé
.digest('hex')
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
}Réponses attendues & relances
- Réponds un code 2xx en moins de 10 secondes (traite en asynchrone si besoin).
- En cas d'échec (autre code, timeout), la livraison est retentée : 1 min, 10 min, 1 h, 6 h puis 24 h. Après 6 tentatives, elle est abandonnée (statut « dead » dans le journal).
- 50 échecs consécutifs désactivent automatiquement l'endpoint (réactivable depuis le dashboard).
- L'ordre de livraison n'est pas garanti : utilise created_at du payload pour ordonner.
- Le bouton « Tester » du dashboard envoie un événement ping signé, visible dans le journal des livraisons.
Exemples & intégrations
curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/learners?limit=100"
Codes d'erreur
| Code | Signification |
|---|---|
| 401 | Clé API manquante, invalide, révoquée ou expirée |
| 404 | Ressource inconnue (vérifie l'URL) |
| 405 | Méthode non autorisée (utilise GET) |
| 429 | Trop de requêtes ; respecte l'en-tête Retry-After |
| 500 | Erreur interne ; réessaie ou contacte le support |
Limites & garanties
- Écriture limitée : par défaut les clés sont en lecture seule ; seule l'inscription d'apprenant (POST /users) est possible, et uniquement avec une clé au scope « write ».
- Périmètre : une clé ne voit et n'agit que sur les apprenants de son organisation.
- Volume : jusqu'à 50 apprenants par appel en inscription (bulk) ; pagine tes appels au-delà.
- Débit : 120 requêtes par minute et par clé. Au-delà, réponse 429 avec en-tête Retry-After.
- Cohérence : les chiffres correspondent exactement aux dossiers PDF.
OpenAPI & Postman
Importe l'API dans ton client (Postman, Insomnia, Bruno…) ou génère un client depuis la spec OpenAPI 3.1 :
Dans Postman : Import → Link, et colle l'URL https://skilzy.io/openapi.json (ou le fichier). Renseigne ensuite la variable apiKey de la collection avec ta clé sk_live_….
