Webhooks
Les webhooks CaptainDNS poussent les événements de votre profil vers l'URL HTTP que vous configurez. Plus besoin de polling : chaque alerte et chaque changement de politique parvient à votre endpoint en quelques secondes, avec un payload signé que vous pouvez vérifier côté récepteur.
Vue d'ensemble
CaptainDNS émet un POST JSON vers l'URL configurée à chaque fois qu'un événement se produit sur votre profil (monitor down, échec de déploiement MTA-STS, etc.). Si vous avez défini un secret lors de la création du canal, la requête est signée en HMAC-SHA256 et horodatée pour vous permettre de rejeter les rejeux. Chaque canal est abonné à une ou plusieurs catégories (monitoring, deployment, dns) : seuls les événements des catégories cochées sont poussés vers votre endpoint.
En cas d'échec, la livraison est réessayée automatiquement jusqu'à 6 fois selon un backoff 10s, 1min, 10min, 1h, 6h, 24h. L'historique de chaque livraison est consultable dans le dashboard (onglet Deliveries) et les livraisons en échec permanent peuvent être rejouées manuellement.
Configuration
La création et la gestion des canaux webhook se font dans le dashboard CaptainDNS, section Notifications. Trois champs sont requis :
- URL : endpoint HTTPS accessible publiquement. Les URL HTTP sont refusées.
- Secret (optionnel mais recommandé) : chaîne partagée entre CaptainDNS et votre récepteur. Utilisée pour signer chaque requête. Sans secret, aucun header de signature n'est envoyé.
- Catégories : au moins une parmi
monitoring,deployment,dns. Le filtrage est appliqué côté serveur lors du dispatch.
Le nombre de canaux actifs par profil (webhooks et intégrations Slack combinés) dépend de votre plan :
| Plan | Canaux actifs |
|---|---|
| Free | 1 |
| Starter | 3 |
| Pro | 10 |
| Business | 25 |
| Enterprise | 100 |
Au-delà du quota, la création renvoie 402 webhook_quota_exceeded. Pour monter ce plafond, upgrader de plan ou contacter le support.
Les opérations CRUD passent par des routes REST dédiées (/v1/notifications/channels/*) protégées par la session Auth0 : elles sont consommées par le dashboard et ne sont pas ouvertes à la clé API publique.
Requête envoyée
Chaque événement produit une requête de la forme suivante :
POST https://votre-endpoint.captaindns.com/ HTTP/1.1
Content-Type: application/json
User-Agent: CaptainDNS-Webhook/2.0 (+https://www.captaindns.com/fr/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", ...}
Détails :
- Méthode :
POSTsur l'URL exacte configurée. Content-Type: toujoursapplication/json.User-Agent:CaptainDNS-Webhook/2.0 (+https://www.captaindns.com/fr/docs/api/webhooks). Utile pour filtrer le trafic dans vos logs.X-CaptainDNS-Event-ID: identifiant stable de l'événement métier. Conservé à l'identique sur toutes les tentatives et sur les rejeux manuels. Clé recommandée pour dédupliquer côté récepteur.X-CaptainDNS-Delivery-ID: identifiant unique de la tentative. Change à chaque retry et à chaque rejeu.X-CaptainDNS-Attempt: compteur au formatn/6(nombre de tentatives maximum).X-CaptainDNS-Event-Type: copie du champevent_typedu body, pratique pour router sans parser le JSON.X-CaptainDNS-Signature: présent si un secret est configuré. Formatsha256=<hex>.X-CaptainDNS-Timestamp: présent si un secret est configuré. Timestamp Unix en secondes, utilisé dans le calcul de la signature et pour se prémunir des rejeux.- Timeout dispatch : CaptainDNS attend au plus 10 secondes une réponse de votre endpoint. Au-delà, la tentative est considérée en échec.
- Retries : 6 tentatives au total (1 initiale + 5 retries), backoff
10s, 1min, 10min, 1h, 6h, 24hsur les statuts5xx,408,429et les erreurs réseau (timeout, DNS, TLS). Les autres4xx(400, 401, 403, 404, 422, etc.) passent immédiatement enfailed_permanentsans retry. Après 20 livraisonsfailed_permanentconsécutives, le canal est désactivé automatiquement et un email est envoyé au owner.
Format du payload
Le corps JSON suit le schéma V2 (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 du schéma de payload. Vaut"2"pour toutes les livraisons actuelles.event_id: UUID stable sur toutes les tentatives et les rejeux du même événement. Clé de déduplication recommandée.delivery_id: UUID unique par tentative. Utile pour corréler avec le dashboard Deliveries.attempt: numéro de la tentative (1 à 6).event_type: identifiant stable de l'événement, en SCREAMING_SNAKE_CASE. Voir la liste ci-dessous.category: catégorie webhook parmimonitoring,deployment,dns.timestamp: date d'émission au format RFC 3339 UTC.subject: libellé lisible par un humain, réutilisé comme sujet d'email pour le canal mail équivalent.status: état du dispatch côté CaptainDNS, typiquement"sent".data: objet libre dont la forme dépend de l'event_type. Peut être omis ou vide selon les événements.
Traitez le payload comme tolérant : de nouveaux champs peuvent apparaître sans bump de version. Votre parseur doit les ignorer silencieusement.
Vérification de la signature
Quand un secret est configuré, CaptainDNS signe la requête en HMAC-SHA256 pour permettre au récepteur de vérifier l'origine et l'intégrité du payload.
Algorithme :
- Récupérez le timestamp du header
X-CaptainDNS-Timestampet le corps brut de la requête (avant tout parsing JSON). - Concaténez
<timestamp>.<body_brut>. - Calculez le HMAC-SHA256 avec votre secret.
- Comparez le résultat hex au contenu du header
X-CaptainDNS-Signature(après avoir retiré le préfixesha256=), en temps constant.
Rejetez toute requête dont le timestamp s'écarte de plus de 5 minutes du temps courant, pour limiter les attaques par rejeu.
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"),
);
}
Important : rawBody doit être le buffer brut tel que reçu, avant JSON.parse. Avec Express, utilisez express.raw({ type: "application/json" }) sur la route du webhook. Avec Next.js, lisez le stream via 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)
Avec FastAPI, récupérez le corps brut via await request.body(). Avec Flask, utilisez request.get_data() sans accéder d'abord à request.json.
Liste des événements
Les 22 événements actuellement émis, groupés par catégorie webhook (monitoring, deployment, dns). Chaque événement appartient à une unique catégorie, utilisée pour le filtrage côté serveur en fonction des catégories cochées sur le canal.
Catégorie monitoring (7)
Uptime, santé TLS, redirections en panne, alertes transverses.
event_type | Description |
|---|---|
MONITOR_DOWN | Un monitor passe en état DOWN après plusieurs échecs consécutifs. |
MONITOR_RECOVERY | Un monitor repasse UP après un DOWN. |
MONITOR_DISABLE_WARNING | Un monitor en échec permanent approche de la désactivation automatique. |
MONITOR_AUTO_DISABLED | Un monitor a été désactivé automatiquement après trop d'échecs prolongés. |
TLS_EXPIRY_WARNING | Un certificat TLS observé approche de sa date d'expiration. |
REDIRECT_DOWN | Une redirection hébergée ne répond plus correctement. |
GENERIC_ALERT | Alerte transverse qui ne rentre dans aucune catégorie dédiée. |
Catégorie deployment (13)
Activation, échecs et désactivation des politiques mail hébergées (MTA-STS, TLS-RPT, DMARC, BIMI), redirections, vérification de domaine.
event_type | Description |
|---|---|
MTASTS_ACTIVATED | Une politique MTA-STS hébergée est passée en mode enforce. |
MTASTS_DEPLOY_FAILURE | Un déploiement MTA-STS a échoué (DNS, certificat ou policy). |
MTASTS_DEACTIVATED | Une politique MTA-STS hébergée a été désactivée. |
TLSRPT_ACTIVATED | Un enregistrement TLS-RPT hébergé est devenu actif. |
TLSRPT_DEPLOY_FAILURE | Un déploiement TLS-RPT a échoué. |
TLSRPT_HIGH_FAILURE | Un rapport TLS-RPT agrégé signale un taux d'échec élevé. |
DMARC_ACTIVATED | Une politique DMARC hébergée est passée en quarantine ou reject. |
DMARC_DEPLOY_FAILURE | Un déploiement DMARC a échoué. |
DMARC_ALIGNMENT_DROP | Le taux d'alignement DMARC chute sous un seuil critique. |
BIMI_CERT_EXPIRY | Un certificat VMC ou CMC hébergé approche de son expiration. |
REDIRECT_ACTIVATED | Une redirection hébergée est devenue active. |
REDIRECT_DEACTIVATED | Une redirection hébergée a été désactivée. |
DOMAIN_REVERIFY_FAILED | Une revérification périodique d'ownership de domaine a échoué. |
Catégorie dns (2)
Diffs DNS et anomalies de latence détectés par les resolve watches.
event_type | Description |
|---|---|
RESOLVE_WATCH_DIFF | Une resolve watch détecte un changement de réponse DNS. |
RESOLVE_LATENCY_ANOMALY | Une resolve watch détecte une anomalie de latence significative. |
Dashboard des livraisons
Chaque tentative est persistée dans la table webhook_deliveries et consultable dans le dashboard, sous-onglet Deliveries de la section Notifications. Colonnes disponibles : timestamp, canal, event_type, code HTTP retourné, compteur attempts sur 6, statut (pending, retrying, sent, failed_permanent).
Les livraisons failed_permanent (6 tentatives épuisées) peuvent être rejouées manuellement depuis le tableau. Un rejeu insère une nouvelle ligne avec le même event_id mais un nouveau delivery_id et réinitialise le compteur à 0 : jusqu'à 6 nouvelles tentatives sont ainsi planifiées.
L'historique est conservé pendant 90 jours, tous plans confondus, puis purgé par un worker quotidien. Une future version alignera la rétention sur la durée de conservation des logs du plan (log_retention_days).
Un bouton Send test sur chaque canal webhook actif envoie un payload factice event_type: "TEST" en catégorie monitoring. Rate-limité à 5 tests par minute et par profil.
Bonnes pratiques côté récepteur
- Répondre vite : renvoyez un statut
2xxen moins de 5 secondes. Si le traitement est long, acquittez immédiatement et délèguez à une queue asynchrone. - Vérifier la signature avant de faire confiance : ne désérialisez jamais le payload avant d'avoir validé
X-CaptainDNS-Signature, sauf pour lire le timestamp. - Tolérer les champs inconnus : votre parseur doit ignorer silencieusement les clés non connues, aussi bien au niveau racine que dans
data. Ceci garantit la compatibilité ascendante. - Traiter
event_typecomme une énumération ouverte : ignorez les types que vous ne savez pas gérer plutôt que d'échouer. - Idempotence applicative : la même notification peut arriver plusieurs fois (retry réseau, rejeu manuel depuis le dashboard). Utilisez
event_idcomme clé de déduplication : il reste stable sur toutes les tentatives et tous les rejeux d'un même événement.delivery_idchange à chaque tentative. - Logger
event_idetdelivery_id: utile pour corréler avec le sous-onglet Deliveries du dashboard lors d'un incident.
Limites actuelles et évolutions
- Filtrage à la catégorie, pas à l'
event_type: les canaux s'abonnent àmonitoring,deploymentoudns. Si vous ne voulez traiter qu'un sous-ensemble précis (par exemple uniquementMONITOR_DOWN), filtrez côté récepteur sur le champevent_type. - Endpoints CRUD dashboard-only : les routes
/v1/notifications/channels/*et/v1/notifications/deliveries/*sont protégées par la session Auth0 et ne sont pas accessibles avec une clé API publiquecdns_live_*/cdns_test_*. La gestion programmatique des canaux via clé API est sur la feuille de route. - Rétention fixe 90 jours : l'alignement sur
log_retention_days(plan) est prévu dans une itération ultérieure. - Signatures HMAC-SHA256 uniquement : le support Ed25519 et la rotation de secret avec grace period sont envisagés pour une version future.
Les évolutions seront annoncées dans le changelog de l'API publique.