Saltar al contenido principal

Outbound Webhooks

Crimoo puede notificar sistemas externos (CRMs, Zapier, n8n, Make, etc.) en tiempo real cuando identifica o actualiza contactos. Cada webhook es una suscripción configurable a uno o más tipos de eventos.


Qué hace cada tipo de evento​

contact.identified — Contacto Identificado​

Se dispara la primera vez que Crimoo asocia una identidad real a un visitante. Ocurre cuando:

  • El visitante envía un formulario capturado por una capture rule
  • Crimoo extrae el email (u otro PII), hashea y crea un nuevo registro de contacto en PostgreSQL
  • Es la señal de que "este visitante anónimo ahora tiene nombre e email"

Caso de uso típico: Enviar el nuevo lead a un CRM externo (HubSpot, Salesforce, Pipedrive) en el momento exacto en que se identifica.

{
"eventType": "contact.identified",
"data": {
"contact": {
"id": "uuid",
"email": "user@example.com",
"fields": { "name": "Juan", "phone": "+34600000000" }
}
}
}

contact.updated — Contacto Actualizado​

Se dispara cuando un contacto ya existente recibe nueva información. Ocurre cuando:

  • El mismo visitante vuelve a enviar un formulario con campos distintos o adicionales
  • Crimoo detecta el contacto por hash de email o por cookie _crimoo_uid y actualiza sus campos
  • El payload incluye previousFields para comparar qué cambió

Caso de uso típico: Sincronizar actualizaciones de perfil a un sistema externo, o disparar un workflow cuando el contacto agrega su teléfono tras haber dado solo el email.

{
"eventType": "contact.updated",
"data": {
"contact": {
"id": "uuid",
"email": "user@example.com",
"fields": { "name": "Juan", "phone": "+34600000000", "company": "Acme" }
},
"previousFields": { "name": "Juan" }
}
}

contact.merged (próximamente)​

Se dispara cuando dos contactos duplicados se fusionan en uno solo. El payload incluirá el ID del contacto eliminado.

contact.deleted (próximamente)​

Se dispara cuando un contacto es eliminado del CRM.


Flujo general​


Payload completo​

Headers enviados:

Content-Type: application/json
X-Webhook-Event: contact.identified
X-Delivery-Id: 550e8400-e29b-41d4-a716-446655440000
X-Webhook-Signature: sha256=<hmac_hex>
User-Agent: Crimoo-Webhook/1.0

Body:

{
"deliveryId": "550e8400-e29b-41d4-a716-446655440000",
"eventType": "contact.identified",
"timestamp": "2026-04-07T14:23:01.000Z",
"gtmContainerId": "GTM-XXXXXXX",
"data": {
"contact": { ... },
"previousFields": { ... }
}
}

Idempotencia: deliveryId es el mismo UUID en todos los reintentos del mismo evento. Úsalo para deduplicar en el receptor.


Verificar la firma HMAC​

El sistema firma el body con el secret del webhook usando HMAC-SHA256. Para validarlo en el receptor:

import { createHmac, timingSafeEqual } from 'crypto';

const expected = `sha256=${createHmac('sha256', webhookSecret).update(rawBody).digest('hex')}`;
const received = req.headers['x-webhook-signature'];
const valid = timingSafeEqual(Buffer.from(expected), Buffer.from(received));

Usa timingSafeEqual para evitar timing attacks. El secret se muestra una sola vez al crear el webhook.


Entrega con reintentos y circuit breaker​

Tiempos de reintento​

IntentoDemora
1Inmediato
2+10 segundos
3+30 segundos
4+90 segundos
5+270 segundos (~4.5 min)

Circuit breaker​

Cuando el circuit breaker se activa, el webhook aparece con alerta en la UI y deja de recibir eventos hasta que se resetee manualmente.


Configuración​

CampoDescripción
NombreIdentificador legible
URLEndpoint HTTPS del receptor
Eventoscontact.identified, contact.updated (multi-selección)
Rate limitPeticiones por minuto (default 60, token bucket por webhook)

API REST​

Base: /api/crm/webhooks/:gtmContainerId

MétodoRutaDescripción
GET/Listar webhooks
POST/Crear (devuelve secret completo, solo una vez)
GET/:idObtener webhook (secret enmascarado)
PUT/:idActualizar
DELETE/:idEliminar
POST/:id/testEnviar entrega de prueba
GET/:id/deliveriesHistorial de entregas (últimos 50, TTL 7 días)
POST/:id/reset-circuitResetear circuit breaker