Webhooks

Cette documentation explique comment intégrer notre système de webhooks pour recevoir des notifications en temps réel concernant les événements des apprenants dans votre application.

Aperçu

Les webhooks permettent à votre application de recevoir des requêtes HTTP POST chaque fois que des événements spécifiques se produisent dans notre système. Au lieu d'interroger notre API pour détecter les changements, les webhooks fournissent un moyen fiable d'être notifié immédiatement lorsque des événements se produisent.

Types d'Événements

Nous prenons en charge les types d'événements webhook suivants :

Type d'ÉvénementDescriptionCharge Utile
learner.updatedDéclenché quand les informations d'un apprenant sont mises à jourObjet Learner
learner.startedDéclenché quand un apprenant commence une nouvelle activitéObjet Learner
learner.completedDéclenché quand un apprenant termine une activitéObjet Learner

Structure de la Charge Utile du Webhook

Toutes les requêtes webhook seront envoyées sous forme de requêtes HTTP POST avec la structure JSON suivante :

Corps

Des types plus détaillés suivent

interface FullLearner {
  attempt: {
    id: string;
    userId: string;
    organizationId: string;
    moduleId: string;
    courseId: string;
    completedAt: Date | null;
    data: Record<string, string>;
    createdAt: Date;
    updatedAt: Date;
    status: "completed" | "passed" | "failed" | "in-progress" | "not-started";
    score?: { raw?: number; max?: number; min?: number };
    module: Module;
  } | null;
  user: {
    id: string;
    email: string;
    name: string;
  };
  connection: UserToCourseType | UserToCollectionType;
}

Tentative

interface FullLearnerAttempt {
  id: string;
  userId: string;
  organizationId: string;
  moduleId: string;
  courseId: string;
  completedAt: Date | null;
  data: Record<string, string>;
  createdAt: Date;
  updatedAt: Date;
  status: "completed" | "passed" | "failed" | "in-progress" | "not-started";
  score?: { raw?: number; max?: number; min?: number };
  module: Module;
}

Utilisateur

interface FullLearnerUser {
  id: string;
  email: string;
  name: string;
}

Connexion

Soit une connexion à un cours ou à une collection :

Connexion au cours

interface UserToCourseType {
  userId: string;
  organizationId: string;
  courseId: string;
  externalId: string | null;
  connectType: "invite" | "request";
  connectStatus: "pending" | "accepted" | "rejected";
  createdAt: Date;
  updatedAt: Date;
}

Connexion à la collection

interface UserToCollectionType {
  userId: string;
  organizationId: string;
  collectionId: string;
  connectType: "invite" | "request";
  connectStatus: "pending" | "accepted" | "rejected";
  createdAt: Date;
  updatedAt: Date;
}

En-têtes de Requête

Chaque requête webhook inclut les en-têtes suivants :

  • Content-Type: application/json
  • webhook-timestamp: 2024-01-15T10:30:00.000Z - Horodatage ISO quand le webhook a été envoyé
  • webhook-signature: <signature> - Signature HMAC-SHA256 pour vérification (voir section Sécurité)
  • Tous les en-têtes personnalisés que vous avez configurés pour votre endpoint webhook

Sécurité et Vérification

Vérification de Signature

Chaque requête webhook inclut une signature dans l'en-tête webhook-signature. Cette signature est générée en utilisant HMAC-SHA256 avec votre secret webhook.

Génération de signature :

HMAC-SHA256(secret, horodatage + "." + corps_requête)

Exemple de vérification (Node.js) :

const crypto = require("crypto");

