Ir al contenido principal

Webhooks

Los webhooks de CaptainDNS envían los eventos de tu perfil a la URL HTTP que configures. Ya no necesitas hacer polling: cada alerta y cada cambio de política llega a tu endpoint en pocos segundos, con un payload firmado que puedes verificar en el lado del receptor.

Visión general

CaptainDNS emite un POST JSON a la URL configurada cada vez que ocurre un evento en tu perfil (monitor caído, fallo de despliegue MTA-STS, etc.). Si has definido un secreto al crear el canal, la petición se firma con HMAC-SHA256 y se marca con un timestamp para que puedas rechazar replays. Cada canal se suscribe a una o varias categorías (monitoring, deployment, dns): solo los eventos de las categorías marcadas se envían a tu endpoint.

Si una entrega falla, se reintenta automáticamente hasta 6 veces con un backoff 10s, 1min, 10min, 1h, 6h, 24h. El historial de cada entrega se consulta en el dashboard (pestaña Deliveries) y las entregas en fallo permanente pueden reenviarse manualmente.

Configuración

La creación y gestión de los canales webhook se realiza en el dashboard de CaptainDNS, sección Notificaciones. Hay tres campos obligatorios:

  • URL: endpoint HTTPS accesible públicamente. Las URL HTTP se rechazan.
  • Secreto (opcional pero recomendado): cadena compartida entre CaptainDNS y tu receptor. Se usa para firmar cada petición. Sin secreto, no se envía ningún header de firma.
  • Categorías: al menos una entre monitoring, deployment, dns. El filtrado se aplica en el servidor durante el dispatch.

El número de canales activos por perfil (webhooks e integraciones Slack combinados) depende de tu plan:

PlanCanales activos
Free1
Solo1
Starter3
Pro10
Business25
Enterprise100

Superada la cuota, la creación devuelve 402 webhook_quota_exceeded. Para ampliar este límite, cambia de plan o contacta con el soporte.

