Aller au contenu principal
Version: Next

Exemples de Charges Utiles

Cette page fournit des exemples complets et réels de charges utiles webhook pour chaque type d'événement. Utilisez ces exemples pour comprendre la structure des données et construire vos gestionnaires de webhook.

Structure de Requête

Toutes les notifications webhook sont envoyées comme des requêtes HTTP POST avec les caractéristiques suivantes :

Méthode HTTP : POST Content-Type : application/json Authentification : Méthode d'authentification configurée (Bearer, Basic Auth, Clé API, etc.)

Exemple d'En-têtes de Requête

POST /webhooks/beedeez HTTP/1.1
Host: api.votreentreprise.com
Content-Type: application/json
Authorization: Bearer votre-token-bearer-secret
X-Webhook-Source: beedeez
Content-Length: 1234

Événement Certificat Complété

Déclenché lorsqu'un utilisateur complète avec succès un certificat.

Type d'Événement

certificate_completed

Exemple de Charge Utile Complète

{
"_id": "507f1f77bcf86cd799439011",
"_userId": "507f191e810c19729de860ea",
"_ownerId": "507f191e810c19729de860eb",
"_badgeId": "507f191e810c19729de860ec",
"creationDate": "2024-11-27T10:30:00.000Z",
"expires": "2025-11-27T10:30:00.000Z",
"disabled": false,
"expiryReminderSent": false,
"_lessonActiveIds": [
"507f191e810c19729de860ed",
"507f191e810c19729de860ee",
"507f191e810c19729de860ef"
],
"ruleData": {
"type": "certification",
"name": "Certificat Formation Sécurité",
"description": "Compléter tous les modules de formation sécurité",
"validityDurationDays": 365
},
"createdAt": "2024-11-27T10:30:00.000Z",
"updatedAt": "2024-11-27T10:30:00.000Z",
"__v": 0
}

Description des Champs