function verifyWebhookSignature(secret, timestamp, body, signature) {
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${body}`)
    .digest("hex");

  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
}

// Utilisation dans votre gestionnaire de webhook
app.post("/webhook", (req, res) => {
  const timestamp = req.headers["webhook-timestamp"];
  const signature = req.headers["webhook-signature"];
  const body = JSON.stringify(req.body);

  if (!verifyWebhookSignature(webhookSecret, timestamp, body, signature)) {
    return res.status(401).send("Non autorisé");
  }

  // Traiter le webhook
  console.log("Événement reçu:", req.body.event);
  res.status(200).send("OK");
});

Bonnes Pratiques

  1. Toujours vérifier les signatures - Ne jamais traiter des webhooks sans vérification de signature
  2. Vérifier les horodatages - Rejeter les webhooks avec des horodatages trop anciens/futurs
  3. Répondre rapidement - Répondre avec un code de statut 2xx dans les 10 secondes
  4. Gérer l'idempotence - Le même événement peut être livré plusieurs fois

Politique de Nouvelle Tentative

Notre système de webhook implémente une stratégie de nouvelle tentative avec backoff exponentiel :

TentativeDélai
1èreImmédiat
2ème5 secondes
3ème1 minute
4ème5 minutes
5ème30 minutes
6ème2 heures
7ème5 heures
8ème10 heures

Notes importantes :

  • Les webhooks sont retentés pendant jusqu'à 7 jours
  • Une livraison est considérée comme réussie quand votre endpoint retourne un code de statut HTTP 2xx
  • Après la tentative finale, la livraison du webhook est marquée comme échouée et ne sera plus retentée
  • Les livraisons échouées sont automatiquement nettoyées après la période d'expiration

Exigences des Endpoints

Votre endpoint webhook doit :

  1. Accepter les requêtes POST avec Content-Type: application/json
  2. Répondre avec des codes de statut 2xx (200, 201, 204) pour un traitement réussi
  3. Répondre dans les 10 secondes pour éviter les timeouts
  4. Utiliser HTTPS pour les environnements de production
  5. Être publiquement accessible depuis nos serveurs

Tester Votre Intégration

Exemple de Gestionnaire de Webhook

Voici un exemple de gestionnaire de webhook de base :

const express = require("express");
const crypto = require("crypto");

const app = express();
app.use(express.json());

app.post("/webhook", (req, res) => {
  try {
    // 1. Vérifier la signature
    const timestamp = req.headers["webhook-timestamp"];
    const signature = req.headers["webhook-signature"];
    const body = JSON.stringify(req.body);

    if (!verifySignature(timestamp, body, signature)) {
      return res.status(401).send("Non autorisé");
    }

    // 2. Traiter l'événement
    const { event, data } = req.body;

    switch (event) {
      case "learner.updated":
        console.log(`L'apprenant ${data.id} a été mis à jour`);
        // Votre logique métier ici
        break;

      case "learner.started":
        console.log(`L'apprenant ${data.id} a commencé une activité`);
        // Votre logique métier ici
        break;

      case "learner.completed":
        console.log(`L'apprenant ${data.id} a terminé une activité`);
        // Votre logique métier ici
        break;

      default:
        console.log(`Type d'événement inconnu: ${event}`);
    }

    // 3. Répondre rapidement
    res.status(200).send("OK");
  } catch (error) {
    console.error("Erreur de traitement du webhook:", error);
    res.status(500).send("Erreur Interne du Serveur");
  }
});

function verifySignature(timestamp, body, signature) {
  const expectedSignature = crypto
    .createHmac("sha256", process.env.WEBHOOK_SECRET)
    .update(`${timestamp}.${body}`)
    .digest("hex");

  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
}

app.listen(3000, () => {
  console.log("Serveur webhook en cours d'exécution sur le port 3000");
});

Dépannage

Problèmes Courants

Échec des livraisons de webhook :

  • Vérifiez que votre endpoint retourne des codes de statut 2xx
  • Vérifiez que votre endpoint est publiquement accessible
  • Assurez-vous que votre serveur répond dans les 10 secondes
  • Vérifiez que la vérification de signature fonctionne correctement

Pas de réception de webhooks :

  • Confirmez que votre webhook est activé dans le tableau de bord
  • Vérifiez que les bons événements sont sélectionnés
  • Vérifiez que l'URL du webhook est correcte et accessible

Événements en double :

  • Implémentez l'idempotence dans votre gestionnaire de webhook
  • Utilisez l'horodatage de l'événement ou un identifiant unique pour détecter les doublons

Support

Si vous rencontrez des problèmes avec la livraison des webhooks ou avez besoin d'aide pour l'implémentation, veuillez contacter notre équipe de support avec :

  1. L'URL de votre endpoint webhook
  2. Les types d'événements spécifiques que vous essayez de recevoir
  3. Tous messages d'erreur ou logs de votre endpoint
  4. L'heure approximative où le problème s'est produit

Limites de Débit

  • Les livraisons de webhook sont traitées toutes les 5 secondes
  • Aucune limite de débit spécifique n'est imposée aux endpoints individuels
  • Cependant, les endpoints constamment lents ou défaillants peuvent être temporairement désactivés

N'oubliez pas d'implémenter une gestion d'erreur et une journalisation appropriées dans vos gestionnaires de webhook pour assurer un traitement fiable des événements.