Aller au contenu principal
QualiForma
CatalogueDevenir formateurQualiopiIADéveloppeurs
Connexion
  • Catalogue
  • Devenir formateur
  • Qualiopi
  • IA
  • Développeurs
  • Connexion
  • Vue d'ensemble

Démarrage

  • Quickstart
  • Authentification

Référence

  • API Reference (Scalar)

Endpoints

  • Tous les endpoints

Cœur LMS

  • Tenants
  • Utilisateurs
  • Formations
  • Inscriptions
  • Sessions live

Conformité Qualiopi

  • Dashboard Qualiopi
  • Conformité
  • Émargement
  • Questionnaires
  • Réclamations
  • Plans d'amélioration
  • Parcours adaptatifs
  • Compétences formateurs
  • BPF
  • Médiateurs

Paiements & Facturation

  • Paiements
  • Facturation Factur-X
  • Webhooks

Design System

  • Vue d'ensemble
  • Couleurs
  • Typographie
  • Espacement
  • Elevation
  • Motion
  • Radius
  • Composants
  • · Formulaires
  • · Feedback
  • · Navigation
  • · Progression
  • · Données
Swagger UI (s'ouvre dans un nouvel onglet)
  1. Développeurs
  2. Endpoints
  3. Webhooks

Webhooks

Recevez des événements en temps réel quand des actions surviennent dans votre tenant. Signature HMAC-SHA256 pour l'authenticité.

Comment ça fonctionne

  1. 1Enregistrez votre endpoint HTTPS via POST /webhooks
  2. 2QualiForma stocke un secret unique (whsec_...) pour ce webhook
  3. 3Quand un événement survient, QualiForma POSTe le payload JSON sur votre URL
  4. 4Vérifiez la signature X-Qualiforma-Signature avec le secret HMAC-SHA256
  5. 5Répondez 200 OK en moins de 30 secondes (timeout)
  6. 63 retries automatiques en cas d'échec (1min, 5min, 30min)

POST/webhooks

Enregistre un endpoint et retourne le secret HMAC. Stocker ce secret immédiatement— il n'est plus accessible ensuite.

Enregistrer un webhook · cURL / Shell
TOKEN="eyJhbGci..."

curl -X POST https://api.qualiforma.site/api/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H 'X-Tenant-ID: votre-tenant' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://votre-app.com/webhooks/qualiforma",
    "events": [
      "enrollment.created",
      "enrollment.completed",
      "payment.completed",
      "course.published"
    ],
    "description": "Webhook principal production"
  }'

# Réponse :
# {
#   "data": {
#     "id": "wh_abc123",
#     "url": "https://votre-app.com/webhooks/qualiforma",
#     "secret": "whsec_XYZ...",  ← à stocker dans votre secret manager
#     "events": ["enrollment.created", "..."],
#     "active": true
#   }
# }

Format du payload

Chaque événement est envoyé en POST avec le header X-Qualiforma-Signature: {hmac_hex}.

Exemple — enrollment.created · JSON
{
  "id": "evt_abc123",
  "event": "enrollment.created",
  "tenantId": "votre-tenant",
  "createdAt": "2026-01-01T12:30:00.000Z",
  "data": {
    "enrollmentId": "enr_def456",
    "courseId": "crs_ghi789",
    "userId": "usr_jkl012",
    "courseTitle": "Excel Avancé pour RH",
    "userName": "Marie Martin",
    "enrolledAt": "2026-01-01T12:30:00.000Z"
  }
}

Vérification HMAC-SHA256

La signature est calculée sur le raw body bytes (avant tout parsing JSON). Utiliser hmac.compare_digest / timingSafeEqual pour éviter les timing attacks.

Vérification HMAC — Python/Flask · Python
import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = 'whsec_XYZ...'  # depuis votre secret manager

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    """Vérifie la signature HMAC-SHA256 du webhook QualiForma."""
    expected = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

@app.route('/webhooks/qualiforma', methods=['POST'])
def handle_webhook():
    payload = request.get_data()  # raw bytes — NE PAS parser avant vérification
    signature = request.headers.get('X-Qualiforma-Signature', '')

    if not verify_webhook(payload, signature, WEBHOOK_SECRET):
        abort(401, 'Signature invalide')

    event = request.json
    handle_event(event)
    return '', 200

def handle_event(event: dict):
    match event['event']:
        case 'enrollment.created':
            # Inscrire l'apprenant dans votre SIRH
            pass
        case 'payment.completed':
            # Émettre une facture
            pass
        case 'enrollment.completed':
            # Générer un certificat externe
            pass
