Proteside Docs

Webhooks

Recibe alertas, scripts nuevos y reportes listos en tu endpoint, verifica la firma HMAC y gestiona los reintentos.

Con un webhook, Proteside envía un POST a tu endpoint en cuanto algo ocurre: una alerta nueva, un script desconocido en el checkout, un reporte PCI DSS listo. No necesitas consultar la API constantemente.

Webhooks, Slack, Microsoft Teams y correo electrónico son todos canales de notificación de la misma organización. Lo que creas mediante la API aparece en Ajustes → Notificaciones, y viceversa.

Crear un webhook

Abre Ajustes → Notificaciones y haz clic en Agregar canal. Debes ser propietario o administrador.

Elige el tipo Webhook y el alcance: Esta tienda (solo eventos de la tienda seleccionada) u Organización (eventos de todas las tiendas). El tipo y el alcance no se pueden cambiar después.

Completa Nombre y URL (debe empezar con https://) y deja Formato en JSON (firmado).

Completa Secreto de firma con un valor aleatorio de 16 a 128 caracteres y guárdalo: lo vas a necesitar para verificar las entregas.

Elige la Severidad mínima y los Eventos, mantén Canal activo encendido y haz clic en Guardar cambios.

Sin secreto, sin firma

Desde el dashboard el secreto es opcional. Si el campo queda vacío, las entregas salen sin el header X-Proteside-Signature y la tarjeta del canal muestra "sin firma". Completa siempre el secreto en los webhooks JSON.

Eventos

EventoCuándo se envíaseverityFiltrado por min_severity
alert.createdSe abre una alerta nueva (primera ocurrencia), incluidas las violaciones de CSPla de la alertasí
alert.resolvedUna alerta de SDK silencioso se resuelve sola porque el SDK volvió a enviar eventosmediumsí
integrity.changedEl contenido de un script autorizado cambió (alerta SCRIPT_INTEGRITY_MISMATCH)highsí
header.changedUn header de seguridad de la página de pago cambió (alerta HEADER_CHANGED)mediumsí
sdk.silentUna tienda con tráfico pasó 24 horas sin enviar eventos del SDK (alerta SDK_SILENT)mediumsí
script.detectedApareció un script nuevo en el checkout y espera revisióninfono
policy.appliedUna política de aprobación decidió sobre un script (modo automático o Aplicar)infono
report.readyEl reporte PCI DSS programado está listo (lunes, 09:00 UTC)infono
testDisparaste una pruebainfoignora todos los filtros

Eventos del catálogo que hoy no se envían

script.authorized y script.blocked aparecen en la lista de eventos del dashboard y se aceptan en el campo events, pero Proteside no los envía hoy. Para seguir las autorizaciones y los bloqueos, consulta GET /scripts o la auditoría.

alert.resolved solo se envía en la resolución automática de SDK silencioso. Resolver una alerta desde el dashboard o con PATCH /alerts/{id} no genera ningún evento. Los reportes generados con GET /reports/pci tampoco generan report.ready: solo el envío programado lo genera.

Filtros del canal

Cada canal tiene dos filtros:

  • events: lista de eventos aceptados. Vacía o null significa todos.
  • min_severity: aplica solo a los eventos de alerta (alert.created, alert.resolved, integrity.changed, header.changed y sdk.silent). script.detected, policy.applied y report.ready pasan siempre.

El valor predeterminado high descarta los eventos medium

Los canales nuevos nacen con min_severity: high (Alto o superior). Con ese valor, sdk.silent, header.changed y las alertas medium o low (como las violaciones de CSP) nunca llegan. Si quieres saber cuándo el SDK dejó de funcionar, usa min_severity: "medium" (Medio o superior) o menor.

Formato de la entrega

Proteside hace un POST con cuerpo JSON y estos headers:

HeaderContenido
Content-Typeapplication/json
User-AgentProteside-Webhooks/1.0
X-Proteside-EventTipo de evento, por ejemplo alert.created
X-Proteside-DeliveryID de la entrega (único por canal y evento, igual en todos los reintentos)
X-Proteside-Signaturet=<unix>,v1=<hex> (presente cuando el canal tiene secreto)

Todos los eventos usan el mismo sobre:

Propiedad

Tipo

Ejemplos de payload

Verificar la firma

Cuando el canal tiene secreto, cada entrega incluye:

X-Proteside-Signature: t=1791277201,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • t es el momento del envío en segundos Unix (se recalcula en cada intento).
  • v1 es el HMAC-SHA256, en hexadecimal, de t + . + cuerpo crudo, usando el secreto completo como clave, incluido el prefijo whsec_.

Para verificarla:

  1. Lee el cuerpo crudo, antes de cualquier parseo de JSON. Volver a serializar el JSON cambia los bytes y rompe la firma.
  2. Separa t y v1 y revisa el formato: t entero, v1 con 64 caracteres hexadecimales.
  3. Rechaza las entregas con un t a más de 300 segundos de tu reloj. Proteside no impone ese límite: la protección contra reenvíos maliciosos es responsabilidad de quien recibe.
  4. Calcula el HMAC esperado y compáralo en tiempo 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 el formato antes de cualquier comparación
  if (!/^\d+$/.test(parts.t ?? '') || !/^[0-9a-f]{64}$/i.test(parts.v1 ?? '')) return false;

  // 2. ventana de tiempo contra reenvíos
  const t = Number(parts.t);
  if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_S) return false;

  // 3. HMAC-SHA256(secreto, "t." + cuerpo crudo), comparado en tiempo 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(); // en producción, usa Redis o tu base de datos

