evobits

evobits · especificação de integração · webhook de alertas

Contrato de webhook de alertas

Como um canal do tipo webhook, configurado numa empresa do evobits, recebe o ciclo de vida de um alerta (aberto, escalonado, repetido, resolvido), um teste de canal ou o resumo diário, e como confirmar que a entrega é mesmo do evobits.

Para
Sistemas que recebem o webhook de um canal do tipo webhook (Alertas → Canais)
Destino
a URL cadastrada no canal, escolhida por vocês
Método
POST (JSON)
Autenticação
HMAC-SHA256 por canal, num cabeçalho de assinatura

Copia esta especificação inteira em Markdown, para colar no ChatGPT, no Claude, no Gemini ou no assistente que vocês usam.

Exemplo de prompt
Preciso receber o webhook de alertas do evobits no nosso sistema, seguindo a
especificação que vou colar abaixo.

Nossa stack: (descreva aqui: linguagem, framework, como recebemos webhooks hoje).

Leia a especificação inteira e:
1. crie o endpoint que recebe o POST e responde 2xx rápido (o corpo pode ser
   processado depois, de forma assíncrona);
2. valide a assinatura sobre o corpo BRUTO, em tempo constante, e rejeite
   timestamp fora da janela de 5 minutos;
3. trate os eventos alerta.aberto, alerta.escalonado, alerta.repeticao,
   alerta.resolvido, teste e digest;
4. use X-Evobits-Entrega para não processar a mesma entrega duas vezes;
5. escreva os testes do checklist de aceite.

<COLE AQUI O TEXTO COPIADO PELO BOTÃO "COPIAR PARA IA">

01Por que este contrato existe

Um canal do tipo webhook manda, em tempo real, o mesmo ciclo de vida de alerta que o e-mail e o SMS já notificam: abertura, escalonamento, repetição sem reconhecimento e resolução, além do envio de teste do canal e do resumo diário (quando ligado). É a forma de integrar o evobits com um sistema próprio (um bot de chat, um sistema de chamados, um painel interno) sem depender de e-mail.

Nenhum endereço, credencial ou dado de configuração de dispositivo sai neste webhook: só o que já aparece no painel administrativo (mensagem do alerta, nomes de dispositivo e sensor, severidade, estado e os mesmos links que o e-mail usa).

02Como configurar

O canal é criado no painel administrativo (Alertas → Canais → tipo webhook), com a URL de destino. O segredo HMAC é gerado pelo evobits (nunca por quem cadastra o canal) e mostrado uma única vez, na criação ou na rotação do canal, com um aviso de que não será mostrado de novo. Guarde-o como guardaria qualquer segredo, nunca em texto plano versionado.

Por padrão só https:// é aceito, e endereços privados, de loopback ou de link-local são recusados na criação do canal e a cada entrega. Isso não é configurável pelo painel: é uma proteção do evobits contra o canal apontar sem querer para a própria rede interna de quem o configurou.

03Eventos e corpo

Content-Type application/json. O campo evento diz o que aconteceu:

Eventos
EventoQuando
alerta.abertoUm alerta novo foi aberto e o primeiro passo da regra foi notificado
alerta.escalonadoO alerta continua sem reconhecimento e passou para o próximo passo da regra
alerta.repeticaoO alerta continua aberto e sem reconhecimento; o último passo se repete
alerta.resolvidoA condição deixou de valer (só quando a regra notifica resolução)
testeEnvio de teste do canal, pelo botão Testar do painel
digestResumo diário do canal (só quando "Resumo diário" está ligado no canal)

Corpo de um alerta.aberto:

{
  "evento": "alerta.aberto",
  "entrega": "8421",
  "enviadoEm": "2026-09-24T14:32:10.000Z",
  "tenant": { "slug": "acme" },
  "alerta": {
    "id": "512",
    "regra": "CPU alta",
    "severidade": "ruim",
    "estado": "aberto",
    "mensagem": "CPU em 95% (limiar 90%) em srv-01",
    "abertoEm": "2026-09-24T14:29:00.000Z",
    "fechadoEm": null,
    "dispositivo": { "id": "…", "nome": "srv-01" },
    "sensor": { "id": "…", "nome": "CPU", "tipo": "snmp_cpu" },
    "detalhe": { "valor": 95, "unidade": "%", "limiar": 90 }
  },
  "links": {
    "painel": "https://app.evobits.com.br/admin/alertas/512",
    "reconhecer": "https://app.evobits.com.br/admin/alertas/ack/…"
  }
}

dispositivo e sensor vêm null quando o alvo do alerta é uma sonda (condição sonda_offline): nesse caso a própria mensagem já descreve a sonda por extenso. links.reconhecer só existe em alerta.aberto, alerta.escalonado e alerta.repeticao: um alerta resolvido, um teste ou um digest não têm o que reconhecer.

