Webhooks
Les webhooks transmettent en temps réel des événements à vos propres systèmes lorsqu'une analyse s'achève ou que l'état d'une non-conformité change. Chaque charge utile est signée, afin que votre récepteur puisse vérifier qu'elle provient bien d'AllyProof.
Événements pris en charge
| Événement | Se déclenche lorsque |
|---|---|
scan.completed | Une tâche d'analyse passe à l'état completed - manuelle, planifiée ou lancée par l'API. La charge utile contient le site, le score, la répartition par gravité et les métadonnées de l'analyse. |
violation.new | Une non-conformité est détectée pour la première fois sur un site (aucune ligne ouverte ou résolue antérieure pour la même règle). Se déclenche une fois par non-conformité sur toute sa durée de vie : une nouvelle analyse qui redétecte une non-conformité déjà connue ne redéclenche PAS l'événement. Utile pour les intégrations de gestion de tickets qui souhaitent une ligne par défaut constaté. La charge utile contient violation_id, site_id, site_url, rule_id, impact et first_detected_at. |
violation.resolved | Une non-conformité jusque-là ouverte passe à l'état résolu. La charge utile contient resolution_source, avec l'une de ces trois valeurs : "scanner" (non détectée par une nouvelle analyse), "manual" (marquée comme résolue dans le tableau de bord ou via l'API REST), ou "jira" (le ticket Jira lié est passé dans la catégorie Terminé). Le garde-fou de transition évite une double émission lorsqu'une nouvelle mise à jour ne change rien. |
violation.regressed | Une non-conformité précédemment résolue réapparaît dans la dernière analyse. Se déclenche une fois par problème réapparu et par analyse. La charge utile contient la date resolved_at antérieure, pour que votre intégration puisse signaler les chronologies de « correction annulée ». |
Mise en place
- Ouvrez Réglages → Notifications (rôle administrateur ou propriétaire).
- Saisissez votre URL de webhook : le point d'accès HTTPS qui doit recevoir les événements. AllyProof refuse le HTTP simple ainsi que toute URL résolue vers une adresse IP d'une plage privée.
- Saisissez un secret de webhook : une chaîne aléatoire de longueur suffisante. C'est la clé utilisée pour signer chaque charge utile sortante. AllyProof ne la génère pas automatiquement ; si le champ est vide, l'envoi des webhooks est entièrement suspendu - la plateforme refuse d'émettre des charges utiles non signées.
- Enregistrez.
Chaque organisation dispose d'une URL de webhook et d'un secret. Si vous devez diffuser vers plusieurs destinations, dirigez l'URL vers un service de redistribution léger, de votre côté.
Format de la charge utile
Tous les événements envoient en POST un corps JSON de même structure de premier niveau :
{
"event": "scan.completed",
"timestamp": "2026-04-24T09:12:34.567Z",
"data": {
"site_id": "…",
"site_url": "https://example.com",
"scan_id": "…",
"score": 87,
"summary": {
"critical": 0,
"serious": 2,
"moderate": 5,
"minor": 10,
"pages_scanned": 25
}
}
}
L'objet data varie selon le type d'événement. Traitez les champs inconnus comme des extensions compatibles avec l'avenir, et non comme des ruptures : de nouveaux champs peuvent apparaître sans changement de version.
Vérification de la signature
Chaque requête comporte l'en-tête X-AllyProof-Signature : le HMAC-SHA256 du corps brut de la requête, avec votre secret de webhook comme clé, encodé en hexadécimal. Vérifiez toujours la signature avant d'agir sur la charge utile, et traitez une requête non vérifiable exactement comme si elle n'était jamais arrivée.
import crypto from "node:crypto";
function verify(req) {
const signature = req.headers["x-allyproof-signature"];
const expected = crypto
.createHmac("sha256", process.env.ALLYPROOF_WEBHOOK_SECRET)
.update(req.rawBody, "utf8")
.digest("hex");
// La comparaison a temps constant protege contre les attaques temporelles.
const ok =
signature.length === expected.length &&
crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
if (!ok) throw new Error("invalid signature");
}
Garanties d'acheminement
- Au mieux. AllyProof émet la requête HTTP une seule fois. Une réponse autre que 2xx est journalisée, mais aucune nouvelle tentative n'est effectuée automatiquement : si votre récepteur est indisponible, relisez les données de référence via l'API à son rétablissement.
- Non bloquant. Un webhook lent ou injoignable ne bloque jamais le traitement d'une analyse et n'affecte pas l'interface. L'envoi intervient après l'enregistrement de l'analyse.
- Signé uniquement. Si le secret n'est pas défini, l'envoi est refusé. La plateforme n'émet jamais de charge utile non signée.
Sécurité
- Les URL de webhook sont contrôlées au regard des plages d'adresses IP privées afin de prévenir les attaques par falsification de requête côté serveur ; les cibles internes (10/8, 172.16/12, 192.168/16, boucle locale) sont rejetées dès l'enregistrement.
- Le secret est conservé côté serveur et n'est jamais renvoyé par l'API une fois défini. Pour le faire tourner, remplacez-le dans les réglages : les anciens comme les nouveaux récepteurs devront utiliser la nouvelle valeur.
Cas d'usage courants
- Publier les résultats d'analyse dans un canal Slack via un relais de webhook.
- Ouvrir des tickets Jira ou Linear sur
violation.new. - Mettre à jour un tableau de bord de conformité interne sur
scan.completed. - Déclencher un processus de correction dans votre outil de gestion de projet.
- Alerter l'astreinte lorsqu'une non-conformité de gravité critique apparaît, en combinaison avec les données de gravité de fin d'analyse.