Skip to main content

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