Proteside Docs

Webhooks

Receba alertas, scripts novos e relatórios prontos no seu endpoint, verifique a assinatura HMAC e trate as retentativas.

Com um webhook, o Proteside envia um POST para o seu endpoint assim que algo acontece: um alerta novo, um script desconhecido no checkout, um relatório PCI DSS pronto. Você não precisa ficar consultando a API.

Webhooks, Slack, Microsoft Teams e e-mail são todos canais de notificação da mesma organização. O que você cria pela API aparece em Configurações → Notificações, e vice-versa.

Criar um webhook

Abra Configurações → Notificações e clique em Adicionar canal. É preciso ser proprietário ou administrador.

Escolha o tipo Webhook e o escopo: Esta loja (só eventos da loja selecionada) ou Organização (eventos de todas as lojas). Tipo e escopo não podem ser alterados depois.

Preencha Nome e URL (precisa começar com https://) e deixe Formato em JSON (assinado).

Preencha Segredo de assinatura com um valor aleatório de 16 a 128 caracteres e guarde-o: você vai precisar dele para verificar as entregas.

Escolha a Severidade mínima e os Eventos, mantenha Canal ativo ligado e clique em Salvar alterações.

Sem segredo, sem assinatura

Pelo dashboard o segredo é opcional. Se o campo ficar vazio, as entregas saem sem o header X-Proteside-Signature e o card do canal mostra "sem assinatura". Sempre preencha o segredo em webhooks JSON.

Eventos

EventoQuando é enviadoseverityFiltrado por min_severity
alert.createdUm alerta novo é aberto (primeira ocorrência), inclusive violações de CSPa do alertasim
alert.resolvedUm alerta de SDK silencioso é resolvido sozinho porque o SDK voltou a enviar eventosmediumsim
integrity.changedO conteúdo de um script autorizado mudou (alerta SCRIPT_INTEGRITY_MISMATCH)highsim
header.changedUm header de segurança da página de pagamento mudou (alerta HEADER_CHANGED)mediumsim
sdk.silentUma loja com tráfego ficou 24 horas sem enviar eventos do SDK (alerta SDK_SILENT)mediumsim
script.detectedUm script novo apareceu no checkout e aguarda revisãoinfonão
policy.appliedUma política de aprovação decidiu um script (modo automático ou Aplicar)infonão
report.readyO relatório PCI DSS agendado ficou pronto (segunda-feira, 09:00 UTC)infonão
testVocê disparou um testeinfoignora todos os filtros

Eventos do catálogo que hoje não são enviados

script.authorized e script.blocked aparecem na lista de eventos do dashboard e são aceitos no campo events, mas o Proteside não os envia hoje. Para acompanhar autorizações e bloqueios, consulte GET /scripts ou a auditoria.

alert.resolved só é enviado na resolução automática de SDK silencioso. Resolver um alerta pelo dashboard ou por PATCH /alerts/{id} não gera evento. Os relatórios gerados por GET /reports/pci também não geram report.ready: só o envio agendado gera.

Filtros do canal

Cada canal tem dois filtros:

  • events: lista de eventos aceitos. Vazio ou null significa todos.
  • min_severity: vale só para os eventos de alerta (alert.created, alert.resolved, integrity.changed, header.changed e sdk.silent). script.detected, policy.applied e report.ready passam sempre.

O padrão high descarta eventos medium

Canais novos nascem com min_severity: high (Alto ou superior). Com esse valor, sdk.silent, header.changed e alertas medium ou low (como violações de CSP) nunca chegam. Se quer saber quando o SDK parou de rodar, use min_severity: "medium" (Médio ou superior) ou menor.

Formato da entrega

O Proteside faz um POST com corpo JSON e estes headers:

HeaderConteúdo
Content-Typeapplication/json
User-AgentProteside-Webhooks/1.0
X-Proteside-EventTipo do evento, por exemplo alert.created
X-Proteside-DeliveryID da entrega (único por canal e evento, igual em todas as retentativas)
X-Proteside-Signaturet=<unix>,v1=<hex> (presente quando o canal tem segredo)

Todo evento usa o mesmo envelope:

Propriedade

Tipo

Exemplos de payload

Verificar a assinatura

Quando o canal tem segredo, cada entrega traz:

X-Proteside-Signature: t=1791277201,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • t é o momento do envio em segundos Unix (recalculado a cada tentativa).
  • v1 é o HMAC-SHA256, em hexadecimal, de t + . + corpo cru, usando o segredo inteiro como chave, incluindo o prefixo whsec_.

Para verificar:

  1. Leia o corpo cru, antes de qualquer parse de JSON. Reserializar o JSON muda os bytes e quebra a assinatura.
  2. Separe t e v1 e confira o formato: t inteiro, v1 com 64 caracteres hexadecimais.
  3. Recuse entregas com t a mais de 300 segundos do seu relógio. O Proteside não impõe esse limite: a proteção contra reenvio malicioso é responsabilidade de quem recebe.
  4. Calcule o HMAC esperado e compare em tempo constante.
server.js
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';

const SECRET = process.env.PROTESIDE_WEBHOOK_SECRET; // "whsec_..."
const TOLERANCE_S = 300;

function verifySignature(rawBody, header, secret) {
  if (!header) return false;

  const parts = {};
  for (const item of header.split(',')) {
    const i = item.indexOf('=');
    if (i > 0) parts[item.slice(0, i).trim()] = item.slice(i + 1).trim();
  }

  // 1. valida o formato antes de qualquer comparação
  if (!/^\d+$/.test(parts.t ?? '') || !/^[0-9a-f]{64}$/i.test(parts.v1 ?? '')) return false;

  // 2. janela de tempo contra reenvio
  const t = Number(parts.t);
  if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_S) return false;

  // 3. HMAC-SHA256(segredo, "t." + corpo cru), comparado em tempo constante
  const expected = createHmac('sha256', secret).update(`${t}.`).update(rawBody).digest();
  const given = Buffer.from(parts.v1, 'hex');
  return given.length === expected.length && timingSafeEqual(given, expected);
}