ChampTypeDescription
_idObjectIdIdentifiant unique pour cette instance de badge/certificat
_userIdObjectIdID de l'utilisateur qui a obtenu le certificat
_ownerIdObjectIdID de l'organisation/propriétaire
_badgeIdObjectIdID de la définition/modèle du badge
creationDateDate ISO 8601Quand l'utilisateur a complété les exigences du certificat
expiresDate ISO 8601Quand le certificat expire (null si pas d'expiration)
disabledBooleanSi le certificat a été désactivé/révoqué
expiryReminderSentBooleanSi un rappel d'expiration a été envoyé
_lessonActiveIdsArray\<ObjectId>IDs des activités de leçon qui ont validé ce certificat
ruleDataObjectDonnées supplémentaires de la définition de la règle du badge
createdAtDate ISO 8601Quand cet enregistrement de certificat a été créé dans la base de données
updatedAtDate ISO 8601Quand cet enregistrement de certificat a été mis à jour pour la dernière fois
__vNumberClé de version MongoDB

Exemple de Cas d'Usage

Scénario : Notifier le système RH quand un employé complète une certification de sécurité

// Gestionnaire de webhook Node.js
app.post('/webhooks/beedeez/certificats', async (req, res) => {
const certificate = req.body;

if (certificate.ruleData.type === 'certification') {
await systemeRH.mettreAJourDossierEmploye({
employeId: certificate._userId,
certification: {
nom: certificate.ruleData.name,
dateCompletion: certificate.creationDate,
dateExpiration: certificate.expires,
certificatId: certificate._id,
},
});

// Envoyer une notification au manager
await serviceNotification.envoyer({
destinataire: 'manager@entreprise.com',
sujet: 'Certification Employé Complétée',
corps: `L'employé a complété ${certificate.ruleData.name}`,
});
}

res.status(200).json({ received: true });
});

Événement Certificat Expiré

Déclenché lorsqu'un certificat atteint sa date d'expiration.

Type d'Événement

certificate_expired

Exemple de Charge Utile Complète

{
"_id": "507f1f77bcf86cd799439011",
"_userId": "507f191e810c19729de860ea",
"_ownerId": "507f191e810c19729de860eb",
"_badgeId": "507f191e810c19729de860ec",
"creationDate": "2023-11-27T10:30:00.000Z",
"expires": "2024-11-27T10:30:00.000Z",
"disabled": false,
"expiryReminderSent": true,
"_lessonActiveIds": ["507f191e810c19729de860ed", "507f191e810c19729de860ee"],
"ruleData": {
"type": "certification",
"name": "Certification Premiers Secours",
"description": "Formation premiers secours et intervention d'urgence",
"validityDurationDays": 365
},
"createdAt": "2023-11-27T10:30:00.000Z",
"updatedAt": "2024-11-27T10:30:00.000Z",
"__v": 0
}

Description des Champs

Les champs sont identiques à l'événement certificate_completed. Différences clés :

  • La date expires est dans le passé (le certificat a expiré)
  • expiryReminderSent est typiquement true (un rappel a été envoyé avant l'expiration)
  • creationDate montre la date de complétion originale (il y a 1 an dans cet exemple)

Exemple de Cas d'Usage

Scénario : Déclencher un workflow de re-certification quand un certificat expire

// Gestionnaire de webhook Node.js
app.post('/webhooks/beedeez/certificats', async (req, res) => {
const certificate = req.body;

// Vérifier si le certificat a expiré
const maintenant = new Date();
const dateExpiration = new Date(certificate.expires);

if (dateExpiration < maintenant) {
// Déclencher le workflow de re-certification
await moteurWorkflow.demarrer({
workflow: 'recertification',
userId: certificate._userId,
certificateId: certificate._badgeId,
certificatNom: certificate.ruleData.name,
dateExpiration: certificate.expires,
});

// Mettre à jour le suivi de conformité
await systemeConformite.marquerCommeExpire({
employeId: certificate._userId,
certificatId: certificate._id,
necessiteRenouvellement: true,
});

// Envoyer une notification à l'employé et au manager
await serviceNotification.envoyerAlerteCertificatExpire({
userId: certificate._userId,
certificatNom: certificate.ruleData.name,
dateExpiration: certificate.expires,
});
}

res.status(200).json({ received: true });
});

Gestion de Plusieurs Types d'Événements

Si votre point de terminaison webhook gère plusieurs types d'événements, vous pouvez les différencier en utilisant la structure de la charge utile ou des métadonnées supplémentaires.

Exemple : Gestionnaire Multi-Événements

app.post('/webhooks/beedeez', async (req, res) => {
const payload = req.body;

// Déterminer le type d'événement basé sur les caractéristiques de la charge utile
const maintenant = new Date();
const dateExpiration = payload.expires ? new Date(payload.expires) : null;

let typeEvenement;
if (dateExpiration && dateExpiration < maintenant) {
typeEvenement = 'certificate_expired';
} else {
typeEvenement = 'certificate_completed';
}

// Router vers le gestionnaire approprié
switch (typeEvenement) {
case 'certificate_completed':
await gererCertificatComplete(payload);
break;
case 'certificate_expired':
await gererCertificatExpire(payload);
break;
default:
console.warn("Type d'événement inconnu:", typeEvenement);
}

res.status(200).json({ received: true, eventType: typeEvenement });
});

async function gererCertificatComplete(certificate) {
console.log('Certificat complété:', certificate._id);
// Logique de gestion de la complétion...
}

async function gererCertificatExpire(certificate) {
console.log('Certificat expiré:', certificate._id);
// Logique de gestion de l'expiration...
}

Réponses d'Erreur

Votre point de terminaison webhook doit retourner des codes de statut HTTP appropriés :

Réponse de Succès

HTTP/1.1 200 OK
Content-Type: application/json

{
"received": true,
"message": "Webhook traité avec succès",
"eventId": "507f1f77bcf86cd799439011"
}

Réponses d'Erreur

Échec d'Authentification :

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
"error": "Non autorisé",
"message": "Identifiants d'authentification invalides"
}

Charge Utile Invalide :

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
"error": "Mauvaise Requête",
"message": "Structure de charge utile invalide"
}

Erreur Serveur Interne :

HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
"error": "Erreur Serveur Interne",
"message": "Échec du traitement du webhook"
}
danger

Retournez uniquement des codes de statut 2xx si le webhook a été reçu et traité avec succès. Retourner 2xx empêche Beedeez de réessayer la livraison.


Gestion de l'Idempotence

Puisque les webhooks peuvent être livrés plusieurs fois, implémentez l'idempotence :

Exemple : Gestionnaire Idempotent

const webhooksTraites = new Set(); // En production, utilisez une base de données

app.post('/webhooks/beedeez', async (req, res) => {
const certificate = req.body;
const webhookId = certificate._id;

// Vérifier si déjà traité
if (webhooksTraites.has(webhookId)) {
console.log('Webhook dupliqué, déjà traité:', webhookId);
return res.status(200).json({
received: true,
duplicate: true,
});
}

try {
// Traiter le webhook
await traiterCertificat(certificate);

// Marquer comme traité
webhooksTraites.add(webhookId);

res.status(200).json({ received: true });
} catch (error) {
console.error('Erreur de traitement du webhook:', error);
res.status(500).json({ error: 'Échec du traitement' });
}
});

Idempotence Basée sur Base de Données

