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:
| Plano | Canais ativos |
|---|---|
| Free | 1 |
| Starter | 3 |
| Pro | 10 |
| Business | 25 |
| Enterprise | 100 |
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:
POSTna URL exata configurada. Content-Type: sempreapplication/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 formaton/6(número máximo de tentativas).X-CaptainDNS-Event-Type: cópia do campoevent_typedo body, prático para rotear sem parsear o JSON.X-CaptainDNS-Signature: presente se um segredo estiver configurado. Formatosha256=<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, 24hsobre os status5xx,408,429e erros de rede (timeout, DNS, TLS). Os demais4xx(400, 401, 403, 404, 422, etc.) passam imediatamente parafailed_permanentsem retry. Após 20 entregasfailed_permanentconsecutivas, 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 entremonitoring,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 doevent_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:
- Pegue o timestamp do header
X-CaptainDNS-Timestampe o corpo bruto da requisição (antes de qualquer parsing JSON). - Concatene
<timestamp>.<body_bruto>. - Calcule o HMAC-SHA256 com o seu segredo.
- Compare o resultado em hex com o conteúdo do header
X-CaptainDNS-Signature(depois de remover o prefixosha256=), 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_type | Descrição |
|---|---|
MONITOR_DOWN | Um monitor entra em estado DOWN após várias falhas consecutivas. |
MONITOR_RECOVERY | Um monitor volta para UP após um DOWN. |
MONITOR_DISABLE_WARNING | Um monitor em falha permanente está próximo da desativação automática. |
MONITOR_AUTO_DISABLED | Um monitor foi desativado automaticamente após muitas falhas prolongadas. |
TLS_EXPIRY_WARNING | Um certificado TLS observado está próximo da data de expiração. |
REDIRECT_DOWN | Um redirecionamento hospedado não responde mais corretamente. |
GENERIC_ALERT | Alerta 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_type | Descrição |
|---|---|
MTASTS_ACTIVATED | Uma política MTA-STS hospedada passou para o modo enforce. |
MTASTS_DEPLOY_FAILURE | Um deploy MTA-STS falhou (DNS, certificado ou policy). |
MTASTS_DEACTIVATED | Uma política MTA-STS hospedada foi desativada. |
TLSRPT_ACTIVATED | Um registro TLS-RPT hospedado ficou ativo. |
TLSRPT_DEPLOY_FAILURE | Um deploy TLS-RPT falhou. |
TLSRPT_HIGH_FAILURE | Um relatório TLS-RPT agregado indica uma taxa de falha elevada. |
DMARC_ACTIVATED | Uma política DMARC hospedada passou para quarantine ou reject. |
DMARC_DEPLOY_FAILURE | Um deploy DMARC falhou. |
DMARC_ALIGNMENT_DROP | A taxa de alinhamento DMARC cai abaixo de um limite crítico. |
BIMI_CERT_EXPIRY | Um certificado VMC ou CMC hospedado está próximo da expiração. |
REDIRECT_ACTIVATED | Um redirecionamento hospedado ficou ativo. |
REDIRECT_DEACTIVATED | Um redirecionamento hospedado foi desativado. |
DOMAIN_REVERIFY_FAILED | Uma 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_type | Descrição |
|---|---|
RESOLVE_WATCH_DIFF | Uma resolve watch detecta uma mudança de resposta DNS. |
RESOLVE_LATENCY_ANOMALY | Uma 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
2xxem 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_typecomo 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_idcomo chave de deduplicação: ele permanece estável em todas as tentativas e em todos os reenvios de um mesmo evento.delivery_idmuda a cada tentativa. - Logue
event_idedelivery_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 emmonitoring,deploymentoudns. Se você quiser tratar apenas um subconjunto específico (por exemplo, somenteMONITOR_DOWN), filtre do lado do receptor sobre o campoevent_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úblicacdns_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.