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:
| Plan | Aktive Kanäle |
|---|---|
| Free | 1 |
| Solo | 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, 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:
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 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 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 Ihres 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.
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:
- Holen Sie den Timestamp aus dem Header
X-CaptainDNS-Timestampund den Rohinhalt der Anfrage (vor jedem JSON-Parsing). - Verketten Sie
<timestamp>.<roher_body>. - Berechnen Sie den HMAC-SHA256 mit Ihrem Secret.
- Vergleichen Sie das Hex-Ergebnis mit dem Inhalt des Headers
X-CaptainDNS-Signature(nach Entfernen des Präfixessha256=), 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_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 Warnung, die in keine dedizierte Kategorie fällt. |
DELIVERABILITY_CHANGE | Die Konfiguration einer überwachten Domain hat sich geändert: eine Säulenbewertung oder ein Eintrag. |
DELIVERABILITY_DOMAIN_UNRESOLVABLE | Eine ü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_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. |
MTASTS_SERIAL_ROTATED | Das Serial einer gehosteten MTA-STS-Policy wurde rotiert: der TXT-Eintrag muss noch erneut veröffentlicht werden. |
MTASTS_REPUBLISH_REMINDER | Erinnerung an die erneute Veröffentlichung des MTA-STS-TXT-Eintrags, 24 Stunden nach der Rotation. |
MTASTS_REPUBLISH_FINAL | Letzte Warnung vor der Deaktivierung der MTA-STS-Policy, 48 Stunden nach der Rotation. |
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. |
TLSRPT_LOSING_MAIL | Die TLS-RPT-Reports zeigen blockierte Sessions: die Domain verliert legitime Post. |
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. |
DMARC_LOSING_MAIL | Die DMARC-Reports zeigen Post der Domain selbst, die das Alignment nicht besteht. |
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.
Fehlercodes
Die von den CRUD-Endpunkten zurückgegebenen Fehler verwenden folgende Codes:
| Status | Code | Bedeutung |
|---|---|---|
400 | invalid_categories | Die übermittelte Kategorienliste ist leer oder enthält nicht unterstützte Werte. |
402 | webhook_quota_exceeded | Das Profil hat das Limit aktiver Kanäle seines Plans erreicht. |
422 | channel_not_webhook | Der Zielkanal ist kein Webhook (Testversand nicht möglich). |
422 | channel_disabled | Der Kanal ist deaktiviert (Testversand nicht möglich). |
429 | test_rate_limited | Mehr 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-Signaturevalidiert 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_typeals 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_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 Sie nur eine präzise Teilmenge verarbeiten wollen (zum Beispiel ausschließlichMONITOR_DOWN), filtern Sie 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.