Webhooks sortants
Ce que ça fait — et ce que ça ne fait pas
- C'est uniquement sortant. C'est Vaks PM qui vous appelle. Rien ici n'ouvre de chemin entrant vers votre instance : cela fonctionne donc depuis un déploiement entièrement privé, pourvu qu'il puisse joindre votre récepteur.
- La livraison est « au moins une fois », pas « exactement une fois ». Les réessais font que votre récepteur peut voir deux fois la même livraison. Dédupliquez sur l'identifiant de livraison — voir l'étape 2.
- L'ordre n'est pas garanti. L'envoi se fait en « fire-and-forget » et les réessais réordonnent librement. N'inférez jamais une séquence depuis l'ordre d'arrivée ; utilisez les horodatages de la charge utile.
- Certaines actions déclenchent plus d'un événement. Terminer une tâche émet à la fois
task.updatedettask.completed. Un récepteur abonné à tout verra les deux. - Aucune licence requise. Les webhooks sont disponibles sur toutes les offres.
Événements souscriptibles
Vingt-sept événements, plus le joker * qui souscrit à l'ensemble — y compris à ceux ajoutés dans de futures versions.
| Famille | Événements |
|---|---|
| Tâches | task.created · task.updated · task.deleted · task.moved · task.assigned · task.unassigned · task.completed |
| Flux de travail des agents | task.claimed · task.released · task.review_requested · task.changes_requested · task.approved · deliverable.submitted · deliverable.reviewed |
| Projets | project.created · project.updated · project.activated · project.announcement |
| Collaboration | comment.created |
| Compétences | skill_request.created · skill_request.acknowledged · skill_request.resolved · skill_request.rejected · skill_request.reminded |
| Exploitation | audit.alert · backup.failed · automation.alert |
audit.alert (échecs de connexion répétés), backup.failed et automation.alert ne sont rattachés à aucun projet : un webhook de périmètre projet ou équipe ne les reçoit donc jamais. project.activated mérite aussi d'être connu : il se déclenche au passage à l'état actif et constitue le signal qu'un provisionneur externe doit attendre.
Prérequis
| Côté | Ce qu'il vous faut |
|---|---|
| Votre côté | Un point de terminaison HTTPS qui répond 2xx rapidement. Tout le reste compte comme un échec. Il doit être résoluble publiquement — les adresses privées, de loopback et link-local sont refusées, voir gouvernance. Pour un canal Teams, un flux Power Automate suffit ; aucun code. |
| Vaks PM | org:manage pour les webhooks à l'échelle de l'organisation. Les chefs de projet peuvent créer des webhooks pour leur propre projet sans être administrateurs — voir périmètre & délégation. |
Étape 1 — Créer le point de terminaison
Ouvrez Admin → Intégrations → Webhooks → Endpoints et pressez Nouveau webhook.
| Champ | Quoi renseigner |
|---|---|
| Libellé | Texte libre pour votre propre usage, affiché dans la liste et dans le journal des livraisons. |
| URL | Votre récepteur. HTTPS fortement recommandé — http:// est accepté mais envoie la charge utile en clair. Seuls les ports 80, 443, 8080 et 8443 sont autorisés. |
| Événements | Cochez ce que vous voulez. Préférez nommer les événements que vous traitez plutôt que de souscrire à tout : le joker livrera de futurs types d'événements que votre récepteur n'a jamais vus. |
| Périmètre | Toute l'org, Un projet ou Une équipe. Voir ci-dessous. |
| Format du payload | JSON (brut) pour vos propres intégrations, Teams (Adaptive Card) pour publier dans un canal. Voir le format Teams. |
| Secret | Laissez vide et un secret est généré pour vous. C'est la clé que votre récepteur utilise pour vérifier les signatures. |
| Actif | Cochez pour commencer à livrer. |
Étape 2 — Vérifier la signature
Chaque requête porte une signature HMAC-SHA256 du corps, calculée avec le secret de votre webhook. C'est sa vérification qui rend le point de terminaison sûr à exposer : sans elle, quiconque devine votre URL peut poster de faux événements.
Deux détails décident si votre implémentation est correcte :
- Signez le corps brut, exactement tel que reçu. Analyser le JSON puis le re-sérialiser change les octets et la signature ne correspondra jamais. Capturez le corps brut avant que votre framework ne l'analyse.
- Comparez en temps constant, pas avec
==, pour que la comparaison ne puisse pas servir d'oracle temporel.
// Node.js / Express — attention : express.raw, pas express.json
const crypto = require('crypto');
app.post('/vaks-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const expected =
'sha256=' + crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
const got = req.get('X-Vaks-Signature') || '';
const a = Buffer.from(expected), b = Buffer.from(got);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).end();
}
const event = JSON.parse(req.body.toString('utf8'));
// Dédupliquez : un même deliveryId peut arriver plusieurs fois.
if (alreadyHandled(req.get('X-Vaks-Delivery'))) return res.status(200).end();
handle(event);
res.status(200).end(); // répondez 2xx vite ; faites le travail de façon asynchrone
});
X-Vaks-Delivery, qui reste stable à travers les réessais d'une même livraison, et considérez X-Vaks-Timestamp comme purement informatif. Si le rejeu compte dans votre modèle de menace, rejetez les livraisons dont l'horodatage est éloigné de l'instant présent, en plus de vérifier la signature.
Étape 3 — Tester
Pressez Envoyer un test sur la ligne du webhook. Cela envoie immédiatement un événement ping, quels que soient les événements auxquels vous avez souscrit : il fonctionne donc toujours comme contrôle de connectivité.
Ouvrez ensuite Livraisons récentes sous la liste et vérifiez que la ligne indique SUCCESS avec une réponse 2xx. Si ce n'est pas le cas, dépliez Détails — l'erreur, la charge utile exacte envoyée et le corps de réponse de votre récepteur y sont tous enregistrés. Ce panneau est l'outil de débogage le plus utile de cette page.
payload. C'est attendu, et vrai uniquement pour l'événement de test.
Charge utile & en-têtes
Au format brut, chaque requête a la même enveloppe, les données propres à l'événement étant imbriquées sous payload :
{
"event": "task.created",
"deliveryId": "9f1c…",
"organizationId": "…",
"at": "2026-07-19T21:04:11.482Z",
"payload": { }
}
| En-tête | Signification |
|---|---|
X-Vaks-Event | Le nom de l'événement, pour router sans analyser le corps. |
X-Vaks-Delivery | Identifiant de livraison. Stable à travers chaque réessai d'une même livraison — c'est votre clé de déduplication. |
X-Vaks-Signature | sha256= suivi du HMAC du corps brut, en hexadécimal minuscule. |
X-Vaks-Timestamp | Heure d'envoi, en secondes epoch. Régénérée à chaque réessai, et non couverte par la signature. |
User-Agent | Vaks-PM-Webhook/1.0 |
La forme des charges utiles varie selon l'événement et se découvre au mieux depuis une livraison réelle : souscrivez, déclenchez l'action, et lisez la charge utile enregistrée dans le journal des livraisons. Construisez votre récepteur de façon défensive — traitez les champs comme facultatifs et ignorez ce que vous ne reconnaissez pas, car les charges utiles gagnent des champs d'une version à l'autre.
Réessais & échecs
- Un succès est tout HTTP 2xx. Tout le reste — 3xx, 4xx, 5xx, délai dépassé, échec DNS — est un échec.
- Le délai est de 10 secondes par tentative. Répondez immédiatement et faites votre traitement de façon asynchrone ; un récepteur lent produit des réessais et des doublons.
- 5 tentatives par livraison, avec un backoff exponentiel démarrant à 5 secondes.
- Les redirections sont suivies, jusqu'à 3 sauts, chacun re-contrôlé au regard des règles de sécurité.
Format Microsoft Teams
Choisir Teams (Adaptive Card) remplace l'enveloppe JSON par un message Teams enveloppant une Adaptive Card. Le corps envoyé par Vaks PM ressemble à ceci :
{
"type": "message",
"attachments": [{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": { /* une Adaptive Card : titre, accroche, jusqu'à 8 faits */ }
}]
}
Cette forme est exactement ce qu'attend Teams. La carte affiche un titre pour l'événement, une accroche d'une ligne tirée de la charge utile, et jusqu'à huit faits lorsqu'ils sont présents : statut, priorité, assigné, auteur, échéance, équipe, compétence et projet. Il y a deux façons de la faire arriver dans un canal — choisissez-en une.
Option A — Teams Workflows / Power Automate (recommandé)
C'est le chemin actuel de Microsoft, et celui qui continuera de fonctionner. Microsoft retire les anciens connecteurs de webhook entrant (option B) : préférez donc celui-ci pour toute nouvelle configuration.
- Dans le canal Teams cible, ouvrez le menu … → Workflows (ou passez directement par Power Automate). Choisissez le modèle « Post to a channel when a webhook request is received ».
- Sélectionnez l'équipe et le canal où publier, puis créez le flux. Son déclencheur — « When a Teams webhook request is received » — génère une URL de rappel. Copiez-la.
- Dans Vaks PM, créez un webhook (ou modifiez-en un) : réglez le Format du payload sur Teams (Adaptive Card), collez l'URL de rappel dans URL, et cochez les événements voulus.
- De retour dans le flux, assurez-vous que l'action de publication reçoit bien l'Adaptive Card issue du corps de la requête. Vaks PM envoie la carte sous
attachments[0].content; si le modèle ne la référence pas déjà, pointez l'action « Post card in a chat or channel » vers ce chemin. - Enregistrez le flux, puis pressez Envoyer un test sur le webhook dans Vaks PM. Une carte doit apparaître dans le canal en quelques secondes. Sinon, ouvrez Livraisons récentes — un 2xx sans carte signifie généralement que le flux s'est exécuté mais que le champ de la carte n'était pas relié à
attachments[0].content.
Option B — Webhook entrant Teams (le plus simple, en cours de retrait)
Si votre tenant autorise encore les connecteurs de webhook entrant, aucune configuration de flux n'est nécessaire — le corps envoyé par Vaks PM est exactement ce qu'attend le connecteur.
- Dans le canal, menu … → Connectors → Incoming Webhook → Configure. Nommez-le, réglez éventuellement une icône, puis Create.
- Copiez l'URL générée — elle est sur
….webhook.office.com. - Dans Vaks PM, créez le webhook avec le Format du payload = Teams (Adaptive Card) et cette URL. Envoyer un test ; la carte se publie directement.
webhook.office.com (option B) et les URL Power Automate. Un déclencheur Workflows hébergé ailleurs (par exemple sur logic.azure.com) n'est pas couvert par ce contrôle, mais est soumis à la liste Domaines autorisés (allowlist) si vous en définissez une — ajoutez-y l'hôte du déclencheur. Un envoi bloqué apparaît dans le journal des livraisons avec la mention Bloqué par la gouvernance et le motif.
deliveryId ni organizationId dans le corps, seulement dans les en-têtes : un flux qui en a besoin doit donc lire les en-têtes. Et la carte ne porte aucun lien de retour vers l'élément : le worker qui la rend ne peut pas résoudre votre adresse publique, un bouton pointerait donc dans le vide. Les titres de cartes et les libellés de champs sont toujours en français, quelle que soit la langue de votre interface.
Périmètre & délégation
Le périmètre d'un webhook décide quels événements l'atteignent :
| Périmètre | Reçoit | Qui peut le créer |
|---|---|---|
| Toute l'org | Tout, y compris les événements d'exploitation. | Les administrateurs uniquement, toujours. |
| Un projet | Uniquement les événements portant ce projet. | Les administrateurs, et les rôles délégués — voir ci-dessous. |
| Une équipe | Les événements rattachés à cette équipe, plus les événements des projets auxquels l'équipe est affectée. | Les administrateurs, et les leads d'équipe selon la même politique. |
La délégation est pilotée par Qui peut créer un webhook, sous Admin → Intégrations → Webhooks → Gouvernance :
- Project manager (par défaut) — les PM de portefeuille, plus le chef du projet concerné.
- Portfolio manager — les PM de portefeuille uniquement.
- Admin — personne d'autre que les administrateurs.
Lorsque c'est permis, les chefs de projet y accèdent depuis l'onglet Gérer → Intégrations du projet, qui n'apparaît que si leur rôle l'autorise. Le périmètre est forcé côté serveur à leur propre projet : un utilisateur délégué ne peut donc pas l'élargir — ni à la création, ni en le modifiant ensuite.
Listes d'autorisation & protection anti-SSRF
Parce qu'un webhook envoie vos données à une adresse saisie par quelqu'un, les destinations sont contrôlées. Deux réglages sous Gouvernance resserrent cela, et il vaut la peine de les activer tous les deux avant de déléguer la création de webhooks aux chefs de projet :
| Réglage | Effet |
|---|---|
| Domaines autorisés (allowlist) | Lorsqu'elle est non vide, seuls ces hôtes et leurs sous-domaines peuvent être ciblés. Vide signifie tout hôte public. |
| Tenants Entra ID autorisés (Teams) | Ne s'applique qu'aux points de terminaison Microsoft — webhooks entrants Teams et Power Automate. Vaks PM extrait l'identifiant de tenant depuis l'URL et refuse les destinations appartenant à un autre tenant, de sorte que les données ne peuvent pas être publiées dans le Microsoft 365 de quelqu'un d'autre. |
Indépendamment de tout réglage, les cibles internes sont toujours refusées : localhost, .local, .internal, ainsi que les plages d'adresses privées, de loopback ou link-local. Ce contrôle a lieu deux fois — à l'enregistrement de l'URL puis de nouveau à l'envoi, avec une résolution DNS réelle, ce qui déjoue également un nom d'hôte qui ne résoudrait vers une adresse privée que plus tard.
Journal des livraisons
Livraisons récentes, sous la liste des points de terminaison, enregistre chaque tentative : statut et code de réponse, événement, webhook ciblé, date de mise en file, et nombre de tentatives nécessaires. Déplier une ligne montre l'erreur, la charge utile envoyée et le corps de réponse de votre récepteur (les 1000 premiers caractères).
Une livraison en échec peut être rejouée avec Réessayer. Elle réutilise la charge utile stockée et le même identifiant de livraison : un récepteur qui déduplique correctement la reconnaîtra donc — mais l'horodatage et la signature sont recalculés. La destination est d'abord re-contrôlée au regard de la gouvernance actuelle, si bien qu'un réessai vers une adresse désormais interdite est refusé.
Dépannage
| Symptôme | Cause & correctif |
|---|---|
| La signature ne correspond jamais | Presque toujours le problème du corps brut : le corps a été analysé puis re-sérialisé avant la vérification. Capturez les octets tels que reçus. Vérifiez aussi que vous avez conservé le préfixe sha256= dans la comparaison. |
| Le test réussit, mais les vrais événements n'arrivent jamais | Le test ignore votre souscription aux événements. Vérifiez que les événements sont bien cochés, et que le périmètre correspond — un webhook de périmètre projet ne reçoit rien pour les autres projets, et ne reçoit jamais les événements d'exploitation. |
| Bloqué par la gouvernance dans le journal des livraisons | La destination enfreint une règle : absente de la liste d'autorisation de domaines, adresse privée, port non autorisé, ou URL Microsoft dont le tenant n'est pas permis. Le motif enregistré indique laquelle. |
| Tout a échoué d'un coup, et le webhook est maintenant désactivé | 5 livraisons consécutives épuisées. Corrigez le récepteur, puis modifiez le webhook et cochez Actif. Les événements manqués pendant la désactivation ne sont pas rejoués. |
| Événements en double | C'est attendu. Réessais, et certaines actions émettant deux événements. Dédupliquez sur X-Vaks-Delivery et considérez comme normal l'appariement de task.updated avec un événement plus spécifique. |
| Les livraisons s'arrêtent après une régénération | La régénération n'a pas de fenêtre de recouvrement. Le récepteur doit détenir le nouveau secret avant la livraison suivante. |
| Délais dépassés sous charge | 10 secondes par tentative, et le chronomètre inclut votre traitement. Accusez réception avec un 2xx d'abord, travaillez ensuite. |
Ce qui est journalisé
La création, la modification, la suppression d'un webhook et la régénération de son secret sont toutes auditées en sévérité critique — un webhook est un chemin d'exfiltration de données, ses changements sont donc traités comme des événements de sécurité. Vous les retrouverez sous Admin → Sécurité & Conformité → Journal d'audit, avec l'acteur et la cible.
À lire aussi : toutes les intégrations · exploitation pour l'alerte de sauvegarde, qui utilise l'événement backup.failed.