MeetingHub MeetingHub Télécharger la collection Postman

Référence — webhooks

Les routes de gestion, les événements, la signature et les réessais.

Un webhook est une adresse de votre système que MeetingHub appelle quand un événement se produit dans un établissement. Il se pose de deux façons :

Dans les deux cas, l'appel envoyé à votre serveur, sa signature et ses réessais sont les mêmes : ils sont décrits à la fin de cette page.

L'objet Webhook

{
  "id": "5a74f72e-81af-402c-a2af-a5ebd35b7bba",
  "url": "https://ent.mon-lycee.fr/hooks/meetinghub",
  "events": ["attendance.closed"],
  "label": "ENT — émargement",
  "enabled": true,
  "disabledReason": null,
  "consecutiveFailures": 0,
  "lastSuccessAt": "2026-10-14T10:30:05.000Z",
  "lastFailureAt": null,
  "createdAt": "2026-09-02T08:00:00.000Z",
  "partnerWebhookId": null

}
ChampTypeDescription
iduuidIdentifiant du webhook
urlstringL'adresse appelée, sous sa forme normalisée
eventsstring[]Les événements souscrits, sans doublon. Vide : aucun. Voir Événements
labelstring ou nullUn nom libre
enabledbooleanfalse si le webhook a été coupé, par vous ou par le serveur
disabledReasonstring ou nullPourquoi le serveur l'a coupé ; null s'il est actif ou si c'est vous qui l'avez coupé
consecutiveFailuresintegerLe nombre de tentatives échouées d'affilée, tous événements confondus. Remis à 0 par une livraison réussie ou une réactivation
lastSuccessAtdatetime ou nullLa dernière livraison réussie
lastFailureAtdatetime ou nullLa dernière tentative échouée
createdAtdatetimeLa date de création
partnerWebhookIduuid ou nullPour la copie d'un webhook posé par l'éditeur partenaire de l'établissement : l'identifiant de ce webhook chez l'éditeur ; null sinon

Le secret de signature n'est jamais rendu, sauf une fois, à la création.

L'objet Livraison

Une livraison est l'envoi d'un événement à un webhook, avec ses tentatives.

ChampTypeDescription
iduuidIdentifiant de la livraison. C'est la valeur de l'en-tête x-meetinghub-delivery
webhookIduuidLe webhook destinataire
eventstringL'événement
payloadstringLe corps exact envoyé, en JSON sérialisé. Il est figé à l'émission et identique à chaque tentative
statusstringpending : en attente d'une tentative ; delivered : livrée ; failed : abandonnée
attemptsintegerLe nombre de tentatives faites
nextAttemptAtdatetimeLa prochaine tentative prévue, pour une livraison pending
lastAttemptAtdatetime ou nullLa dernière tentative ; null avant la première
responseStatusinteger ou nullLe code HTTP rendu par votre serveur à la dernière tentative ; null s'il n'a pas répondu. 403 pour une adresse refusée par MeetingHub
errorstring ou nullPour un échec : les 500 premiers caractères du corps de votre réponse, ou la cause de l'échec ; null pour une réussite
createdAtdatetimeLe moment de l'émission

Événements

ÉvénementÉmis quand
attendance.closedUne feuille de présence vient d'être arrêtée, automatiquement ou à la main
session.cancelledUn cours est annulé un par un : depuis MeetingHub, ou par DELETE /api/institutions/:institutionId/lessons/:lessonId. Une annulation dans un dépôt d'emploi du temps, ou par sa ref, ne l'émet pas
roster.importedUne liste d'inscrits vient d'être appliquée, par l'API ou par un import de fichier dans MeetingHub. Un essai à blanc ne l'émet pas

GET /api/institutions/:institutionId/webhooks

Liste tous les webhooks de l'établissement, coupés compris, et les copies des webhooks de son éditeur partenaire.

Réponse 200 :

{
  "webhooks": [ { "id": "5a74f72e-…", "url": "https://ent.mon-lycee.fr/hooks/meetinghub", "…": "…" } ],
  "events": ["attendance.closed", "session.cancelled", "roster.imported"]

}
ChampTypeDescription
webhooksWebhook[]Les webhooks, du plus ancien au plus récent. Voir L'objet Webhook
eventsstring[]Tous les événements qui existent

POST /api/institutions/:institutionId/webhooks

Crée un webhook et rend son secret, une seule fois.

{
  "url": "https://ent.mon-lycee.fr/hooks/meetinghub",
  "events": ["attendance.closed"],
  "label": "ENT — émargement"

}
ChampTypeObligatoireRègles
urlstringouiVoir Adresses acceptées
eventsstring[]ouiPeut être vide. Chaque valeur doit être un événement existant ; les doublons sont retirés
labelstringnonEspaces de bord retirés. Vide ou absent : null

Réponse 201 :

