Webhooks
Webhooks senden Echtzeit-Ereignisse in Ihre eigenen Systeme, wenn Scans abgeschlossen sind oder sich der Verstoß-Status ändert. Jede Payload ist signiert, damit Ihr Empfänger verifizieren kann, dass sie von AllyProof stammt.
Unterstützte Ereignisse
| Ereignis | Ausgelöst, wenn |
|---|---|
scan.completed | Ein Scan-Job in den Status completed wechselt – manuell, geplant oder per API ausgelöst. Die Payload enthält die Website, den Score, die Schweregrad-Aufschlüsselung und Scan-Metadaten. |
violation.new | Ein Verstoß erstmalig auf einer Website erkannt wird (keine vorherige offene oder behobene Zeile für dieselbe Regel). Löst einmal pro Verstoß-Lebenszeit aus – ein erneuter Scan, der einen bereits bekannten Verstoß wieder erkennt, löst NICHT erneut aus. Nützlich für Ticketing-Integrationen, die eine Zeile pro Defekt wünschen. Die Payload enthält violation_id, site_id, site_url, rule_id, impact und first_detected_at. |
violation.resolved | Ein zuvor offener Verstoß in behoben übergeht. Die Payload enthält resolution_source mit einem von drei Werten: "scanner" (von einem frischen Scan nicht mehr erkannt), "manual" (ein Nutzer hat ihn im Dashboard oder über die REST-API als behoben markiert) oder "jira" (das verknüpfte Jira-Ticket ist in die Kategorie Erledigt gewechselt). Die Übergangsprüfung verhindert doppeltes Auslösen, wenn ein erneutes Update ein No-op ist. |
violation.regressed | Ein zuvor behobener Verstoß im letzten Scan wieder aufgetaucht ist. Löst einmal pro regressiertem Problem pro Scan aus. Die Payload enthält das vorherige resolved_at, damit Ihre Integration "Fix zurückgesetzt"-Zeitlinien markieren kann. |
Einrichtung
- Öffnen Sie Einstellungen → Benachrichtigungen (Admin- oder Owner-Rolle).
- Geben Sie Ihre Webhook-URL ein – den HTTPS-Endpunkt, der Ereignisse empfangen soll. AllyProof lehnt reines HTTP und jede URL ab, die zu einer privaten IP-Range auflöst.
- Geben Sie ein Webhook-Geheimnis ein – eine beliebige zufällige Zeichenkette ausreichender Länge. Dies ist der Schlüssel, mit dem jede ausgehende Payload signiert wird. AllyProof generiert es nicht automatisch; ist das Feld leer, wird die Webhook-Zustellung vollständig übersprungen (die Plattform verweigert das Senden unsignierter Payloads).
- Speichern.
Jede Organisation hat eine Webhook-URL und ein Geheimnis. Müssen Sie an mehrere Ziele verteilen, richten Sie Ihre URL auf einen schlanken Verteiler-Worker auf Ihrer Seite.
Payload-Format
Alle Ereignisse senden per POST einen JSON-Body mit derselben Struktur auf oberster Ebene:
{
"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
}
}
}
Das data-Objekt variiert je nach Ereignistyp. Behandeln Sie unbekannte Felder als abwärtskompatible Erweiterungen, nicht als Breaking Changes – neue Felder können ohne Versionssprung auftauchen.
Signaturverifikation
Jede Anfrage enthält den Header X-AllyProof-Signature: den HMAC-SHA256 des rohen Anfrage-Bodys, geschlüsselt mit Ihrem Webhook-Geheimnis, hex-codiert. Verifizieren Sie die Signatur immer, bevor Sie auf die Payload reagieren – behandeln Sie eine nicht verifizierbare Anfrage genau so, als wäre sie nie angekommen.
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");
// Zeitkonstanter Vergleich schützt vor Timing-Angriffen.
const ok =
signature.length === expected.length &&
crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
if (!ok) throw new Error("invalid signature");
}
Zustellgarantien
- Best-Effort. AllyProof löst die HTTP-Anfrage einmal aus. Eine Nicht-2xx-Antwort wird protokolliert, aber nicht automatisch wiederholt – ist Ihr Empfänger nicht erreichbar, verlassen Sie sich darauf, die kanonischen Daten über die API erneut zu lesen, sobald er wieder erreichbar ist.
- Nicht blockierend. Ein langsamer oder nicht erreichbarer Webhook blockiert nie die Scan-Verarbeitung und beeinflusst nie die kundenseitige Oberfläche. Die Zustellung läuft, nachdem der Scan-Datensatz gespeichert wurde.
- Nur signiert. Ist das Geheimnis nicht gesetzt, wird die Zustellung verweigert. Die Plattform sendet niemals unsignierte Payloads.
Sicherheit
- Webhook-URLs werden gegen private IP-Ranges validiert, um SSRF zu verhindern; interne Ziele (10/8, 172.16/12, 192.168/16, Loopback) werden beim Speichern abgelehnt.
- Das Geheimnis wird serverseitig gespeichert und nach dem Setzen nie über die API zurückgegeben. Rotieren Sie es, indem Sie es in den Einstellungen ersetzen – sowohl alte als auch neue Empfänger benötigen dann den neuen Wert.
Typische Anwendungsfälle
- Scan-Ergebnisse über ein Webhook-Relay in einen Slack-Kanal posten.
- Jira- oder Linear-Tickets bei
violation.newöffnen. - Ein internes Compliance-Dashboard bei
scan.completedaktualisieren. - Einen Behebungs-Workflow in Ihrem PM-Tool auslösen.
- On-Call alarmieren, wenn ein Verstoß mit kritischem Schweregrad auftaucht (kombiniert mit den Schweregrad-Daten von "Scan abgeschlossen").