Zum Hauptinhalt springen

Webhooks

Die CaptainDNS Webhooks pushen die Ereignisse Ihres Profils an die von Ihnen konfigurierte HTTP-URL. Kein Polling mehr nötig: Jede Warnung und jede Policy-Änderung erreicht Ihren Endpoint innerhalb weniger Sekunden, mit einem signierten Payload, den Sie empfängerseitig verifizieren können.

Überblick

CaptainDNS sendet einen JSON-POST an die konfigurierte URL, sobald ein Ereignis in Ihrem Profil auftritt (Monitor down, fehlgeschlagenes MTA-STS-Deployment usw.). Wenn Sie beim Anlegen des Webhook-Kanals ein Secret definiert haben, wird die Anfrage mit HMAC-SHA256 signiert und mit Zeitstempel versehen, damit Sie Replays ablehnen können. Jeder Kanal ist einer oder mehreren Kategorien zugeordnet (monitoring, deployment, dns): Nur Ereignisse der aktivierten Kategorien werden an Ihren 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 Ihrem 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 Ihrem Plan ab:

PlanAktive Kanäle
Free1
Solo1
Starter3
Pro10
Business25
Enterprise100

Wird das Kontingent überschritten, gibt die Erstellung 402 webhook_quota_exceeded zurück. Um dieses Limit zu erhöhen, aktualisieren Sie Ihren Plan oder kontaktieren Sie 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://ihr-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 Ihren 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 Ihres 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.

Behandeln Sie den Payload tolerant: neue Felder können ohne Versions-Bump erscheinen. Ihr 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. Holen Sie den Timestamp aus dem Header X-CaptainDNS-Timestamp und den Rohinhalt der Anfrage (vor jedem JSON-Parsing).
  2. Verketten Sie <timestamp>.<roher_body>.
  3. Berechnen Sie den HMAC-SHA256 mit Ihrem Secret.
  4. Vergleichen Sie das Hex-Ergebnis mit dem Inhalt des Headers X-CaptainDNS-Signature (nach Entfernen des Präfixes sha256=), in konstanter Zeit.

Lehnen Sie 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 verwenden Sie express.raw({ type: "application/json" }) auf der Webhook-Route. Mit Next.js lesen Sie 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 holen Sie den Rohinhalt per await request.body(). Mit Flask verwenden Sie request.get_data(), ohne zuvor auf request.json zuzugreifen.

Liste der Ereignisse

Die 29 aktuell ausgelösten 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 (9)

Uptime, TLS-Zustand, ausgefallene Redirects, Zustellbarkeits-Monitoring, übergreifende Warnungen.

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 Warnung, die in keine dedizierte Kategorie fällt.
DELIVERABILITY_CHANGEDie Konfiguration einer überwachten Domain hat sich geändert: eine Säulenbewertung oder ein Eintrag.
DELIVERABILITY_DOMAIN_UNRESOLVABLEEine überwachte Domain löst auf DNS-Ebene überhaupt nicht mehr auf.

Kategorie deployment (18)

Aktivierung, Fehlschläge und Deaktivierung gehosteter Mail-Policies (MTA-STS, TLS-RPT, DMARC, BIMI), MTA-STS-Republish-Kampagne, Verlust legitimer Post, 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.
MTASTS_SERIAL_ROTATEDDas Serial einer gehosteten MTA-STS-Policy wurde rotiert: der TXT-Eintrag muss noch erneut veröffentlicht werden.
MTASTS_REPUBLISH_REMINDERErinnerung an die erneute Veröffentlichung des MTA-STS-TXT-Eintrags, 24 Stunden nach der Rotation.
MTASTS_REPUBLISH_FINALLetzte Warnung vor der Deaktivierung der MTA-STS-Policy, 48 Stunden nach der Rotation.
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.
TLSRPT_LOSING_MAILDie TLS-RPT-Reports zeigen blockierte Sessions: die Domain verliert legitime Post.
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.
DMARC_LOSING_MAILDie DMARC-Reports zeigen Post der Domain selbst, die das Alignment nicht besteht.
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.

Fehlercodes

Die von den CRUD-Endpunkten zurückgegebenen Fehler verwenden folgende Codes:

StatusCodeBedeutung
400invalid_categoriesDie übermittelte Kategorienliste ist leer oder enthält nicht unterstützte Werte.
402webhook_quota_exceededDas Profil hat das Limit aktiver Kanäle seines Plans erreicht.
422channel_not_webhookDer Zielkanal ist kein Webhook (Testversand nicht möglich).
422channel_disabledDer Kanal ist deaktiviert (Testversand nicht möglich).
429test_rate_limitedMehr als 5 Tests pro Minute und Profil.

Beim Anlegen eines Kanals kann zusätzlich ein 409 auftreten, wenn die Speicherschicht das Einfügen ablehnt (Kanallimit erreicht). Er enthält eine Klartextmeldung, keinen Maschinencode.

Best Practices auf Empfängerseite

  • Schnell antworten: senden Sie einen 2xx-Status in unter 5 Sekunden zurück. Bei längerer Verarbeitung bestätigen Sie sofort und delegieren Sie an eine asynchrone Queue.
  • Signatur prüfen, bevor Sie vertrauen: deserialisieren Sie den Payload niemals, bevor Sie X-CaptainDNS-Signature validiert haben, außer zum Lesen des Timestamps.
  • Unbekannte Felder tolerieren: Ihr 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: ignorieren Sie Typen, die Sie nicht verarbeiten können, statt zu scheitern.
  • Anwendungsseitige Idempotenz: dieselbe Benachrichtigung kann mehrfach eintreffen (Netzwerk-Retry, manuelles erneutes Senden aus dem Dashboard). Verwenden Sie 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 Sie nur eine präzise Teilmenge verarbeiten wollen (zum Beispiel ausschließlich MONITOR_DOWN), filtern Sie 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.