// Utilisation d'une base de données pour suivre les webhooks traités
app.post('/webhooks/beedeez', async (req, res) => {
const certificate = req.body;
const webhookId = certificate._id;

// Vérifier la base de données
const existant = await db.logsWebhook.findOne({ webhookId });

if (existant) {
return res.status(200).json({
received: true,
duplicate: true,
});
}

try {
// Traiter le webhook
await traiterCertificat(certificate);

// Stocker dans la base de données
await db.logsWebhook.insert({
webhookId,
recuLe: new Date(),
payload: certificate,
traite: true,
});

res.status(200).json({ received: true });
} catch (error) {
console.error('Erreur de traitement du webhook:', error);
res.status(500).json({ error: 'Échec du traitement' });
}
});

Test des Webhooks

Test Manuel avec curl

# Tester l'événement certificate_completed
curl -X POST https://votre-endpoint.com/webhooks/beedeez \
-H "Authorization: Bearer votre-token" \
-H "Content-Type: application/json" \
-d '{
"_id": "507f1f77bcf86cd799439011",
"_userId": "507f191e810c19729de860ea",
"_ownerId": "507f191e810c19729de860eb",
"_badgeId": "507f191e810c19729de860ec",
"creationDate": "2024-11-27T10:30:00.000Z",
"expires": "2025-11-27T10:30:00.000Z",
"disabled": false,
"expiryReminderSent": false,
"_lessonActiveIds": ["507f191e810c19729de860ed"],
"ruleData": {
"type": "certification",
"name": "Certificat Test",
"validityDurationDays": 365
},
"createdAt": "2024-11-27T10:30:00.000Z",
"updatedAt": "2024-11-27T10:30:00.000Z"
}'

Test avec Postman

  1. Créez une nouvelle requête POST
  2. Définissez l'URL vers votre point de terminaison webhook
  3. Ajoutez l'en-tête d'authentification (Bearer token, clé API, etc.)
  4. Définissez Content-Type à application/json
  5. Collez l'exemple de charge utile dans le corps
  6. Envoyez la requête et vérifiez la réponse

Exemple d'Intégration Complète

Voici un gestionnaire de webhook complet et prêt pour la production :

const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

// Configuration
const WEBHOOK_TOKEN = process.env.BEEDEEZ_WEBHOOK_TOKEN;
const WEBHOOK_SECRET = process.env.BEEDEEZ_WEBHOOK_SECRET;

// Middleware : Authentification
function authentifierWebhook(req, res, next) {
const authHeader = req.headers.authorization;

if (!authHeader || authHeader !== `Bearer ${WEBHOOK_TOKEN}`) {
return res.status(401).json({ error: 'Non autorisé' });
}

next();
}

// Middleware : Valider la charge utile
function validerChargeUtile(req, res, next) {
const payload = req.body;

if (!payload._id || !payload._userId || !payload._ownerId) {
return res.status(400).json({ error: 'Charge utile invalide' });
}

next();
}

// Gestionnaire de webhook
app.post(
'/webhooks/beedeez',
authentifierWebhook,
validerChargeUtile,
async (req, res) => {
const certificate = req.body;

try {
// Déterminer le type d'événement
const maintenant = new Date();
const dateExpiration = certificate.expires
? new Date(certificate.expires)
: null;
const estExpire = dateExpiration && dateExpiration < maintenant;

// Logger la réception
console.log('Webhook reçu:', {
certificateId: certificate._id,
userId: certificate._userId,
eventType: estExpire ? 'expired' : 'completed',
});

// Répondre immédiatement
res.status(200).json({
received: true,
certificateId: certificate._id,
});

// Traiter de manière asynchrone
if (estExpire) {
await gererCertificatExpire(certificate);
} else {
await gererCertificatComplete(certificate);
}
} catch (error) {
console.error('Erreur de traitement du webhook:', error);
// Retourner quand même 200 si on l'a reçu avec succès
// Logger l'erreur pour investigation
}
},
);

async function gererCertificatComplete(certificate) {
// Mettre à jour le système RH
await mettreAJourSystemeRH(certificate);

// Envoyer un email de félicitations
await envoyerEmailFelicitations(certificate);

// Mettre à jour les analytiques
await suivreCompletionCertificat(certificate);
}

async function gererCertificatExpire(certificate) {
// Marquer comme expiré dans le système de conformité
await marquerCertificatExpire(certificate);

// Déclencher le workflow de re-certification
await declencherRecertification(certificate);

// Envoyer un rappel de renouvellement
await envoyerRappelRenouvellement(certificate);
}

// Démarrer le serveur
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Serveur webhook en écoute sur le port ${PORT}`);
});

Prochaines Étapes