// express.raw mantiene el cuerpo como Buffer, sin parsearlo
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); // ya procesado
  seen.add(event.id);

  res.sendStatus(204); // responde rápido (menos de 8 s)...
  queueMicrotask(() => handleEvent(event)); // ...y procesa después
});

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

app.listen(3000);

Cambiar el secreto

Envía un nuevo secret en PATCH /webhooks/{id} (o edita el Secreto de firma en el dashboard). El cambio aplica desde la siguiente entrega, sin período de convivencia: los reintentos pendientes también pasan a firmarse con el secreto nuevo. Para no perder eventos, haz que el receptor acepte los dos secretos durante algunos minutos, cámbialo en Proteside y después quita el anterior.

Entrega y reintentos

  • El primer intento sale en el momento del evento.
  • Tu endpoint tiene 8 segundos para responder. Solo las respuestas 2xx cuentan como éxito.
  • Las redirecciones no se siguen: un 301 o 302 cuenta como falla. Registra la URL final.
  • En caso de falla, Proteside lo vuelve a intentar hasta completar 4 intentos: el inmediato y tres más, unos 1 minuto, 10 minutos y 60 minutos después de la falla anterior. Los reintentos se ejecutan en ciclos de 5 minutos, así que pueden retrasarse algunos minutos más allá de esos intervalos.
  • Después de la cuarta falla, la entrega queda como Falló y no se reenvía. El canal no se desactiva solo.
  • Si el canal está desactivado en el momento del reintento, la entrega falla sin un nuevo envío.
  • No hay garantía de orden entre eventos.

Puede llegar más de una vez

Si tu endpoint procesa el evento pero tarda más de 8 segundos en responder, Proteside lo considera una falla y lo vuelve a enviar. Deduplica por el id del evento (evt_…), que se mantiene en los reintentos, o por el header X-Proteside-Delivery. Responde 2xx rápido y procesa en segundo plano.

El historial de las últimas 50 entregas (evento, estado, intentos, código HTTP y error) está en Ajustes → Notificaciones, en la tarjeta Historial de entregas, durante hasta 90 días. La API no expone el historial ni permite reenviar una entrega manualmente.

Probar el webhook

Desde el dashboard, haz clic en Probar conexión en la tarjeta del canal. Mediante la 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" }

La prueba envía un evento test firmado, con un único intento. Ignora los filtros de eventos y de severidad y se envía aunque el canal esté desactivado.

Revisa el campo delivered

POST /webhooks/{id}/test responde 200 incluso cuando la entrega falla. Verifica delivered y, si es false, el motivo en error: http_<status>: <inicio del cuerpo>, timeout o un mensaje de red.

Slack, Microsoft Teams y correo electrónico

Si solo quieres avisos para personas, no necesitas un endpoint propio:

  • Slack: crea un Incoming Webhook en Slack y registra la URL con "format": "slack" en la API, o el tipo Slack en el dashboard. Proteside envía un mensaje con formato, con el resumen del evento y un botón para abrirlo en el dashboard.
  • Microsoft Teams: lo mismo con "format": "teams" o el tipo Teams. El mensaje es una Adaptive Card.
  • Correo electrónico: solo desde el dashboard (tipo Correo, hasta 5 destinatarios). Cada tienda nace con el canal "E-mail (padrão)" para el correo del propietario.

Los mensajes de Slack y Teams tienen textos fijos en inglés. Los canales de correo electrónico reciben solo alert.created, report.ready y test, aunque haya otros eventos marcados: los avisos de integrity.changed, header.changed y sdk.silent no llegan por correo. Para esos, usa webhook, Slack o Teams.

Próximos pasos

En esta página