Vérification HMAC — Node.js/Express · TypeScript
import { createHmac, timingSafeEqual } from 'node:crypto';
import type { Request, Response } from 'express';

const WEBHOOK_SECRET = process.env.QUALIFORMA_WEBHOOK_SECRET!;

function verifySignature(payload: Buffer, signature: string): boolean {
  const expected = createHmac('sha256', WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');

  // timingSafeEqual prévient les timing attacks
  return timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(signature, 'hex')
  );
}

// Express middleware
export function webhookHandler(req: Request, res: Response): void {
  // IMPORTANT: utiliser le raw body (Buffer), pas req.body parsé
  const payload = req.rawBody as Buffer;
  const signature = req.headers['x-qualiforma-signature'] as string;

  if (!signature || !verifySignature(payload, signature)) {
    res.status(401).json({ error: 'Signature invalide' });
    return;
  }

  const event = JSON.parse(payload.toString('utf-8'));

  switch (event.event) {
    case 'enrollment.created':
      // Traiter l'inscription
      break;
    case 'payment.completed':
      // Traiter le paiement
      break;
    case 'enrollment.completed':
      // Traiter la complétion
      break;
    default:
      break;
  }

  res.status(200).send('ok');
}
Vérification HMAC — PHP · PHP
<?php
// Verification du webhook QualiForma — Symfony / Slim / Laravel

$webhookSecret = getenv('QUALIFORMA_WEBHOOK_SECRET') ?: 'whsec_XYZ...';

function verifySignature(string $payload, string $signature, string $secret): bool
{
    $expected = hash_hmac('sha256', $payload, $secret);
    // hash_equals previent les timing attacks
    return hash_equals($expected, $signature);
}

// Handler webhook (exemple Slim/PSR-7, adaptable a Symfony/Laravel)
function handleWebhook(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, string $secret)
{
    // IMPORTANT : raw body bytes, jamais $request->getParsedBody()
    $payload = (string) $request->getBody();
    $signature = $request->getHeaderLine('X-Qualiforma-Signature');

    if ($signature === '' || !verifySignature($payload, $signature, $secret)) {
        return $response->withStatus(401);
    }

    $event = json_decode($payload, true);

    switch ($event['event']) {
        case 'enrollment.created':
            // Inscrire l'apprenant dans votre SIRH
            break;
        case 'payment.completed':
            // Emettre une facture
            break;
        case 'enrollment.completed':
            // Generer un certificat externe
            break;
    }

    return $response->withStatus(200);
}

Événements disponibles (14)

Liste des événements webhook disponibles
ÉvénementDescription
enrollment.createdUn apprenant vient d'être inscrit à une formation
enrollment.completedUn apprenant a terminé une formation (100% progression)
enrollment.suspendedAccès suspendu manuellement par un admin
enrollment.revokedInscription révoquée (remboursement ou admin)
payment.completedPaiement capturé avec succès
payment.refundedPaiement remboursé (partiel ou total)
payment.failedPaiement échoué ou annulé par l'apprenant
course.publishedFormation publiée (DRAFT → PUBLISHED)
course.archivedFormation archivée
session.startedSession live démarrée
session.endedSession live terminée
emargement.signedEmargement signé par un apprenant
certificate.generatedAttestation de formation générée
user.createdNouvel utilisateur créé dans le tenant

Bonnes pratiques

  • →Répondez 200 immédiatement, traitez l'événement en arrière-plan (queue)
  • →Rendez votre handler idempotent — QualiForma peut envoyer un événement plusieurs fois
  • →Vérifiez toujours la signature avant tout traitement
  • →Stockez le secret whsec_ dans un secret manager (jamais en clair dans le code)
  • →Loggez les événements reçus pour le debugging (sans les données sensibles)
QualiForma

La plateforme de formation professionnelle certifiee Qualiopi.

Plateforme

  • Catalogue
  • Espace formateur
  • Étude de besoin

Societe

  • A propos
  • Contact

Ressources

  • Développeurs
  • Référence API
  • Webhooks

Legal

  • CGV
  • Mentions legales
  • Confidentialite

Catalogue par catégorie

  • Management
  • Digital
  • Communication
  • Langues
  • Sécurité
  • Gestion

Comparatifs

  • QualiForma vs Didask
  • QualiForma vs Edusign
  • QualiForma vs Klaxoon
  • QualiForma vs 360Learning

Glossaire Qualiopi

  • I01 — Conditions d'information
  • I05 — Adaptation des prestations
  • I11 — Évaluations en cours
  • I22 — Compétences des intervenants
  • I30 — Recueil des appréciations
  • Voir les 32 indicateurs →

© 2026 QualiForma. Tous droits reserves.

Certifie Qualiopi