{
  "webhook": {
    "id": "5a74f72e-81af-402c-a2af-a5ebd35b7bba",
    "url": "https://ent.mon-lycee.fr/hooks/meetinghub",
    "events": ["attendance.closed"],
    "label": "ENT — émargement",
    "enabled": true,
    "disabledReason": null,
    "consecutiveFailures": 0,
    "lastSuccessAt": null,
    "lastFailureAt": null,
    "createdAt": "2026-09-02T08:00:00.000Z",
    "partnerWebhookId": null,
    "secret": "whsec_Lc68se…"
  },
  "notice": "Ce secret ne sera plus affiché. Copiez-le maintenant."

}
ChampTypeDescription
webhookWebhookLe webhook créé, avec en plus le champ secret
webhook.secretstringLe secret de signature : whsec_ suivi de 43 caractères. Il n'est plus jamais rendu
noticestringUn rappel destiné à la personne qui crée le webhook
CodeCorpsCause
400adresse illisibleurl absente ou qui n'est pas une adresse
400seul https est acceptéurl n'est pas en https
400pas d’identifiants dans l’adresseurl contient un nom d'utilisateur ou un mot de passe
400adresse interne refuséeurl vise une adresse interne
400adresse trop longueurl dépasse 2 000 caractères
400events attend une listeevents absent ou pas un tableau
400événement inconnu : … — parmi …Une valeur de events n'est pas un événement

Ces refus portent le code invalid.

Adresses acceptées

RègleDétail
Protocolehttps seulement
IdentifiantsAucun nom d'utilisateur ni mot de passe dans l'adresse
Noms refuséslocalhost, ip6-localhost, et tout nom qui finit par .local, .localhost, .internal, .intranet ou .home.arpa
Adresses IP refuséesToute adresse privée, locale ou réservée, en IPv4 comme en IPv6, y compris une IPv4 écrite sous une forme IPv6
Longueur2 000 caractères au plus

La vérification est refaite à CHAQUE appel, sur les adresses obtenues en résolvant le nom : toutes doivent être publiques, et la connexion est faite à l'une d'elles, et à elle seule. Un nom qui résout vers une adresse interne au moment de l'appel est refusé, même s'il était public à la création du webhook.

PATCH /api/institutions/:institutionId/webhooks/:webhookId

Modifie un webhook. Seuls les champs présents sont modifiés.

Paramètre de cheminTypeDescription
webhookIduuidL'identifiant du webhook
ChampTypeObligatoireRègles
urlstringnonMêmes règles qu'à la création
eventsstring[]nonMêmes règles qu'à la création ; remplace la liste entière
labelstring ou nullnonVide, null ou autre chose qu'une chaîne : null
enabledbooleannonfalse coupe le webhook ; true le réactive et remet disabledReason à null et consecutiveFailures à 0

Une nouvelle url s'applique aussi aux livraisons encore en attente. Un webhook coupé garde ses livraisons en attente ; il les reprend quand on le réactive.

Réponse 200 :

{ "webhook": { "id": "5a74f72e-…", "enabled": true, "…": "…" } 
}
ChampTypeDescription
webhookWebhookLe webhook modifié, sans son secret
CodeCorpsCause
400mêmes messages qu'à la créationurl ou events invalide
400enabled attend un booléenenabled n'est pas true ou false
404alerte inconnueAucun webhook de cet identifiant dans l'établissement, ou webhookId mal formé

DELETE /api/institutions/:institutionId/webhooks/:webhookId

Supprime un webhook et tout son historique de livraisons.

Paramètre de cheminTypeDescription
webhookIduuidL'identifiant du webhook

Réponse 204, sans corps.

CodeCorpsCause
404alerte inconnueAucun webhook de cet identifiant dans l'établissement, ou webhookId mal formé

GET /api/institutions/:institutionId/webhooks/:webhookId/deliveries

Les livraisons d'un webhook, de la plus récente à la plus ancienne. C'est aussi ainsi que l'établissement suit les livraisons d'un webhook de son éditeur partenaire : par sa copie.

Paramètre de cheminTypeDescription
webhookIduuidL'identifiant du webhook
Paramètre de requêteTypeDéfautBornesDescription
limitinteger501 à 200Nombre de livraisons rendues. 0 ou une valeur illisible valent 50

Réponse 200 :

{
  "deliveries": [
    {
      "id": "c1d2e3f4-…",
      "webhookId": "5a74f72e-…",
      "event": "attendance.closed",
      "payload": "{\"event\":\"attendance.closed\",\"id\":\"evt_…\",…}",
      "status": "delivered",
      "attempts": 1,
      "nextAttemptAt": "2026-10-14T10:30:05.000Z",
      "lastAttemptAt": "2026-10-14T10:30:05.000Z",
      "responseStatus": 200,
      "error": null,
      "createdAt": "2026-10-14T10:30:04.000Z"
    }
  ]

}
ChampTypeDescription
deliveriesLivraison[]Voir L'objet Livraison
CodeCorpsCause
404alerte inconnueAucun webhook de cet identifiant dans l'établissement, ou webhookId mal formé