Em teste não há campo alerta nenhum. Em digest o lugar de alerta é um resumo, com o período e três listas no mesmo formato do objeto alerta acima:

{
  "evento": "digest",
  "entrega": "9010",
  "enviadoEm": "2026-09-24T11:00:00.000Z",
  "tenant": { "slug": "acme" },
  "resumo": {
    "desde": "2026-09-23T11:00:00.000Z",
    "ate": "2026-09-24T11:00:00.000Z",
    "abertos": [ "…" ],
    "fechados": [ "…" ],
    "aindaAbertos": [ "…" ]
  },
  "links": { "painel": "https://app.evobits.com.br/admin/alertas" }
}

04Cabeçalhos

Cabeçalhos de toda entrega
CabeçalhoO que é
Content-Typeapplication/json
User-Agentevobits-webhook/1
X-Evobits-EventoO mesmo valor do campo evento do corpo
X-Evobits-EntregaId desta entrega, igual em toda reentrega da MESMA notificação (chave de idempotência)
X-Evobits-TimestampInstante do envio, em segundos desde a época Unix
X-Evobits-Assinaturasha256=<hex>, ver a seção seguinte

05Verificar a assinatura

A assinatura é o HMAC-SHA256, em hexadecimal, do texto <timestamp>.<corpo bruto>, com o segredo do canal como chave. Some sempre sobre o corpo bruto (os bytes recebidos, antes de qualquer JSON.parse): reformatar o JSON antes de assinar dá um resultado diferente do que o evobits calculou.

Janela de 5 minutos. Compare o X-Evobits-Timestamp com o relógio de vocês e recuse (sem processar) qualquer entrega fora de uma janela de 5 minutos para trás ou para a frente. Isso limita o estrago de uma entrega capturada e reenviada por um terceiro.

Comparação em tempo constante. Nunca compare a assinatura com === ou ==: use uma função de comparação que leve o mesmo tempo independente de onde a primeira diferença aparece (exemplos abaixo). Uma comparação comum vaza, pelo tempo de resposta, quantos caracteres iniciais acertaram.

Node.js:

import { createHmac, timingSafeEqual } from 'node:crypto';

function verificar(corpoBruto, timestamp, assinaturaRecebida, segredo) {
  const idade = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!Number.isFinite(idade) || idade > 300) return false; // janela de 5 min

  const esperada = 'sha256=' + createHmac('sha256', segredo)
    .update(`${timestamp}.${corpoBruto}`)
    .digest('hex');

  const a = Buffer.from(esperada);
  const b = Buffer.from(assinaturaRecebida || '');
  return a.length === b.length && timingSafeEqual(a, b);
}

Python:

import hmac
import hashlib
import time

def verificar(corpo_bruto: bytes, timestamp: str, assinatura_recebida: str, segredo: str) -> bool:
    idade = abs(time.time() - float(timestamp))
    if idade > 300:  # janela de 5 min
        return False

    mensagem = timestamp.encode() + b'.' + corpo_bruto
    esperada = 'sha256=' + hmac.new(segredo.encode(), mensagem, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperada, assinatura_recebida or '')

06Reentrega e idempotência

Uma resposta 2xx confirma a entrega. Qualquer outra coisa (4xx, 5xx, timeout de 10 segundos, erro de conexão) conta como falha e entra na fila de reentrega, com espera progressiva entre as tentativas: 1, 2, 5, 15 e 60 minutos. Sem uma resposta 2xx até a sexta tentativa, o evobits desiste e a entrega fica marcada como falha (visível no painel, na tela do alerta).

Um redirecionamento não é seguido: aponte a URL do canal direto para o endpoint final, sem passar por um encurtador ou por um redirect de HTTP para HTTPS.

Idempotente por X-Evobits-Entrega. Toda reentrega da MESMA notificação chega com o MESMO valor neste cabeçalho. Guarde os últimos ids processados (mesmo que só por algumas horas) e ignore uma entrega repetida em vez de agir duas vezes sobre o mesmo alerta.

07Como saber que está pronto

  1. 01O endpoint responde 2xx rapidamente, mesmo antes de terminar de processar o corpo.
  2. 02A assinatura é conferida sobre o corpo BRUTO, em tempo constante, e uma assinatura errada é rejeitada.
  3. 03Um X-Evobits-Timestamp fora da janela de 5 minutos é rejeitado.
  4. 04Os seis eventos (alerta.aberto, alerta.escalonado, alerta.repeticao, alerta.resolvido, teste, digest) são tratados sem quebrar em campos ausentes (dispositivo/sensor nulos, sem alerta em teste).
  5. 05Uma entrega repetida com o mesmo X-Evobits-Entrega não duplica a ação do lado de vocês.
  6. 06Um teste pelo botão Testar do painel chega e é reconhecido como evento: "teste".