Webhook de leads
Notifica a Levely cada vez que un lead se crea o cambia de etapa en tu CRM. El endpoint es idempotente: puedes reintentar el mismo evento sin duplicar historial.
Endpoint
POST https://helios.levely.digital/api/webhooks/leads/{slug}/{secret}Levely te asigna un {slug} y un {secret} por cliente. Usa la URL completa tal cual — no modifiques ningún segmento. La autenticación es el secret; no se requieren headers adicionales.
Headers
Content-Type: application/json
Ejemplo con cURL
curl -X POST "https://helios.levely.digital/api/webhooks/leads/{slug}/{secret}" \
-H "Content-Type: application/json" \
-d '{
"lead_id": "12345678",
"raw_crm_lead_id": "12345678",
"status": "CONTACTED",
"raw_crm_from_status_id": "89758063",
"raw_crm_to_status_id": "89758064",
"platform": "kommo",
"created_at": "2026-06-04T15:30:00Z",
"utm_source": "facebook",
"utm_campaign": "verano-2026"
}'Payload de ejemplo
{
"lead_id": "12345678",
"raw_crm_lead_id": "12345678",
"status": "QUALIFIED",
"created_at": "2026-06-04T15:30:00Z",
"previous_status": "CONTACTED",
"raw_crm_from_status_id": "89758063",
"raw_crm_to_status_id": "89758064",
"utm_source": "facebook",
"utm_medium": "cpc",
"utm_campaign": "verano-2026",
"utm_content": "video-1",
"utm_term": "keyword",
"channel": "facebook",
"platform": "meta",
"contact_info": { "email": "maria@ejemplo.com", "phone": "+52..." },
"form_responses": { "presupuesto": "50000" },
"additional_params": { "vendedor": "ana" }
}| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| lead_id | string | Sí | Identificador único del lead en tu CRM. Para Kommo, usa el id numérico del lead (mismo valor que raw_crm_lead_id). |
| raw_crm_lead_id | string (digits) | Sí | Id canónico de la entidad lead en el CRM (Kommo: lead.id). Permite reconciliar con Kommo Sync y remapear etapas sin volver a consultar el CRM. |
| status | enum | Sí | Estado normalizado del lead. Valores: CREATED, CONTACTED, QUALIFIED, WON, LOST. |
| created_at | string | number | No | Fecha de creación del lead en tu CRM (ISO 8601 o Unix en segundos/milisegundos). Solo se usa al crear el lead por primera vez. |
| previous_status | enum | No | Estado Levely anterior. Requerido en cambios de etapa cuando el integrador conoce el estado previo; si no, Levely lo infiere desde su base de datos. |
| raw_crm_from_status_id | string (digits) | No | Id de etapa CRM antes del cambio (Kommo: status_id anterior). Requerido en transiciones de etapa para integraciones Kommo; omitir en el primer evento del lead. |
| raw_crm_to_status_id | string (digits) | Sí | Id de etapa CRM después del cambio (Kommo: status_id actual). Requerido en cada evento para integraciones Kommo. |
| raw_crm_status | string | No | Obsoleto — usa raw_crm_to_status_id. Si envías solo este campo y es numérico, se acepta como alias de raw_crm_to_status_id. |
| lost_motive_id | string | No | Solo en status LOST: id del motivo de pérdida en el CRM (Kommo: loss_reason_id). Opcional; si falta junto con lost_motive, el portal agrupa en «Sin motivo». Se ignora en otros status. |
| lost_motive | string | No | Solo en status LOST: nombre del motivo de pérdida (Kommo loss reason name). Recomendado enviar junto con lost_motive_id; cualquiera de los dos basta para un bucket con nombre. Se ignora en otros status. |
| utm_source, utm_medium, utm_campaign, utm_content, utm_term | string | null | No | UTMs de atribución. Se guardan en la creación del lead y no se sobrescriben en actualizaciones. Si envías UTMs en eventos posteriores, se actualiza `latest_utms` sin perder los originales. |
| channel | string | No | Canal de adquisición (ej. facebook, google, organic). |
| platform | string | No | Plataforma publicitaria (ej. meta, google). |
| contact_info | object | No | Datos de contacto libres (email, teléfono, etc.). |
| form_responses | object | No | Respuestas de formulario u otros campos del CRM. |
| additional_params | object | No | Cualquier dato adicional que quieras conservar junto al lead. |
Estados del funnel
Tu CRM puede usar cualquier nomenclatura interna. Antes de llamar al webhook, mapea cada etapa al enum de Levely:
CREATEDLead creado en el CRMCONTACTEDPrimer contacto realizadoQUALIFIEDLead calificado / con intenciónWONVenta cerradaLOSTLead perdido o descartado
Se aceptan saltos de etapa (ej. CREATED → QUALIFIED) y LOST desde cualquier estado. Las transiciones hacia atrás también se registran tal como las envía tu CRM.
Comportamiento del sistema
- Lead nuevo: si
lead_idno existe, se crea el lead y se registra el primer evento de cambio de estado. - Cambio de estado: si el lead ya existe y el
statuses distinto, se actualiza y se agrega un evento al historial. - Idempotencia: si envías el mismo
lead_idcon el mismostatusque ya tiene, la respuesta es 200 sin crear eventos duplicados. - UTMs: los valores enviados en la creación se conservan como atribución original. En actualizaciones, solo se actualiza
latest_utmssi incluyes al menos un UTM no nulo. - Estado anterior: envía
previous_statusyraw_crm_from_status_idcuando tu CRM lo conozca. Si no, Levely lo infiere desde su registro. - Kommo / Make: incluye siempre
raw_crm_lead_id,raw_crm_to_status_idy, en cambios de etapa,raw_crm_from_status_idcon los ids numéricos de Kommo (lead.idystatus_id). Usaplatform: "kommo"para activar la validación.
Respuestas HTTP
| Código | Body | Cuándo ocurre |
|---|---|---|
| 200 | { "ok": true } | Evento procesado correctamente (incluye reintentos idempotentes). |
| 400 | { "error": "Invalid JSON body" } | El cuerpo no es JSON válido. |
| 401 | { "error": "Invalid webhook secret" } | El secret en la URL no existe o es incorrecto. |
| 422 | { "error": "lead_id is required" } | Falta `lead_id` o está vacío. |
| 422 | { "error": "status must be one of: CREATED, ..." } | El `status` no pertenece al enum permitido. |
| 422 | { "error": "raw_crm_to_status_id must be a numeric CRM stage id" } | Integración Kommo: falta raw_crm_to_status_id o no es un id numérico. |
| 422 | { "error": "created_at must be ISO 8601 or Unix timestamp" } | Se envió `created_at` pero con formato inválido. |
| 500 | { "error": "Failed to process lead" } | Error interno al persistir el evento. |
En integraciones con reintentos automáticos, un 200 con { "ok": true } confirma que el evento fue aceptado. Los errores 4xx no deben reintentarse sin corregir el payload.
¿Necesitas credenciales o ayuda con el mapeo de estados? Contacta al equipo de Levely.