Zum Hauptinhalt springen

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:

PlanAktive Kanäle
Free1
Starter3
Pro10
Business25
Enterprise100

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: POST auf die exakt konfigurierte URL.
  • Content-Type: immer application/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 Format n/6 (maximale Anzahl Versuche).
  • X-CaptainDNS-Event-Type: Kopie des Feldes event_type aus dem Body, praktisch fürs Routing ohne JSON-Parsing.
  • X-CaptainDNS-Signature: vorhanden, wenn ein Secret konfiguriert ist. Format sha256=<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, 24h bei Status 5xx, 408, 429 und Netzwerkfehlern (Timeout, DNS, TLS). Andere 4xx-Fehler (400, 401, 403, 404, 422 usw.) gehen sofort in failed_permanent ohne Retry. Nach 20 aufeinanderfolgenden Zustellungen mit Status failed_permanent wird 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 aus monitoring, 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 vom event_type abhä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:

  1. Hole den Timestamp aus dem Header X-CaptainDNS-Timestamp und den Rohinhalt der Anfrage (vor jedem JSON-Parsing).
  2. Konkateniere <timestamp>.<roher_body>.
  3. Berechne den HMAC-SHA256 mit deinem Secret.
  4. Vergleiche das Hex-Ergebnis mit dem Inhalt des Headers X-CaptainDNS-Signature (nach Entfernen des Präfixes sha256=), 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_typeBeschreibung
MONITOR_DOWNEin Monitor wechselt nach mehreren aufeinanderfolgenden Fehlschlägen in den Zustand DOWN.
MONITOR_RECOVERYEin Monitor wechselt nach einem DOWN zurück auf UP.
MONITOR_DISABLE_WARNINGEin dauerhaft fehlschlagender Monitor nähert sich der automatischen Deaktivierung.
MONITOR_AUTO_DISABLEDEin Monitor wurde nach zu vielen anhaltenden Fehlschlägen automatisch deaktiviert.
TLS_EXPIRY_WARNINGEin beobachtetes TLS-Zertifikat nähert sich seinem Ablaufdatum.
REDIRECT_DOWNEin 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_typeBeschreibung
MTASTS_ACTIVATEDEine gehostete MTA-STS-Policy hat in den Modus enforce gewechselt.
MTASTS_DEPLOY_FAILUREEin MTA-STS-Deployment ist fehlgeschlagen (DNS, Zertifikat oder Policy).
MTASTS_DEACTIVATEDEine gehostete MTA-STS-Policy wurde deaktiviert.
TLSRPT_ACTIVATEDEin gehosteter TLS-RPT-Eintrag ist aktiv geworden.
TLSRPT_DEPLOY_FAILUREEin TLS-RPT-Deployment ist fehlgeschlagen.
TLSRPT_HIGH_FAILUREEin aggregierter TLS-RPT-Report meldet eine hohe Fehlerrate.
DMARC_ACTIVATEDEine gehostete DMARC-Policy wurde auf quarantine oder reject gesetzt.
DMARC_DEPLOY_FAILUREEin DMARC-Deployment ist fehlgeschlagen.
DMARC_ALIGNMENT_DROPDie DMARC-Alignment-Rate fällt unter einen kritischen Schwellenwert.
BIMI_CERT_EXPIRYEin gehostetes VMC- oder CMC-Zertifikat nähert sich seinem Ablaufdatum.
REDIRECT_ACTIVATEDEin gehosteter Redirect ist aktiv geworden.
REDIRECT_DEACTIVATEDEin gehosteter Redirect wurde deaktiviert.
DOMAIN_REVERIFY_FAILEDEine periodische Domain-Ownership-Reverifizierung ist fehlgeschlagen.

Kategorie dns (2)

DNS-Diffs und Latenzanomalien, erkannt durch die Resolve Watches.

event_typeBeschreibung
RESOLVE_WATCH_DIFFEine Resolve Watch erkennt eine Änderung der DNS-Antwort.
RESOLVE_LATENCY_ANOMALYEine 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-Signature validiert 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_type als 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_id als Deduplizierungsschlüssel: Er bleibt über alle Versuche und alle erneuten Sendungen desselben Ereignisses stabil. delivery_id ändert sich bei jedem Versuch.
  • event_id und delivery_id loggen: 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 abonnieren monitoring, deployment oder dns. Wenn du nur eine präzise Teilmenge verarbeiten willst (zum Beispiel ausschließlich MONITOR_DOWN), filtere empfängerseitig auf dem Feld event_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-Key cdns_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.