Aller au contenu principal

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 :

PlanCanaux actifs
Free1
Starter3
Pro10
Business25
Enterprise100

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 : POST sur l'URL exacte configurée.
  • Content-Type : toujours application/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 format n/6 (nombre de tentatives maximum).
  • X-CaptainDNS-Event-Type : copie du champ event_type du body, pratique pour router sans parser le JSON.
  • X-CaptainDNS-Signature : présent si un secret est configuré. Format sha256=<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, 24h sur les statuts 5xx, 408, 429 et les erreurs réseau (timeout, DNS, TLS). Les autres 4xx (400, 401, 403, 404, 422, etc.) passent immédiatement en failed_permanent sans retry. Après 20 livraisons failed_permanent consé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 parmi monitoring, 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 :

  1. Récupérez le timestamp du header X-CaptainDNS-Timestamp et le corps brut de la requête (avant tout parsing JSON).
  2. Concaténez <timestamp>.<body_brut>.
  3. Calculez le HMAC-SHA256 avec votre secret.
  4. Comparez le résultat hex au contenu du header X-CaptainDNS-Signature (après avoir retiré le préfixe sha256=), 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_typeDescription
MONITOR_DOWNUn monitor passe en état DOWN après plusieurs échecs consécutifs.
MONITOR_RECOVERYUn monitor repasse UP après un DOWN.
MONITOR_DISABLE_WARNINGUn monitor en échec permanent approche de la désactivation automatique.
MONITOR_AUTO_DISABLEDUn monitor a été désactivé automatiquement après trop d'échecs prolongés.
TLS_EXPIRY_WARNINGUn certificat TLS observé approche de sa date d'expiration.
REDIRECT_DOWNUne redirection hébergée ne répond plus correctement.
GENERIC_ALERTAlerte 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_typeDescription
MTASTS_ACTIVATEDUne politique MTA-STS hébergée est passée en mode enforce.
MTASTS_DEPLOY_FAILUREUn déploiement MTA-STS a échoué (DNS, certificat ou policy).
MTASTS_DEACTIVATEDUne politique MTA-STS hébergée a été désactivée.
TLSRPT_ACTIVATEDUn enregistrement TLS-RPT hébergé est devenu actif.
TLSRPT_DEPLOY_FAILUREUn déploiement TLS-RPT a échoué.
TLSRPT_HIGH_FAILUREUn rapport TLS-RPT agrégé signale un taux d'échec élevé.
DMARC_ACTIVATEDUne politique DMARC hébergée est passée en quarantine ou reject.
DMARC_DEPLOY_FAILUREUn déploiement DMARC a échoué.
DMARC_ALIGNMENT_DROPLe taux d'alignement DMARC chute sous un seuil critique.
BIMI_CERT_EXPIRYUn certificat VMC ou CMC hébergé approche de son expiration.
REDIRECT_ACTIVATEDUne redirection hébergée est devenue active.
REDIRECT_DEACTIVATEDUne redirection hébergée a été désactivée.
DOMAIN_REVERIFY_FAILEDUne 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_typeDescription
RESOLVE_WATCH_DIFFUne resolve watch détecte un changement de réponse DNS.
RESOLVE_LATENCY_ANOMALYUne 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 2xx en 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_type comme 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_id comme clé de déduplication : il reste stable sur toutes les tentatives et tous les rejeux d'un même événement. delivery_id change à chaque tentative.
  • Logger event_id et delivery_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, deployment ou dns. Si vous ne voulez traiter qu'un sous-ensemble précis (par exemple uniquement MONITOR_DOWN), filtrez côté récepteur sur le champ event_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 publique cdns_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.