L'appel envoyé à votre serveur

MeetingHub fait un POST sur l'adresse du webhook.

En-têteValeur
content-typeapplication/json
x-meetinghub-signaturet= suivi de l'heure de la tentative en secondes Unix, ,v1= puis la signature en hexadécimal. Voir Signature
x-meetinghub-eventLe nom de l'événement
x-meetinghub-deliveryL'identifiant de la livraison, identique à chaque tentative : il sert à dédoublonner
user-agentMeetingHub-Webhooks/1

Le corps :

{
  "event": "attendance.closed",
  "id": "evt_9xK2pQ7rLm3sT8vW",
  "occurredAt": "2026-10-14T10:30:04.118Z",
  "organisationId": "6c1d3a52-7a0e-4f0b-9c55-0a54c3e1f2b8",
  "data": { "sessionId": "9214f192-…", "…": "…" }

}
ChampTypeDescription
eventstringLe nom de l'événement
idstringL'identifiant de l'événement : evt_ suivi de 16 caractères. Tous les webhooks qui reçoivent un même événement reçoivent le même id
occurredAtdatetimeLe moment de l'émission
organisationIduuidL'établissement concerné : le :institutionId des routes de /api
dataobjectLes données de l'événement, décrites ci-dessous

attendance.closed

Champ de dataTypeDescription
sessionIduuidLe cours. C'est le :lessonId à passer à GET /api/institutions/:institutionId/lessons/:lessonId/attendance
roomIduuidLa salle
groupIduuid ou nullLa classe attendue
sheetIduuidLa feuille arrêtée
roomNamestringLe nom de la salle, tel que recopié dans la feuille
groupNamestring ou nullLe nom de la classe, tel que recopié dans la feuille
titlestring ou nullL'intitulé du cours
startsAtdatetimeL'heure de début prévue
durationMinutesintegerLa durée prévue
expectedCountintegerL'effectif attendu
presentCountintegerLe nombre de personnes venues
closedAtdatetimeLe moment de l'arrêt
signaturestring ou nullLe code de vérification de la feuille

Le corps ne contient pas la feuille elle-même : récupérez-la avec une clé de portée attendance:read. Un cours qui n'a rien à relever n'a pas de feuille, et n'émet pas cet événement.

session.cancelled

Champ de dataTypeDescription
sessionIduuidLe cours annulé — son :lessonId
roomIduuidSa salle
groupIduuid ou nullSa classe
titlestring ou nullSon intitulé
startsAtdatetimeSon heure de début prévue
endsAtdatetimeSon heure de fin prévue
cancelledAtdatetimeLe moment de l'annulation

roster.imported

Champ de dataTypeDescription
groupIduuidLa classe
groupNamestringSon nom
readintegerLe nombre d'entrées de la liste
accountsCreatedintegerLes comptes créés
addedToGroupintegerLes personnes ajoutées à la classe
rejectedintegerLe nombre d'entrées refusées

Il ne contient ni courriel, ni téléphone, ni lien d'activation.

Signature

v1 est un HMAC-SHA256, en hexadécimal minuscule, calculé avec le secret du webhook sur la chaîne suivante :

<t>.<corps brut>

où t est la valeur de t= dans l'en-tête, et le corps brut est la suite exacte d'octets reçue, avant toute analyse du JSON. Le secret s'utilise tel qu'il a été rendu à la création, préfixe whsec_ compris.

La signature est recalculée à chaque tentative : t est l'heure de la tentative, pas celle de l'événement. Refusez un appel dont t s'écarte de plus de 300 secondes de votre horloge : c'est ce qui empêche de rejouer un appel intercepté. Comparez les signatures à temps constant.

Livraison et réessais

ParamètreValeur
Délai de réponse accordé10 secondes par tentative
Réponse acceptéeTout code 2xx
Corps de votre réponseLu sur 8 Ko au plus ; les 500 premiers caractères d'une réponse d'échec sont gardés dans error
RedirectionsJamais suivies : une réponse 3xx est un échec, sans réessai
RéessayéAucune réponse, 408, 429, et tout code 5xx
Abandonné sans réessaiTout autre code 4xx, les 3xx, et une adresse refusée par MeetingHub — adresse interne, ou nom qui ne se résout pas
Tentatives au plus6 : une première, puis 5 réessais
Délais entre les tentatives2, 4, 8, 16 puis 32 secondes, chacun allongé d'au plus 25 % au hasard
Relève de la fileToutes les 5 secondes, par lots de 20 livraisons, envoyées en parallèle
AbandonEnviron une à deux minutes après l'émission, si aucune tentative n'a réussi
Coupure du webhookAprès 20 tentatives échouées d'affilée, tous événements confondus ; le motif est écrit dans disabledReason

Deux conséquences à prévoir de votre côté :

Une livraison abandonnée reste consultable avec GET /api/institutions/:institutionId/webhooks/:webhookId/deliveries ; elle n'est pas renvoyée d'elle-même.