Ir para o conteúdo principal

Webhooks

Os webhooks CaptainDNS enviam os eventos do seu perfil para a URL HTTP que você configurar. Sem necessidade de polling: cada alerta e cada mudança de política chega ao seu endpoint em segundos, com um payload assinado que você pode verificar do lado do receptor.

Visão geral

A CaptainDNS envia um POST JSON para a URL configurada sempre que um evento ocorre no seu perfil (monitor down, falha de deploy MTA-STS, etc.). Se você definiu um segredo na criação do canal, a requisição é assinada em HMAC-SHA256 e carimbada com timestamp, permitindo rejeitar replays. Cada canal é inscrito em uma ou mais categorias (monitoring, deployment, dns): apenas os eventos das categorias marcadas são enviados ao seu endpoint.

Em caso de falha, a entrega é tentada novamente de forma automática até 6 vezes, com backoff 10s, 1min, 10min, 1h, 6h, 24h. O histórico de cada entrega pode ser consultado no painel (aba Deliveries) e as entregas em falha permanente podem ser reenviadas manualmente.

Configuração

A criação e a gestão dos canais webhook são feitas no painel CaptainDNS, na seção Notifications. Três campos são obrigatórios:

  • URL: endpoint HTTPS acessível publicamente. URLs HTTP são recusadas.
  • Secret (opcional, mas recomendado): string compartilhada entre a CaptainDNS e o seu receptor. Usada para assinar cada requisição. Sem segredo, nenhum header de assinatura é enviado.
  • Categorias: pelo menos uma entre monitoring, deployment, dns. A filtragem é aplicada do lado do servidor no momento do dispatch.

O número de canais ativos por perfil (webhooks e integrações Slack combinados) depende do seu plano:

PlanoCanais ativos
Free1
Starter3
Pro10
Business25
Enterprise100

Ao ultrapassar a cota, a criação retorna 402 webhook_quota_exceeded. Para elevar esse teto, faça upgrade de plano ou entre em contato com o suporte.

