Vai al contenuto principale

Webhook

I webhook CaptainDNS inviano gli eventi del tuo profilo all'URL HTTP che configuri. Niente più polling: ogni avviso e ogni cambio di policy arriva al tuo endpoint in pochi secondi, con un payload firmato che puoi verificare lato ricevente.

Panoramica

CaptainDNS emette un POST JSON verso l'URL configurata ogni volta che si verifica un evento sul tuo profilo (monitor down, fallimento di deploy MTA-STS, ecc.). Se hai impostato un secret durante la creazione del canale, la richiesta viene firmata in HMAC-SHA256 e marcata temporalmente per permetterti di rifiutare i replay. Ogni canale è iscritto a una o più categorie (monitoring, deployment, dns): solo gli eventi delle categorie selezionate vengono inviati al tuo endpoint.

In caso di fallimento, la consegna viene ritentata automaticamente fino a 6 volte secondo un backoff 10s, 1min, 10min, 1h, 6h, 24h. La cronologia di ogni consegna è consultabile nella dashboard (scheda Deliveries) e le consegne in fallimento permanente possono essere rieseguite manualmente.

Configurazione

La creazione e la gestione dei canali webhook avvengono nella dashboard CaptainDNS, sezione Notifications. Sono richiesti tre campi:

  • URL: endpoint HTTPS accessibile pubblicamente. Le URL HTTP vengono rifiutate.
  • Secret (opzionale ma consigliato): stringa condivisa tra CaptainDNS e il tuo ricevente. Usata per firmare ogni richiesta. Senza secret, nessun header di firma viene inviato.
  • Categorie: almeno una tra monitoring, deployment, dns. Il filtro viene applicato lato server durante il dispatch.

Il numero di canali attivi per profilo (webhook e integrazioni Slack combinati) dipende dal tuo piano:

PianoCanali attivi
Free1
Starter3
Pro10
Business25
Enterprise100

Oltre la quota, la creazione restituisce 402 webhook_quota_exceeded. Per aumentare questo limite, effettua un upgrade del piano o contatta il supporto.

