Webhooks
Die CaptainDNS Webhooks pushen die Ereignisse deines Profils an die von dir konfigurierte HTTP-URL. Kein Polling mehr nötig: Jede Alerte und jede Policy-Änderung erreicht deinen Endpoint innerhalb weniger Sekunden, mit einem signierten Payload, den du empfängerseitig verifizieren kannst.
Überblick
CaptainDNS sendet einen JSON-POST an die konfigurierte URL, sobald ein Ereignis in deinem Profil auftritt (Monitor down, fehlgeschlagenes MTA-STS-Deployment usw.). Wenn du beim Anlegen des Webhook-Kanals ein Secret definiert hast, wird die Anfrage mit HMAC-SHA256 signiert und mit Zeitstempel versehen, damit du Replays ablehnen kannst. Jeder Kanal ist einer oder mehreren Kategorien zugeordnet (monitoring, deployment, dns): Nur Ereignisse der aktivierten Kategorien werden an deinen Endpoint gepusht.
Bei einem Fehlschlag wird die Zustellung automatisch bis zu 6 Mal wiederholt, mit Backoff 10s, 1min, 10min, 1h, 6h, 24h. Die Historie jeder Zustellung ist im Dashboard (Reiter Deliveries) einsehbar, und dauerhaft fehlgeschlagene Zustellungen können manuell erneut gesendet werden.
Konfiguration
Die Erstellung und Verwaltung der Webhook-Kanäle erfolgt im CaptainDNS-Dashboard im Bereich Notifications. Drei Felder sind erforderlich:
- URL: öffentlich erreichbarer HTTPS-Endpoint. HTTP-URLs werden abgelehnt.
- Secret (optional, aber empfohlen): gemeinsamer String zwischen CaptainDNS und deinem Empfänger. Wird zum Signieren jeder Anfrage verwendet. Ohne Secret wird kein Signatur-Header gesendet.
- Kategorien: mindestens eine aus
monitoring,deployment,dns. Die Filterung wird serverseitig beim Dispatch angewendet.
Die Anzahl aktiver Kanäle pro Profil (Webhooks und Slack-Integrationen kombiniert) hängt von deinem Plan ab:
| Plan | Aktive Kanäle |
|---|---|
| Free | 1 |
| Starter | 3 |
| Pro | 10 |
| Business | 25 |
| Enterprise | 100 |
Wird das Kontingent überschritten, gibt die Erstellung 402 webhook_quota_exceeded zurück. Um dieses Limit zu erhöhen, aktualisiere deinen Plan oder kontaktiere den Support.
Die CRUD-Operationen laufen über dedizierte REST-Routen (/v1/notifications/channels/*), die durch die Auth0-Session geschützt sind: Sie werden vom Dashboard konsumiert und sind nicht über den öffentlichen API-Key zugänglich.
Gesendete Anfrage
Jedes Ereignis erzeugt eine Anfrage in folgender Form:
POST https://dein-endpoint.captaindns.com/ HTTP/1.1
Content-Type: application/json
User-Agent: CaptainDNS-Webhook/2.0 (+https://www.captaindns.com/de/docs/api/webhooks)
X-CaptainDNS-Event-ID: 7a3c9b1e-2f4d-4a6b-8c7d-9e1f2a3b4c5d
X-CaptainDNS-Delivery-ID: b8c1f4e2-5a6b-4c7d-8e9f-0a1b2c3d4e5f
X-CaptainDNS-Attempt: 1/6
X-CaptainDNS-Event-Type: MONITOR_DOWN
X-CaptainDNS-Signature: sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
X-CaptainDNS-Timestamp: 1776550245
{"schema_version":"2","event_type":"MONITOR_DOWN","category":"monitoring", ...}
Details:
- Methode:
POSTauf die exakt konfigurierte URL. Content-Type: immerapplication/json.User-Agent:CaptainDNS-Webhook/2.0 (+https://www.captaindns.com/de/docs/api/webhooks). Nützlich, um den Traffic in deinen Logs zu filtern.X-CaptainDNS-Event-ID: stabile Kennung des fachlichen Ereignisses. Bleibt bei allen Versuchen und bei manuellem erneutem Senden identisch. Empfohlener Schlüssel zur Deduplizierung auf Empfängerseite.X-CaptainDNS-Delivery-ID: eindeutige Kennung des Versuchs. Ändert sich bei jedem Retry und bei jedem erneuten Senden.X-CaptainDNS-Attempt: Zähler im Formatn/6(maximale Anzahl Versuche).X-CaptainDNS-Event-Type: Kopie des Feldesevent_typeaus dem Body, praktisch fürs Routing ohne JSON-Parsing.X-CaptainDNS-Signature: vorhanden, wenn ein Secret konfiguriert ist. Formatsha256=<hex>.X-CaptainDNS-Timestamp: vorhanden, wenn ein Secret konfiguriert ist. Unix-Timestamp in Sekunden, wird in der Signaturberechnung und zum Schutz vor Replays verwendet.- Dispatch-Timeout: CaptainDNS wartet maximal 10 Sekunden auf eine Antwort deines Endpoints. Danach gilt der Versuch als fehlgeschlagen.
- Retries: 6 Versuche insgesamt (1 initialer + 5 Wiederholungen), Backoff
10s, 1min, 10min, 1h, 6h, 24hbei Status5xx,408,429und Netzwerkfehlern (Timeout, DNS, TLS). Andere4xx-Fehler (400, 401, 403, 404, 422 usw.) gehen sofort infailed_permanentohne Retry. Nach 20 aufeinanderfolgenden Zustellungen mit Statusfailed_permanentwird der Kanal automatisch deaktiviert und eine E-Mail an den Owner gesendet.
Payload-Format
Der JSON-Body folgt dem V2-Schema (schema_version: "2"):
{
"schema_version": "2",
"event_id": "7a3c9b1e-2f4d-4a6b-8c7d-9e1f2a3b4c5d",
"delivery_id": "b8c1f4e2-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"attempt": 1,
"event_type": "MONITOR_DOWN",
"category": "monitoring",
"timestamp": "2026-04-14T10:30:45Z",
"subject": "[CaptainDNS] Monitor DOWN",
"status": "sent",
"data": {
"monitor_id": "mon_3f8a1c",
"target": "captaindns.com",
"failure_reason": "connection timeout"
}
}
schema_version: Version des Payload-Schemas. Hat den Wert"2"für alle aktuellen Zustellungen.event_id: UUID, stabil über alle Versuche und erneuten Sendungen desselben Ereignisses. Empfohlener Deduplizierungsschlüssel.delivery_id: UUID, eindeutig pro Versuch. Nützlich zur Korrelation mit dem Deliveries-Dashboard.attempt: Nummer des Versuchs (1 bis 6).event_type: stabile Kennung des Ereignisses, in SCREAMING_SNAKE_CASE. Siehe Liste unten.category: Webhook-Kategorie ausmonitoring,deployment,dns.timestamp: Ausstellungsdatum im Format RFC 3339 UTC.subject: menschenlesbares Label, wird als E-Mail-Betreff für den entsprechenden Mail-Kanal wiederverwendet.status: Dispatch-Status auf CaptainDNS-Seite, typischerweise"sent".data: freies Objekt, dessen Form vomevent_typeabhängt. Kann je nach Ereignis weggelassen oder leer sein.
Behandle den Payload tolerant: neue Felder können ohne Versions-Bump erscheinen. Dein Parser muss sie stillschweigend ignorieren.
Signaturverifikation
Wenn ein Secret konfiguriert ist, signiert CaptainDNS die Anfrage mit HMAC-SHA256, damit der Empfänger Herkunft und Integrität des Payloads prüfen kann.
Algorithmus:
- Hole den Timestamp aus dem Header
X-CaptainDNS-Timestampund den Rohinhalt der Anfrage (vor jedem JSON-Parsing). - Konkateniere
<timestamp>.<roher_body>. - Berechne den HMAC-SHA256 mit deinem Secret.
- Vergleiche das Hex-Ergebnis mit dem Inhalt des Headers
X-CaptainDNS-Signature(nach Entfernen des Präfixessha256=), in konstanter Zeit.
Lehne jede Anfrage ab, deren Timestamp mehr als 5 Minuten von der aktuellen Zeit abweicht, um Replay-Angriffe einzuschränken.
Node.js
import crypto from "node:crypto";
function verifyWebhook(rawBody, signatureHeader, timestamp, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
const received = signatureHeader.replace(/^sha256=/, "");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(received, "hex"),
);
}
Wichtig: rawBody muss der Roh-Buffer sein, wie er empfangen wurde, vor JSON.parse. Mit Express verwende express.raw({ type: "application/json" }) auf der Webhook-Route. Mit Next.js lies den Stream per await request.text().
Python
import hmac, hashlib
def verify_webhook(raw_body: bytes, signature_header: str, timestamp: str, secret: str) -> bool:
mac = hmac.new(secret.encode(), digestmod=hashlib.sha256)
mac.update(f"{timestamp}.".encode())
mac.update(raw_body)
expected = mac.hexdigest()
received = signature_header.removeprefix("sha256=")
return hmac.compare_digest(expected, received)
Mit FastAPI hole den Rohinhalt per await request.body(). Mit Flask verwende request.get_data(), ohne zuvor auf request.json zuzugreifen.
Liste der Ereignisse
Die 22 aktuell emittierten Ereignisse, gruppiert nach Webhook-Kategorie (monitoring, deployment, dns). Jedes Ereignis gehört zu genau einer Kategorie, die für die serverseitige Filterung anhand der auf dem Kanal aktivierten Kategorien verwendet wird.
Kategorie monitoring (7)
Uptime, TLS-Gesundheit, ausgefallene Redirects, übergreifende Alerten.
event_type | Beschreibung |
|---|---|
MONITOR_DOWN | Ein Monitor wechselt nach mehreren aufeinanderfolgenden Fehlschlägen in den Zustand DOWN. |
MONITOR_RECOVERY | Ein Monitor wechselt nach einem DOWN zurück auf UP. |
MONITOR_DISABLE_WARNING | Ein dauerhaft fehlschlagender Monitor nähert sich der automatischen Deaktivierung. |
MONITOR_AUTO_DISABLED | Ein Monitor wurde nach zu vielen anhaltenden Fehlschlägen automatisch deaktiviert. |
TLS_EXPIRY_WARNING | Ein beobachtetes TLS-Zertifikat nähert sich seinem Ablaufdatum. |
REDIRECT_DOWN | Ein gehosteter Redirect antwortet nicht mehr korrekt. |
GENERIC_ALERT | Übergreifende Alerte, die in keine dedizierte Kategorie fällt. |
Kategorie deployment (13)
Aktivierung, Fehlschläge und Deaktivierung gehosteter Mail-Policies (MTA-STS, TLS-RPT, DMARC, BIMI), Redirects und Domain-Verifikation.
event_type | Beschreibung |
|---|---|
MTASTS_ACTIVATED | Eine gehostete MTA-STS-Policy hat in den Modus enforce gewechselt. |
MTASTS_DEPLOY_FAILURE | Ein MTA-STS-Deployment ist fehlgeschlagen (DNS, Zertifikat oder Policy). |
MTASTS_DEACTIVATED | Eine gehostete MTA-STS-Policy wurde deaktiviert. |
TLSRPT_ACTIVATED | Ein gehosteter TLS-RPT-Eintrag ist aktiv geworden. |
TLSRPT_DEPLOY_FAILURE | Ein TLS-RPT-Deployment ist fehlgeschlagen. |
TLSRPT_HIGH_FAILURE | Ein aggregierter TLS-RPT-Report meldet eine hohe Fehlerrate. |
DMARC_ACTIVATED | Eine gehostete DMARC-Policy wurde auf quarantine oder reject gesetzt. |
DMARC_DEPLOY_FAILURE | Ein DMARC-Deployment ist fehlgeschlagen. |
DMARC_ALIGNMENT_DROP | Die DMARC-Alignment-Rate fällt unter einen kritischen Schwellenwert. |
BIMI_CERT_EXPIRY | Ein gehostetes VMC- oder CMC-Zertifikat nähert sich seinem Ablaufdatum. |
REDIRECT_ACTIVATED | Ein gehosteter Redirect ist aktiv geworden. |
REDIRECT_DEACTIVATED | Ein gehosteter Redirect wurde deaktiviert. |
DOMAIN_REVERIFY_FAILED | Eine periodische Domain-Ownership-Reverifizierung ist fehlgeschlagen. |
Kategorie dns (2)
DNS-Diffs und Latenzanomalien, erkannt durch die Resolve Watches.
event_type | Beschreibung |
|---|---|
RESOLVE_WATCH_DIFF | Eine Resolve Watch erkennt eine Änderung der DNS-Antwort. |
RESOLVE_LATENCY_ANOMALY | Eine Resolve Watch erkennt eine signifikante Latenzanomalie. |
Zustellungs-Dashboard
Jeder Versuch wird in der Tabelle webhook_deliveries gespeichert und ist im Dashboard unter dem Reiter Deliveries im Bereich Notifications einsehbar. Verfügbare Spalten: Timestamp, Kanal, event_type, zurückgelieferter HTTP-Code, Zähler attempts von 6, Status (pending, retrying, sent, failed_permanent).
Zustellungen mit Status failed_permanent (6 Versuche aufgebraucht) können manuell aus der Tabelle heraus erneut gesendet werden. Ein erneutes Senden fügt eine neue Zeile mit derselben event_id, aber einer neuen delivery_id ein und setzt den Zähler auf 0 zurück: Bis zu 6 neue Versuche werden so geplant.
Die Historie wird über alle Pläne hinweg 90 Tage lang aufbewahrt und dann von einem täglichen Worker bereinigt. Eine zukünftige Version wird die Aufbewahrungsdauer an die Log-Retention des Plans (log_retention_days) angleichen.
Ein Send test-Button auf jedem aktiven Webhook-Kanal sendet einen Test-Payload mit event_type: "TEST" in der Kategorie monitoring. Rate-limitiert auf 5 Tests pro Minute und Profil.
Best Practices auf Empfängerseite
- Schnell antworten: sende einen
2xx-Status in unter 5 Sekunden zurück. Bei längerer Verarbeitung bestätige sofort und delegiere an eine asynchrone Queue. - Signatur prüfen, bevor du vertraust: deserialisiere den Payload niemals, bevor du
X-CaptainDNS-Signaturevalidiert hast, außer zum Lesen des Timestamps. - Unbekannte Felder tolerieren: dein Parser muss unbekannte Keys stillschweigend ignorieren, sowohl auf Root-Ebene als auch in
data. Das garantiert Aufwärtskompatibilität. event_typeals offene Enumeration behandeln: ignoriere Typen, die du nicht verarbeiten kannst, statt zu scheitern.- Anwendungsseitige Idempotenz: dieselbe Benachrichtigung kann mehrfach eintreffen (Netzwerk-Retry, manuelles erneutes Senden aus dem Dashboard). Verwende
event_idals Deduplizierungsschlüssel: Er bleibt über alle Versuche und alle erneuten Sendungen desselben Ereignisses stabil.delivery_idändert sich bei jedem Versuch. event_idunddelivery_idloggen: nützlich zur Korrelation mit dem Deliveries-Reiter des Dashboards bei einem Vorfall.
Aktuelle Einschränkungen und Roadmap
- Filterung auf Kategorie-Ebene, nicht auf
event_type-Ebene: die Kanäle abonnierenmonitoring,deploymentoderdns. Wenn du nur eine präzise Teilmenge verarbeiten willst (zum Beispiel ausschließlichMONITOR_DOWN), filtere empfängerseitig auf dem Feldevent_type. - CRUD-Endpoints nur fürs Dashboard: die Routen
/v1/notifications/channels/*und/v1/notifications/deliveries/*sind durch die Auth0-Session geschützt und mit einem öffentlichen API-Keycdns_live_*/cdns_test_*nicht zugänglich. Die programmatische Verwaltung der Kanäle per API-Key steht auf der Roadmap. - Feste Aufbewahrung von 90 Tagen: die Angleichung an
log_retention_days(planabhängig) ist für eine spätere Iteration geplant. - Nur HMAC-SHA256-Signaturen: Unterstützung für Ed25519 und Secret-Rotation mit Übergangsphase sind für eine zukünftige Version vorgesehen.
Die Weiterentwicklungen werden im Changelog der Public API angekündigt.