Webhooks
En el momento en que LeadPass decide el veredicto de un lead, puede enviar por POST el resultado completo — veredicto, resumen y la evidencia por criterio — a la URL que tú elijas. Eso significa que los leads cualificados llegan a tu CRM, a Slack o a tu plataforma de automatización segundos después de que terminen de hablar, con todo lo que tu equipo necesita para actuar. Cada petición va firmada, para que puedas confiar en lo que llega.
El contrato del webhook es v1 y solo aditivo: con el tiempo pueden añadirse campos, pero nada de lo documentado aquí se renombrará, cambiará de tipo ni se eliminará sin una versión nueva anunciada de forma explícita.
Primeros pasos
- Abre Ajustes → Workspace → Webhook (
/app/settings/workspace). Activa el webhook del workspace y elige qué veredictos quieres recibir. - Pega la URL de tu endpoint. Una URL de disparador «Catch Raw Hook» de Zapier, «Webhook» de Make o «Webhook» de n8n funciona tal cual — no hace falta ninguna integración nativa.
- Haz clic en Enviar evento de prueba. LeadPass envía un payload de ejemplo
lead.passcon"test": truepara que veas la forma exacta. - Mapea los campos que necesites (
verdict,lead.email,summary,criteria, …) en tu herramienta receptora. Filtra porevent,verdictotestsegún te haga falta. - Copia el secreto de firma desde la misma pestaña de Ajustes y verifica las firmas en tu receptor (mira «Verificar la firma» más abajo). Recomendado para cualquier cosa que vaya más allá de un catch hook no-code.
Eventos
| Evento | Cuándo se dispara |
|---|---|
lead.pass |
El primer veredicto decidido del lead es Pass (revision: 1). |
lead.review |
El primer veredicto decidido del lead es Review (revision: 1). |
lead.no_pass |
El primer veredicto decidido del lead es No pass (revision: 1). |
lead.verdict_revised |
Cualquier revisión decidida posterior sobre el mismo lead (revision > 1): una decisión de un miembro del equipo o un re-análisis, incluso cuando el valor del veredicto no cambia. |
El vocabulario de veredictos en los payloads es pass | review | no_pass. Un cuarto valor, incomplete, solo aparece como previous_verdict o como reemplazo tras un re-análisis — nunca es suscribible y nunca es un primer evento.
Filtro de suscripción
Las casillas de veredicto en los ajustes del workspace deciden qué se entrega:
- Un primer evento (
revision: 1) se envía cuando su veredicto resultante está seleccionado. - Una revisión posterior (
lead.verdict_revised) se envía cuando está seleccionado cualquiera de los dos: el veredicto anterior o el actual. Un endpoint suscrito solo a Pass sigue enterándose cuando un Pass se revoca, pero nunca de una revisión de Review a No pass de un lead que no llegó a ver. Un re-análisis también puede publicar un Pass a Pass con nueva evidencia, resumen o acción sugerida.
Payload
Cada entrega es un POST con un cuerpo JSON con esta forma:
{
"id": "evt_01J8ZKQ2V5X6C7...",
"event": "lead.review",
"version": 1,
"created_at": "2026-07-10T12:34:56+00:00",
"test": false,
"flow": { "public_id": "fl_8k2m9x", "name": "Before a demo", "language": "es" },
"lead": {
"uuid": "9b2f6c9e-...",
"name": "María García",
"email": "[email protected]",
"phone": "+34 600 000 000",
"company": "Example SL",
"utm": { "utm_source": "meta" },
"started_at": "2026-07-10T12:30:00+00:00",
"completed_at": "2026-07-10T12:34:55+00:00"
},
"verdict": "review",
"previous_verdict": null,
"decided_by": "system",
"revision": 1,
"summary": "Budget named, timing unclear...",
"criteria": [
{ "key": "budget", "label": "Budget", "polarity": "positive", "band": "high", "evidence": "\"around €2,000 a month\"" }
],
"failed_gates": [],
"missing_criteria": [],
"links": { "lead": "https://useleadpass.com/app/leads/9b2f6c9e-..." }
}
Notas sobre los campos:
id— id único del evento, estable entre reintentos del mismo evento. Úsalo para deduplicar.event— uno delead.pass,lead.review,lead.no_pass,lead.verdict_revised.test—truesolo en los eventos de prueba enviados desde los ajustes del workspace; nunca describen a una persona real.flow.public_id— el identificador público opaco del flow; filtra por él cuando varios flows compartan un mismo endpoint.flow.languagees el idioma del flow en el momento de la decisión (el idioma en el que responden sus leads) — úsalo para hacer el seguimiento en el idioma correcto.lead— los campos que el flow no recoge llegan comonull.started_ates el momento en que la persona empezó el flow;completed_at, el momento en que el proceso de cualificación llegó a su resultado final.verdict—pass | review | no_pass; en un reemplazo por re-análisis (revision > 1) también puede serincomplete, lo que significa que el veredicto anterior ya no se sostiene y todavía no existe una lectura nueva. Nunca es un primer evento.previous_verdict— los mismos valores, incluidoincomplete, onullen un primer veredicto.decided_by—systemcuando la decisión la tomó el motor (la primera finalización o un re-análisis),team_membercuando la tomó una persona (una corrección o una decisión desde la cola de revisión).revision— 1 para el primer veredicto decidido, y sube en 1 con cada decisión posterior sobre el mismo lead. Los eventos de prueba usanrevision: 0.criteria— los criterios propios del flow, no señales fijas. Cada entrada llevakey,label,polarity,bandyevidence.failed_gates— laskeyde los criterios cuya regla estricta descartó al lead. Así se explica unno_passcuyas bandas parecen decentes: se disparó una regla.missing_criteria— laskeyde los criterios que exigían una evidencia que el lead nunca dio; la causa detrás de un veredicto de reemplazoincomplete.links.lead— abre el lead en LeadPass para cualquier compañero con sesión iniciada y acceso, sea cual sea el workspace que tuviera activo.
Cómo leer las bandas de los criterios según su polaridad
La band de cada criterio es una de no_data | very_low | low | medium | high | very_high. Lee siempre la banda a través de la polarity del criterio:
| Polaridad | Una banda alta significa | Una banda baja significa |
|---|---|---|
positive |
Favorable | Desfavorable |
risk |
Adverso | Favorable |
no_data significa que el lead no dio ninguna evidencia para ese criterio.
Orden: la última revisión gana
revision sube en 1 con cada veredicto decidido de un lead. Los reintentos pueden llegar desordenados, así que guarda la revision más alta que hayas visto por cada lead.uuid e ignora cualquier cosa por debajo. LeadPass también cancela por su lado las entregas en cola que han quedado superadas, pero la regla de la revisión es la garantía. Una revisión posterior puede llevar legítimamente el mismo verdict tras un re-análisis.
Deduplica por id: es estable entre reintentos del mismo evento.
Verificar la firma
Cada petición lleva estas cabeceras:
X-LeadPass-Signature: t=1760096096,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e163747325f37d9fa8c2fe...
X-LeadPass-Event: lead.review
X-LeadPass-Delivery: evt_01J8ZKQ2V5X6C7...
v1 es HMAC-SHA256(t + "." + rawBody, secret), calculado con el secreto de firma de tu workspace (Ajustes → Workspace → Webhook). Para verificarla:
- Extrae
t(un timestamp Unix) yv1de la cabeceraX-LeadPass-Signature. - Calcula
HMAC-SHA256sobre la cadenat + "." + rawBodyusando el cuerpo crudo de la petición, sin parsear. - Compara el resultado con
v1usando una comparación en tiempo constante. - Rechaza los timestamps antiguos — ±5 minutos es una ventana razonable.
PHP
[$t, $v1] = [null, null];
foreach (explode(',', $request->header('X-LeadPass-Signature')) as $part) {
[$k, $v] = explode('=', $part, 2);
if ($k === 't') { $t = $v; }
if ($k === 'v1') { $v1 = $v; }
}
$expected = hash_hmac('sha256', $t.'.'.$request->getContent(), $secret);
abort_unless(hash_equals($expected, (string) $v1) && abs(time() - (int) $t) < 300, 403);
Node.js
const crypto = require('crypto');
function verify(signatureHeader, rawBody, secret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((p) => p.split('=', 2))
);
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
const valid =
expected.length === (parts.v1 || '').length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return valid && fresh;
}
Asegúrate de que tu framework te da el cuerpo crudo de la petición para el HMAC — un objeto JSON re-serializado no coincidirá.
Regenerar el secreto (en la misma pestaña de Ajustes) se aplica desde el siguiente intento de entrega — actualiza primero tu receptor.
Entrega y reintentos
- Solo HTTPS. Las URLs
http://sin cifrar no se aceptan. - Tu endpoint tiene 10 segundos para responder. Cualquier estado
2xxcuenta como entregado. No se siguen las redirecciones. - Los fallos reintentables — errores de red,
408,425,429y cualquier5xx— se reintentan hasta 5 intentos a lo largo de unas 2,5 horas con backoff y jitter. Se respeta una cabeceraRetry-Afternumérica, con un tope de 1 hora. - El resto de respuestas
4xxfallan de inmediato, sin reintento. - Desactivar el webhook cancela las entregas en cola. Cambiar la URL se aplica al siguiente intento de todo lo que siga en cola.
- Los registros de entrega (payload incluido) se conservan 30 días. Borrar un lead borra sus registros de entrega de inmediato.
Desactivación automática tras fallos repetidos
Tras 10 entregas reales fallidas consecutivas sin ningún éxito entre medias, LeadPass apaga el webhook, cancela lo que siga en cola y envía un email al creador del workspace si sigue siendo miembro de la cuenta; si no, al propietario de la cuenta. Los veredictos decididos mientras el webhook está apagado no se reenvían después. Para recuperarlo: arregla tu receptor, vuelve a activar el webhook en los ajustes del workspace y confírmalo con Enviar evento de prueba. Los pings de prueba nunca cuentan para el contador de fallos.
Eventos de prueba
El botón Enviar evento de prueba de Ajustes → Workspace → Webhook envía un payload de ejemplo con:
"test": true"event": "lead.pass""revision": 0
Los eventos de prueba nunca describen a una persona real y nunca cuentan para el contador de fallos de la desactivación automática. Úsalos para montar el mapeo de campos y para confirmar que un receptor está sano después de reactivarlo.
Mantienen la forma exacta de un evento real, incluida una cadena links.lead sintética, para que las herramientas no-code puedan descubrir todos los campos sin exponer ningún registro guardado.