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:
| Piano | Canali attivi |
|---|---|
| Free | 1 |
| Starter | 3 |
| Pro | 10 |
| Business | 25 |
| Enterprise | 100 |
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:
POSTsull'URL esatta configurata. Content-Type: sempreapplication/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 formaton/6(numero massimo di tentativi).X-CaptainDNS-Event-Type: copia del campoevent_typedel body, comodo per fare routing senza parsare il JSON.X-CaptainDNS-Signature: presente se è configurato un secret. Formatosha256=<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, 24hsugli stati5xx,408,429e sugli errori di rete (timeout, DNS, TLS). Gli altri4xx(400, 401, 403, 404, 422, ecc.) passano immediatamente infailed_permanentsenza retry. Dopo 20 consegnefailed_permanentconsecutive, 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 tramonitoring,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:
- Recupera il timestamp dall'header
X-CaptainDNS-Timestampe il corpo grezzo della richiesta (prima di qualsiasi parsing JSON). - Concatena
<timestamp>.<body_grezzo>. - Calcola l'HMAC-SHA256 con il tuo secret.
- Confronta il risultato hex con il contenuto dell'header
X-CaptainDNS-Signature(dopo aver rimosso il prefissosha256=), 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_type | Descrizione |
|---|---|
MONITOR_DOWN | Un monitor passa in stato DOWN dopo diversi fallimenti consecutivi. |
MONITOR_RECOVERY | Un monitor torna UP dopo un DOWN. |
MONITOR_DISABLE_WARNING | Un monitor in fallimento permanente si avvicina alla disattivazione automatica. |
MONITOR_AUTO_DISABLED | Un monitor è stato disattivato automaticamente dopo troppi fallimenti prolungati. |
TLS_EXPIRY_WARNING | Un certificato TLS osservato si avvicina alla data di scadenza. |
REDIRECT_DOWN | Una redirezione hosted non risponde più correttamente. |
GENERIC_ALERT | Avviso 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_type | Descrizione |
|---|---|
MTASTS_ACTIVATED | Una policy MTA-STS hosted è passata in modalità enforce. |
MTASTS_DEPLOY_FAILURE | Un deploy MTA-STS è fallito (DNS, certificato o policy). |
MTASTS_DEACTIVATED | Una policy MTA-STS hosted è stata disattivata. |
TLSRPT_ACTIVATED | Un record TLS-RPT hosted è diventato attivo. |
TLSRPT_DEPLOY_FAILURE | Un deploy TLS-RPT è fallito. |
TLSRPT_HIGH_FAILURE | Un report TLS-RPT aggregato segnala un tasso di fallimento elevato. |
DMARC_ACTIVATED | Una policy DMARC hosted è passata in quarantine o reject. |
DMARC_DEPLOY_FAILURE | Un deploy DMARC è fallito. |
DMARC_ALIGNMENT_DROP | Il tasso di allineamento DMARC scende sotto una soglia critica. |
BIMI_CERT_EXPIRY | Un certificato VMC o CMC hosted si avvicina alla scadenza. |
REDIRECT_ACTIVATED | Una redirezione hosted è diventata attiva. |
REDIRECT_DEACTIVATED | Una redirezione hosted è stata disattivata. |
DOMAIN_REVERIFY_FAILED | Una riverifica periodica di ownership di dominio è fallita. |
Categoria dns (2)
Diff DNS e anomalie di latenza rilevate dalle resolve watch.
event_type | Descrizione |
|---|---|
RESOLVE_WATCH_DIFF | Una resolve watch rileva un cambiamento nella risposta DNS. |
RESOLVE_LATENCY_ANOMALY | Una 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
2xxin 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_typecome 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_idcome chiave di deduplicazione: rimane stabile su tutti i tentativi e tutti i rieseguimenti di uno stesso evento.delivery_idcambia a ogni tentativo. - Logga
event_idedelivery_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 amonitoring,deploymentodns. Se vuoi trattare solo un sottoinsieme preciso (ad esempio soloMONITOR_DOWN), filtra lato ricevente sul campoevent_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 pubblicacdns_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.