Las operaciones CRUD usan rutas REST dedicadas (/v1/notifications/channels/*) protegidas por la sesión de Auth0: las consume el dashboard y no están abiertas a la clave API pública.

Petición enviada

Cada evento produce una petición con el siguiente formato:

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

Detalles:

  • Método: POST sobre la URL exacta configurada.
  • Content-Type: siempre application/json.
  • User-Agent: CaptainDNS-Webhook/2.0 (+https://www.captaindns.com/es/docs/api/webhooks). Útil para filtrar el tráfico en tus logs.
  • X-CaptainDNS-Event-ID: identificador estable del evento de negocio. Se mantiene idéntico en todos los intentos y en los reenvíos manuales. Clave recomendada para deduplicar en el lado del receptor.
  • X-CaptainDNS-Delivery-ID: identificador único del intento. Cambia en cada reintento y en cada reenvío.
  • X-CaptainDNS-Attempt: contador con formato n/6 (número máximo de intentos).
  • X-CaptainDNS-Event-Type: copia del campo event_type del body, práctico para enrutar sin parsear el JSON.
  • X-CaptainDNS-Signature: presente si hay un secreto configurado. Formato sha256=<hex>.
  • X-CaptainDNS-Timestamp: presente si hay un secreto configurado. Timestamp Unix en segundos, usado en el cálculo de la firma y para protegerse contra replays.
  • Timeout de dispatch: CaptainDNS espera como máximo 10 segundos una respuesta de tu endpoint. Superado ese tiempo, el intento se considera fallido.
  • Reintentos: 6 intentos en total (1 inicial + 5 reintentos), backoff 10s, 1min, 10min, 1h, 6h, 24h para los estados 5xx, 408, 429 y los errores de red (timeout, DNS, TLS). Los demás 4xx (400, 401, 403, 404, 422, etc.) pasan inmediatamente a failed_permanent sin reintento. Tras 20 entregas failed_permanent consecutivas, el canal se desactiva automáticamente y se envía un email al owner.

Formato del payload

El cuerpo JSON sigue el esquema 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: versión del esquema de payload. Vale "2" para todas las entregas actuales.
  • event_id: UUID estable en todos los intentos y reenvíos del mismo evento. Clave de deduplicación recomendada.
  • delivery_id: UUID único por intento. Útil para correlacionar con el dashboard Deliveries.
  • attempt: número del intento (1 a 6).
  • event_type: identificador estable del evento, en SCREAMING_SNAKE_CASE. Consulta la lista a continuación.
  • category: categoría webhook entre monitoring, deployment, dns.
  • timestamp: fecha de emisión en formato RFC 3339 UTC.
  • subject: etiqueta legible, reutilizada como asunto de email para el canal mail equivalente.
  • status: estado del dispatch en CaptainDNS, normalmente "sent".
  • data: objeto libre cuya forma depende del event_type. Puede omitirse o estar vacío según el evento.

Trata el payload como tolerante: pueden aparecer nuevos campos sin un bump de versión. Tu parser debe ignorarlos silenciosamente.

Verificación de la firma

Cuando hay un secreto configurado, CaptainDNS firma la petición con HMAC-SHA256 para que el receptor pueda verificar el origen y la integridad del payload.

Algoritmo:

  1. Recupera el timestamp del header X-CaptainDNS-Timestamp y el cuerpo crudo de la petición (antes de cualquier parsing JSON).
  2. Concatena <timestamp>.<body_crudo>.
  3. Calcula el HMAC-SHA256 con tu secreto.
  4. Compara el resultado hex con el contenido del header X-CaptainDNS-Signature (después de quitar el prefijo sha256=), en tiempo constante.

Rechaza cualquier petición cuyo timestamp se desvíe más de 5 minutos del tiempo actual, para limitar los ataques por 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 debe ser el buffer crudo tal cual se recibe, antes de JSON.parse. Con Express, usa express.raw({ type: "application/json" }) en la ruta del webhook. Con Next.js, lee el stream con 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 el cuerpo crudo con await request.body(). Con Flask, usa request.get_data() sin acceder previamente a request.json.

Lista de eventos

Los 29 eventos emitidos actualmente, agrupados por categoría webhook (monitoring, deployment, dns). Cada evento pertenece a una única categoría, usada para el filtrado en el servidor según las categorías marcadas en el canal.

Categoría monitoring (9)

Uptime, salud TLS, redirecciones caídas, monitorización de entregabilidad, alertas transversales.

event_typeDescripción
MONITOR_DOWNUn monitor pasa al estado DOWN tras varios fallos consecutivos.
MONITOR_RECOVERYUn monitor vuelve a UP tras un DOWN.
MONITOR_DISABLE_WARNINGUn monitor en fallo permanente se acerca a la desactivación automática.
MONITOR_AUTO_DISABLEDUn monitor se ha desactivado automáticamente tras demasiados fallos prolongados.
TLS_EXPIRY_WARNINGUn certificado TLS observado se acerca a su fecha de expiración.
REDIRECT_DOWNUna redirección hospedada ya no responde correctamente.
GENERIC_ALERTAlerta transversal que no encaja en ninguna categoría dedicada.
DELIVERABILITY_CHANGELa configuración de un dominio vigilado ha cambiado: una puntuación de pilar o un registro.
DELIVERABILITY_DOMAIN_UNRESOLVABLEUn dominio vigilado ya no resuelve en absoluto a nivel DNS.

Categoría deployment (18)

Activación, fallos y desactivación de las políticas mail hospedadas (MTA-STS, TLS-RPT, DMARC, BIMI), campaña de republicación MTA-STS, pérdida de correo legítimo, redirecciones, verificación de dominio.

event_typeDescripción
MTASTS_ACTIVATEDUna política MTA-STS hospedada ha pasado a modo enforce.
MTASTS_DEPLOY_FAILUREUn despliegue MTA-STS ha fallado (DNS, certificado o policy).
MTASTS_DEACTIVATEDUna política MTA-STS hospedada se ha desactivado.
MTASTS_SERIAL_ROTATEDEl serial de una política MTA-STS hospedada ha rotado: falta republicar el registro TXT.
MTASTS_REPUBLISH_REMINDERRecordatorio para republicar el TXT MTA-STS, 24 horas después de la rotación del serial.
MTASTS_REPUBLISH_FINALÚltimo aviso antes de desactivar la política MTA-STS, 48 horas después de la rotación.
TLSRPT_ACTIVATEDUn registro TLS-RPT hospedado se ha activado.
TLSRPT_DEPLOY_FAILUREUn despliegue TLS-RPT ha fallado.
TLSRPT_HIGH_FAILUREUn informe TLS-RPT agregado señala una tasa de fallos elevada.
TLSRPT_LOSING_MAILLos informes TLS-RPT muestran sesiones bloqueadas: el dominio pierde correo legítimo.
DMARC_ACTIVATEDUna política DMARC hospedada ha pasado a quarantine o reject.
DMARC_DEPLOY_FAILUREUn despliegue DMARC ha fallado.
DMARC_ALIGNMENT_DROPLa tasa de alineación DMARC cae por debajo de un umbral crítico.
DMARC_LOSING_MAILLos informes DMARC muestran correo del propio dominio que falla la alineación.
BIMI_CERT_EXPIRYUn certificado VMC o CMC hospedado se acerca a su expiración.
REDIRECT_ACTIVATEDUna redirección hospedada se ha activado.
REDIRECT_DEACTIVATEDUna redirección hospedada se ha desactivado.
DOMAIN_REVERIFY_FAILEDUna reverificación periódica de propiedad de dominio ha fallado.

Categoría dns (2)

Diffs DNS y anomalías de latencia detectadas por las resolve watches.

event_typeDescripción
RESOLVE_WATCH_DIFFUna resolve watch detecta un cambio en la respuesta DNS.
RESOLVE_LATENCY_ANOMALYUna resolve watch detecta una anomalía de latencia significativa.

Panel de entregas

Cada intento se persiste en la tabla webhook_deliveries y se consulta en el dashboard, subpestaña Deliveries de la sección Notificaciones. Columnas disponibles: timestamp, canal, event_type, código HTTP devuelto, contador attempts sobre 6, estado (pending, retrying, sent, failed_permanent).

Las entregas failed_permanent (6 intentos agotados) pueden reenviarse manualmente desde la tabla. Un reenvío inserta una nueva fila con el mismo event_id pero un nuevo delivery_id y reinicia el contador a 0: se planifican así hasta 6 nuevos intentos.

El historial se conserva durante 90 días, sin distinción de plan, y luego lo depura un worker diario. Una versión futura alineará la retención con la duración de conservación de los logs del plan (log_retention_days).

Un botón Send test en cada canal webhook activo envía un payload ficticio event_type: "TEST" en la categoría monitoring. Limitado a 5 tests por minuto y por perfil.

Códigos de error

Los errores devueltos por los endpoints CRUD utilizan los siguientes códigos:

EstadoCódigoSignificado
400invalid_categoriesLa lista de categorías enviada está vacía o contiene valores no admitidos.
402webhook_quota_exceededEl perfil ha alcanzado el límite de canales activos de su plan.
422channel_not_webhookEl canal indicado no es un webhook (no se puede enviar un test).
422channel_disabledEl canal está desactivado (no se puede enviar un test).
429test_rate_limitedMás de 5 tests por minuto y por perfil.

También puede aparecer un 409 al crear un canal cuando la capa de almacenamiento rechaza la inserción (límite de canales alcanzado). Devuelve un mensaje en texto plano, no un código de máquina.

Buenas prácticas en el lado del receptor

  • Responde rápido: devuelve un estado 2xx en menos de 5 segundos. Si el procesamiento es largo, confirma inmediatamente y delega a una cola asíncrona.
  • Verifica la firma antes de confiar: nunca deserialices el payload sin haber validado primero X-CaptainDNS-Signature, salvo para leer el timestamp.
  • Tolera campos desconocidos: tu parser debe ignorar silenciosamente las claves no conocidas, tanto en la raíz como dentro de data. Esto garantiza la compatibilidad ascendente.
  • Trata event_type como una enumeración abierta: ignora los tipos que no sepas gestionar en lugar de fallar.
  • Idempotencia aplicativa: la misma notificación puede llegar varias veces (reintento de red, reenvío manual desde el dashboard). Usa event_id como clave de deduplicación: permanece estable en todos los intentos y en todos los reenvíos de un mismo evento. delivery_id cambia en cada intento.
  • Loguea event_id y delivery_id: útil para correlacionar con la subpestaña Deliveries del dashboard durante un incidente.

Limitaciones actuales y evoluciones

  • Filtrado por categoría, no por event_type: los canales se suscriben a monitoring, deployment o dns. Si solo quieres procesar un subconjunto concreto (por ejemplo solo MONITOR_DOWN), filtra en el receptor por el campo event_type.
  • Endpoints CRUD dashboard-only: las rutas /v1/notifications/channels/* y /v1/notifications/deliveries/* están protegidas por la sesión de Auth0 y no son accesibles con una clave API pública cdns_live_* / cdns_test_*. La gestión programática de los canales vía clave API está en el roadmap.
  • Retención fija de 90 días: la alineación con log_retention_days (plan) está prevista para una iteración posterior.
  • Firmas HMAC-SHA256 únicamente: el soporte de Ed25519 y la rotación de secreto con grace period se están estudiando para una versión futura.

Las novedades se anunciarán en el changelog de la API pública.