const app = express();
const seen = new Set(); // em produção, use Redis ou o seu banco

// express.raw mantém o corpo como Buffer, sem parse
app.post('/hooks/proteside', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySignature(req.body, req.get('X-Proteside-Signature'), SECRET)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  if (seen.has(event.id)) return res.sendStatus(200); // já processado
  seen.add(event.id);

  res.sendStatus(204); // responda rápido (menos de 8 s)...
  queueMicrotask(() => handleEvent(event)); // ...e processe depois
});

function handleEvent(event) {
  console.log(event.type, event.data);
}

app.listen(3000);

Trocar o segredo

Envie um novo secret em PATCH /webhooks/{id} (ou edite o Segredo de assinatura no dashboard). A troca vale a partir da próxima entrega, sem período de convivência: retentativas pendentes também passam a ser assinadas com o segredo novo. Para não perder eventos, faça o receptor aceitar os dois segredos por alguns minutos, troque no Proteside e depois remova o antigo.

Entrega e retentativas

  • A primeira tentativa sai na hora do evento.
  • O seu endpoint tem 8 segundos para responder. Só respostas 2xx contam como sucesso.
  • Redirecionamentos não são seguidos: um 301 ou 302 conta como falha. Cadastre a URL final.
  • Em caso de falha, o Proteside tenta de novo até completar 4 tentativas: a imediata e mais três, cerca de 1 minuto, 10 minutos e 60 minutos depois da falha anterior. As retentativas rodam em ciclos de 5 minutos, então podem atrasar alguns minutos além desses intervalos.
  • Depois da quarta falha, a entrega fica como Falhou e não é reenviada. O canal não é desativado sozinho.
  • Se o canal estiver desativado na hora da retentativa, a entrega falha sem novo envio.
  • Não há garantia de ordem entre eventos.

Pode chegar mais de uma vez

Se o seu endpoint processa o evento mas demora mais de 8 segundos para responder, o Proteside considera falha e envia de novo. Deduplique pelo id do evento (evt_…), que se mantém nas retentativas, ou pelo header X-Proteside-Delivery. Responda 2xx rápido e processe em segundo plano.

O histórico das últimas 50 entregas (evento, status, tentativas, código HTTP e erro) fica em Configurações → Notificações, no card Histórico de entregas, por até 90 dias. A API não expõe o histórico nem permite reenviar uma entrega manualmente.

Testar o webhook

Pelo dashboard, clique em Testar conexão no card do canal. Pela API:

curl -s -X POST "https://app.proteside.com/api/v1/webhooks/a9b8c7d6-5555-4666-8777-888899990000/test" \
  -H "Authorization: Bearer $PROTESIDE_TOKEN"
{ "id": "a9b8c7d6-5555-4666-8777-888899990000", "delivered": false, "error": "http_404: Not Found" }

O teste envia um evento test assinado, com uma única tentativa. Ele ignora os filtros de eventos e de severidade e é enviado mesmo com o canal desativado.

Confira o campo delivered

POST /webhooks/{id}/test responde 200 mesmo quando a entrega falha. Verifique delivered e, se for false, o motivo em error: http_<status>: <início do corpo>, timeout ou uma mensagem de rede.

Slack, Microsoft Teams e e-mail

Se você só quer avisos para pessoas, não precisa de um endpoint próprio:

  • Slack: crie um Incoming Webhook no Slack e cadastre a URL com "format": "slack" na API, ou o tipo Slack no dashboard. O Proteside envia uma mensagem formatada com o resumo do evento e um botão para abri-lo no dashboard.
  • Microsoft Teams: o mesmo com "format": "teams" ou o tipo Teams. A mensagem é um Adaptive Card.
  • E-mail: só pelo dashboard (tipo E-mail, até 5 destinatários). Cada loja já nasce com o canal "E-mail (padrão)" para o e-mail do proprietário.

As mensagens do Slack e do Teams têm textos fixos em inglês. Os canais de e-mail recebem apenas alert.created, report.ready e test, mesmo que outros eventos estejam marcados: avisos de integrity.changed, header.changed e sdk.silent não chegam por e-mail. Para esses, use webhook, Slack ou Teams.

Próximos passos

Nesta página