Le operazioni CRUD passano tramite rotte REST dedicate (/v1/notifications/channels/*) protette dalla sessione Auth0: sono consumate dalla dashboard e non sono aperte alla chiave API pubblica.

Richiesta inviata

Ogni evento produce una richiesta nella forma seguente:

POST https://tuo-endpoint.captaindns.com/ HTTP/1.1
Content-Type: application/json
User-Agent: CaptainDNS-Webhook/2.0 (+https://www.captaindns.com/it/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", ...}

Dettagli:

  • Metodo: POST sull'URL esatta configurata.
  • Content-Type: sempre application/json.
  • User-Agent: CaptainDNS-Webhook/2.0 (+https://www.captaindns.com/it/docs/api/webhooks). Utile per filtrare il traffico nei tuoi log.
  • X-CaptainDNS-Event-ID: identificatore stabile dell'evento di business. Mantenuto identico su tutti i tentativi e sui rieseguimenti manuali. Chiave consigliata per deduplicare lato ricevente.
  • X-CaptainDNS-Delivery-ID: identificatore univoco del tentativo. Cambia a ogni retry e a ogni rieseguimento.
  • X-CaptainDNS-Attempt: contatore in formato n/6 (numero massimo di tentativi).
  • X-CaptainDNS-Event-Type: copia del campo event_type del body, comodo per fare routing senza parsare il JSON.
  • X-CaptainDNS-Signature: presente se è configurato un secret. Formato sha256=<hex>.
  • X-CaptainDNS-Timestamp: presente se è configurato un secret. Timestamp Unix in secondi, usato nel calcolo della firma e per proteggersi dai replay.
  • Timeout di dispatch: CaptainDNS attende al massimo 10 secondi una risposta dal tuo endpoint. Oltre, il tentativo è considerato fallito.
  • Tentativi: 6 in totale (1 iniziale + 5 retry), backoff 10s, 1min, 10min, 1h, 6h, 24h sugli stati 5xx, 408, 429 e sugli errori di rete (timeout, DNS, TLS). Gli altri 4xx (400, 401, 403, 404, 422, ecc.) passano immediatamente in failed_permanent senza retry. Dopo 20 consegne failed_permanent consecutive, il canale viene disattivato automaticamente e viene inviata una email al proprietario.

Formato del payload

Il corpo JSON segue lo schema 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: versione dello schema del payload. Vale "2" per tutte le consegne attuali.
  • event_id: UUID stabile su tutti i tentativi e i rieseguimenti dello stesso evento. Chiave di deduplicazione consigliata.
  • delivery_id: UUID univoco per tentativo. Utile per correlare con la dashboard Deliveries.
  • attempt: numero del tentativo (da 1 a 6).
  • event_type: identificatore stabile dell'evento, in SCREAMING_SNAKE_CASE. Vedi l'elenco qui sotto.
  • category: categoria webhook tra monitoring, deployment, dns.
  • timestamp: data di emissione in formato RFC 3339 UTC.
  • subject: etichetta leggibile da un umano, riutilizzata come oggetto email per il canale mail equivalente.
  • status: stato del dispatch lato CaptainDNS, tipicamente "sent".
  • data: oggetto libero la cui forma dipende dall'event_type. Può essere omesso o vuoto a seconda degli eventi.

Tratta il payload come tollerante: nuovi campi possono apparire senza bump di versione. Il tuo parser deve ignorarli silenziosamente.

Verifica della firma

Quando è configurato un secret, CaptainDNS firma la richiesta in HMAC-SHA256 per permettere al ricevente di verificare l'origine e l'integrità del payload.

Algoritmo:

  1. Recupera il timestamp dall'header X-CaptainDNS-Timestamp e il corpo grezzo della richiesta (prima di qualsiasi parsing JSON).
  2. Concatena <timestamp>.<body_grezzo>.
  3. Calcola l'HMAC-SHA256 con il tuo secret.
  4. Confronta il risultato hex con il contenuto dell'header X-CaptainDNS-Signature (dopo aver rimosso il prefisso sha256=), in tempo costante.

Rifiuta qualsiasi richiesta il cui timestamp si discosti di più di 5 minuti dall'ora corrente, per limitare gli attacchi di replay.

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"),
  );
}

Importante: rawBody deve essere il buffer grezzo così come ricevuto, prima di JSON.parse. Con Express, usa express.raw({ type: "application/json" }) sulla rotta del webhook. Con Next.js, leggi lo stream tramite 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)

Con FastAPI, recupera il corpo grezzo tramite await request.body(). Con Flask, usa request.get_data() senza accedere prima a request.json.

Elenco degli eventi

I 22 eventi attualmente emessi, raggruppati per categoria webhook (monitoring, deployment, dns). Ogni evento appartiene a un'unica categoria, usata per il filtro lato server in base alle categorie selezionate sul canale.

Categoria monitoring (7)

Uptime, salute TLS, redirezioni in panne, avvisi trasversali.

event_typeDescrizione
MONITOR_DOWNUn monitor passa in stato DOWN dopo diversi fallimenti consecutivi.
MONITOR_RECOVERYUn monitor torna UP dopo un DOWN.
MONITOR_DISABLE_WARNINGUn monitor in fallimento permanente si avvicina alla disattivazione automatica.
MONITOR_AUTO_DISABLEDUn monitor è stato disattivato automaticamente dopo troppi fallimenti prolungati.
TLS_EXPIRY_WARNINGUn certificato TLS osservato si avvicina alla data di scadenza.
REDIRECT_DOWNUna redirezione hosted non risponde più correttamente.
GENERIC_ALERTAvviso trasversale che non rientra in nessuna categoria dedicata.

Categoria deployment (13)

Attivazione, fallimenti e disattivazione delle policy mail hosted (MTA-STS, TLS-RPT, DMARC, BIMI), redirezioni, verifica del dominio.

event_typeDescrizione
MTASTS_ACTIVATEDUna policy MTA-STS hosted è passata in modalità enforce.
MTASTS_DEPLOY_FAILUREUn deploy MTA-STS è fallito (DNS, certificato o policy).
MTASTS_DEACTIVATEDUna policy MTA-STS hosted è stata disattivata.
TLSRPT_ACTIVATEDUn record TLS-RPT hosted è diventato attivo.
TLSRPT_DEPLOY_FAILUREUn deploy TLS-RPT è fallito.
TLSRPT_HIGH_FAILUREUn report TLS-RPT aggregato segnala un tasso di fallimento elevato.
DMARC_ACTIVATEDUna policy DMARC hosted è passata in quarantine o reject.
DMARC_DEPLOY_FAILUREUn deploy DMARC è fallito.
DMARC_ALIGNMENT_DROPIl tasso di allineamento DMARC scende sotto una soglia critica.
BIMI_CERT_EXPIRYUn certificato VMC o CMC hosted si avvicina alla scadenza.
REDIRECT_ACTIVATEDUna redirezione hosted è diventata attiva.
REDIRECT_DEACTIVATEDUna redirezione hosted è stata disattivata.
DOMAIN_REVERIFY_FAILEDUna riverifica periodica di ownership di dominio è fallita.

Categoria dns (2)

Diff DNS e anomalie di latenza rilevate dalle resolve watch.

event_typeDescrizione
RESOLVE_WATCH_DIFFUna resolve watch rileva un cambiamento nella risposta DNS.
RESOLVE_LATENCY_ANOMALYUna resolve watch rileva un'anomalia di latenza significativa.

Dashboard delle consegne

Ogni tentativo è persistito nella tabella webhook_deliveries e consultabile nella dashboard, sotto-scheda Deliveries della sezione Notifications. Colonne disponibili: timestamp, canale, event_type, codice HTTP restituito, contatore attempts su 6, stato (pending, retrying, sent, failed_permanent).

Le consegne failed_permanent (6 tentativi esauriti) possono essere rieseguite manualmente dalla tabella. Un rieseguimento inserisce una nuova riga con lo stesso event_id ma un nuovo delivery_id e azzera il contatore: fino a 6 nuovi tentativi vengono così pianificati.

La cronologia è conservata per 90 giorni, per tutti i piani, poi viene purgata da un worker giornaliero. Una versione futura allineerà la ritenzione alla durata di conservazione dei log del piano (log_retention_days).

Un pulsante Send test su ogni canale webhook attivo invia un payload fittizio event_type: "TEST" in categoria monitoring. Rate-limited a 5 test al minuto per profilo.

Buone pratiche lato ricevente

  • Rispondi velocemente: restituisci uno stato 2xx in meno di 5 secondi. Se l'elaborazione è lunga, fai un acknowledge immediato e delega a una coda asincrona.
  • Verifica la firma prima di fidarti: non deserializzare mai il payload prima di aver validato X-CaptainDNS-Signature, tranne per leggere il timestamp.
  • Tollera i campi sconosciuti: il tuo parser deve ignorare silenziosamente le chiavi non note, sia a livello root che dentro data. Questo garantisce la compatibilità ascendente.
  • Tratta event_type come un'enumerazione aperta: ignora i tipi che non sai gestire invece di fallire.
  • Idempotenza applicativa: la stessa notifica può arrivare più volte (retry di rete, rieseguimento manuale dalla dashboard). Usa event_id come chiave di deduplicazione: rimane stabile su tutti i tentativi e tutti i rieseguimenti di uno stesso evento. delivery_id cambia a ogni tentativo.
  • Logga event_id e delivery_id: utile per correlare con la sotto-scheda Deliveries della dashboard in caso di incidente.

Limiti attuali ed evoluzioni

  • Filtro per categoria, non per event_type: i canali si iscrivono a monitoring, deployment o dns. Se vuoi trattare solo un sottoinsieme preciso (ad esempio solo MONITOR_DOWN), filtra lato ricevente sul campo event_type.
  • Endpoint CRUD dashboard-only: le rotte /v1/notifications/channels/* e /v1/notifications/deliveries/* sono protette dalla sessione Auth0 e non sono accessibili con una chiave API pubblica cdns_live_* / cdns_test_*. La gestione programmatica dei canali tramite chiave API è nella roadmap.
  • Ritenzione fissa a 90 giorni: l'allineamento a log_retention_days (piano) è previsto in un'iterazione successiva.
  • Firme HMAC-SHA256 solo: il supporto Ed25519 e la rotazione del secret con grace period sono previsti per una versione futura.

Le evoluzioni saranno annunciate nel changelog dell'API pubblica.