As operações CRUD passam por rotas REST dedicadas (/v1/notifications/channels/*) protegidas pela sessão Auth0: elas são consumidas pelo painel e não estão abertas à chave API pública.

Requisição enviada

Cada evento gera uma requisição no seguinte formato:

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

Detalhes:

  • Método: POST na URL exata configurada.
  • Content-Type: sempre application/json.
  • User-Agent: CaptainDNS-Webhook/2.0 (+https://www.captaindns.com/pt/docs/api/webhooks). Útil para filtrar o tráfego nos seus logs.
  • X-CaptainDNS-Event-ID: identificador estável do evento de negócio. Mantido idêntico em todas as tentativas e nos reenvios manuais. Chave recomendada para deduplicar do lado do receptor.
  • X-CaptainDNS-Delivery-ID: identificador único da tentativa. Muda a cada retry e a cada reenvio.
  • X-CaptainDNS-Attempt: contador no formato n/6 (número máximo de tentativas).
  • X-CaptainDNS-Event-Type: cópia do campo event_type do body, prático para rotear sem parsear o JSON.
  • X-CaptainDNS-Signature: presente se um segredo estiver configurado. Formato sha256=<hex>.
  • X-CaptainDNS-Timestamp: presente se um segredo estiver configurado. Timestamp Unix em segundos, usado no cálculo da assinatura e para proteção contra replays.
  • Timeout de dispatch: a CaptainDNS aguarda no máximo 10 segundos por uma resposta do seu endpoint. Após esse prazo, a tentativa é considerada falha.
  • Retries: 6 tentativas no total (1 inicial + 5 retries), backoff 10s, 1min, 10min, 1h, 6h, 24h sobre os status 5xx, 408, 429 e erros de rede (timeout, DNS, TLS). Os demais 4xx (400, 401, 403, 404, 422, etc.) passam imediatamente para failed_permanent sem retry. Após 20 entregas failed_permanent consecutivas, o canal é desativado automaticamente e um email é enviado ao dono.

Formato do payload

O corpo JSON segue o 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: versão do schema de payload. Vale "2" para todas as entregas atuais.
  • event_id: UUID estável em todas as tentativas e reenvios do mesmo evento. Chave de deduplicação recomendada.
  • delivery_id: UUID único por tentativa. Útil para correlacionar com o painel Deliveries.
  • attempt: número da tentativa (1 a 6).
  • event_type: identificador estável do evento, em SCREAMING_SNAKE_CASE. Veja a lista abaixo.
  • category: categoria webhook entre monitoring, deployment, dns.
  • timestamp: data de emissão no formato RFC 3339 UTC.
  • subject: texto legível por humanos, reutilizado como assunto do email para o canal de mail equivalente.
  • status: estado do envio do lado da CaptainDNS, tipicamente "sent".
  • data: objeto livre cujo formato depende do event_type. Pode ser omitido ou ficar vazio dependendo do evento.

Trate o payload como tolerante: novos campos podem aparecer sem incremento de versão. Seu parser deve ignorá-los silenciosamente.

Verificação da assinatura

Quando um segredo está configurado, a CaptainDNS assina a requisição em HMAC-SHA256 para permitir que o receptor verifique a origem e a integridade do payload.

Algoritmo:

  1. Pegue o timestamp do header X-CaptainDNS-Timestamp e o corpo bruto da requisição (antes de qualquer parsing JSON).
  2. Concatene <timestamp>.<body_bruto>.
  3. Calcule o HMAC-SHA256 com o seu segredo.
  4. Compare o resultado em hex com o conteúdo do header X-CaptainDNS-Signature (depois de remover o prefixo sha256=), em tempo constante.

Rejeite qualquer requisição cujo timestamp se desvie mais de 5 minutos do tempo atual, para limitar ataques de 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 ser o buffer bruto tal como recebido, antes do JSON.parse. Com Express, use express.raw({ type: "application/json" }) na rota do webhook. Com Next.js, leia o 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)

Com FastAPI, obtenha o corpo bruto via await request.body(). Com Flask, use request.get_data() sem acessar antes request.json.

Lista de eventos

Os 22 eventos atualmente emitidos, agrupados por categoria webhook (monitoring, deployment, dns). Cada evento pertence a uma única categoria, usada para a filtragem do lado do servidor em função das categorias marcadas no canal.

Categoria monitoring (7)

Uptime, saúde TLS, redirecionamentos em falha, alertas transversais.

event_typeDescrição
MONITOR_DOWNUm monitor entra em estado DOWN após várias falhas consecutivas.
MONITOR_RECOVERYUm monitor volta para UP após um DOWN.
MONITOR_DISABLE_WARNINGUm monitor em falha permanente está próximo da desativação automática.
MONITOR_AUTO_DISABLEDUm monitor foi desativado automaticamente após muitas falhas prolongadas.
TLS_EXPIRY_WARNINGUm certificado TLS observado está próximo da data de expiração.
REDIRECT_DOWNUm redirecionamento hospedado não responde mais corretamente.
GENERIC_ALERTAlerta transversal que não se encaixa em nenhuma categoria específica.

Categoria deployment (13)

Ativação, falhas e desativação das políticas de email hospedadas (MTA-STS, TLS-RPT, DMARC, BIMI), redirecionamentos, verificação de domínio.

event_typeDescrição
MTASTS_ACTIVATEDUma política MTA-STS hospedada passou para o modo enforce.
MTASTS_DEPLOY_FAILUREUm deploy MTA-STS falhou (DNS, certificado ou policy).
MTASTS_DEACTIVATEDUma política MTA-STS hospedada foi desativada.
TLSRPT_ACTIVATEDUm registro TLS-RPT hospedado ficou ativo.
TLSRPT_DEPLOY_FAILUREUm deploy TLS-RPT falhou.
TLSRPT_HIGH_FAILUREUm relatório TLS-RPT agregado indica uma taxa de falha elevada.
DMARC_ACTIVATEDUma política DMARC hospedada passou para quarantine ou reject.
DMARC_DEPLOY_FAILUREUm deploy DMARC falhou.
DMARC_ALIGNMENT_DROPA taxa de alinhamento DMARC cai abaixo de um limite crítico.
BIMI_CERT_EXPIRYUm certificado VMC ou CMC hospedado está próximo da expiração.
REDIRECT_ACTIVATEDUm redirecionamento hospedado ficou ativo.
REDIRECT_DEACTIVATEDUm redirecionamento hospedado foi desativado.
DOMAIN_REVERIFY_FAILEDUma reverificação periódica de ownership de domínio falhou.

Categoria dns (2)

Diffs DNS e anomalias de latência detectados pelas resolve watches.

event_typeDescrição
RESOLVE_WATCH_DIFFUma resolve watch detecta uma mudança de resposta DNS.
RESOLVE_LATENCY_ANOMALYUma resolve watch detecta uma anomalia de latência significativa.

Painel de entregas

Cada tentativa é persistida na tabela webhook_deliveries e pode ser consultada no painel, na aba Deliveries da seção Notifications. Colunas disponíveis: timestamp, canal, event_type, código HTTP retornado, contador attempts sobre 6, status (pending, retrying, sent, failed_permanent).

As entregas failed_permanent (6 tentativas esgotadas) podem ser reenviadas manualmente a partir da tabela. Um reenvio insere uma nova linha com o mesmo event_id, mas com um novo delivery_id, e reinicia o contador para 0: até 6 novas tentativas são então planejadas.

O histórico é mantido por 90 dias, em todos os planos, e depois purgado por um worker diário. Uma versão futura vai alinhar a retenção sobre a duração de conservação dos logs do plano (log_retention_days).

Um botão Send test em cada canal webhook ativo envia um payload fictício event_type: "TEST" na categoria monitoring. Rate-limitado a 5 testes por minuto e por perfil.

Boas práticas do lado do receptor

  • Responda rápido: devolva um status 2xx em menos de 5 segundos. Se o processamento for demorado, confirme o recebimento imediatamente e delegue para uma fila assíncrona.
  • Verifique a assinatura antes de confiar: nunca desserialize o payload antes de ter validado o X-CaptainDNS-Signature, exceto para ler o timestamp.
  • Tolere campos desconhecidos: seu parser deve ignorar silenciosamente chaves desconhecidas, tanto no nível raiz quanto dentro de data. Isso garante a compatibilidade retroativa.
  • Trate event_type como uma enumeração aberta: ignore os tipos que você não sabe tratar em vez de falhar.
  • Idempotência aplicativa: a mesma notificação pode chegar várias vezes (retry de rede, reenvio manual a partir do painel). Use event_id como chave de deduplicação: ele permanece estável em todas as tentativas e em todos os reenvios de um mesmo evento. delivery_id muda a cada tentativa.
  • Logue event_id e delivery_id: útil para correlacionar com a aba Deliveries do painel em caso de incidente.

Limitações atuais e evoluções

  • Filtragem por categoria, não por event_type: os canais são inscritos em monitoring, deployment ou dns. Se você quiser tratar apenas um subconjunto específico (por exemplo, somente MONITOR_DOWN), filtre do lado do receptor sobre o campo event_type.
  • Endpoints CRUD dashboard-only: as rotas /v1/notifications/channels/* e /v1/notifications/deliveries/* são protegidas pela sessão Auth0 e não são acessíveis com uma chave API pública cdns_live_* / cdns_test_*. A gestão programática dos canais via chave API está no roadmap.
  • Retenção fixa de 90 dias: o alinhamento sobre log_retention_days (plano) está previsto em uma iteração futura.
  • Assinaturas apenas em HMAC-SHA256: o suporte a Ed25519 e a rotação de segredo com grace period estão sendo considerados para uma versão futura.

As evoluções serão anunciadas no changelog da API pública.