API · Leads — busca y edita tus leads desde otra herramienta
Los endpoints de leads te dejan encontrar un lead, leer su ficha y modificarla desde fuera de ZeroChats: moverlo de etapa cuando reserva en tu calendario, etiquetarlo cuando paga en tu pasarela, o guardarle el email que dejó en un formulario. Es el complemento natural del Webhook personalizado: el webhook te avisa de lo que pasa en ZeroChats, y con estos endpoints reflejas en ZeroChats lo que pasa fuera.
Esta página cubre solo esos endpoints. Cómo conseguir la clave, cómo autenticarte, el límite de peticiones y los errores comunes a toda la API están en API de ZeroChats.
La URL base es:
https://app.zerochats.com/api/v1| Método | Endpoint | Qué hace |
|---|---|---|
GET | /leads | Busca leads por usuario, email o teléfono. |
GET | /leads/{id} | Devuelve un lead por su identificador. |
GET | /leads/resolve | Devuelve el lead al que apunta el utm_user de un enlace. |
PATCH | /leads/{id} | Cambia la etapa, las etiquetas o el contacto de un lead. |
La API no crea leads ni manda mensajes. Un lead nace cuando alguien escribe a tu negocio; desde aquí solo lo encuentras y lo editas.
El lead
Sección titulada «El lead»Los cuatro endpoints devuelven el lead con la misma forma, y es exactamente la misma que el objeto lead que manda el Webhook personalizado. Lo que aprendas a leer en un sitio te sirve en el otro, y el id que te llega por webhook es el que usas aquí.
{ "id": "3d51f0c2-9a18-4b7e-8c56-1e2d3f4a5b6c", "name": "Ada Lovelace", "username": "ada", "platform": "INSTAGRAM", "externalId": "17841400000000000", "phone": null, "state": "IN_PROGRESS", "tags": ["caliente"], "createdAt": "2026-08-11T09:12:30.000Z"}| Campo | Qué es |
|---|---|
id | Identificador del lead en ZeroChats. Estable: úsalo como clave. |
name | Nombre del lead. |
username | Nombre de usuario en la plataforma de origen. |
platform | Canal de origen: INSTAGRAM, WHATSAPP, WASENDERAPI o MESSENGER. |
externalId | Identificador del lead en su plataforma: el id de Instagram, o los dígitos del teléfono en los canales de WhatsApp. null si no se conoce. |
email | Email del lead, o null si todavía no lo ha dado. |
phone | Teléfono del lead, o null si todavía no lo ha dado. |
state | Etapa del embudo de leads, por su nombre. La tabla de equivalencias está más abajo. |
tags | Lista completa de etiquetas del lead en ese momento, por su nombre. |
createdAt | Fecha de creación del lead, en formato ISO 8601 (UTC). |
En estos endpoints la respuesta no lleva meta: solo success y data.
Buscar un lead
Sección titulada «Buscar un lead»GET /leads busca por exactamente un criterio, que va como parámetro en la URL:
| Parámetro | Qué compara |
|---|---|
username | El nombre de usuario de Instagram o WhatsApp, exacto, sin distinguir mayúsculas. |
email | El email recogido en la conversación, exacto, sin distinguir mayúsculas. |
phone | El número de WhatsApp del lead y el teléfono recogido en la conversación. Acepta cualquier formato —+34 600 111 222, 0034600111222, 34600111222—: se queda con los dígitos y compara. Mándalo con prefijo de país. |
curl -H "Authorization: Bearer zcst_tu_clave" \data es siempre una lista, de más reciente a más antiguo y con un máximo de 20 resultados:
{ "success": true, "data": [ { "id": "3d51f0c2-9a18-4b7e-8c56-1e2d3f4a5b6c", "name": "Ada Lovelace", "username": "ada", "platform": "INSTAGRAM", "externalId": "17841400000000000", "phone": null, "state": "IN_PROGRESS", "tags": ["caliente"], "createdAt": "2026-08-11T09:12:30.000Z" } ]}Es una lista y no un único lead porque la misma persona puede ser dos leads: uno por cada canal en el que te ha escrito. Si Ada te escribió por Instagram y luego por WhatsApp y dejó el mismo email en las dos conversaciones, la búsqueda por email devuelve las dos fichas. Si nadie coincide, recibes un 200 con data a [], no un 404.
Mandar dos criterios a la vez, o ninguno, es un 400 con error.code a invalid_request y el mensaje provide exactly one of username, email or phone. Un phone sin ningún dígito también es un 400.
Leer un lead
Sección titulada «Leer un lead»GET /leads/{id} devuelve un lead por su identificador de ZeroChats: el id que te da el webhook o la búsqueda.
curl -H "Authorization: Bearer zcst_tu_clave" \ https://app.zerochats.com/api/v1/leads/3d51f0c2-9a18-4b7e-8c56-1e2d3f4a5b6cSi el lead no existe o no es de tu negocio, la respuesta es un 404 con error.code a not_found. Los dos casos se ven igual a propósito: la clave no puede ni siquiera confirmar que un lead ajeno existe.
Editar un lead
Sección titulada «Editar un lead»PATCH /leads/{id} recibe un JSON con los campos que quieras cambiar. Solo se escriben los que mandas; el resto queda como está.
curl -X PATCH \ -H "Authorization: Bearer zcst_tu_clave" \ -H "Content-Type: application/json" \ -d '{ "state": "BOOKED", "addTags": ["caliente"] }' \ https://app.zerochats.com/api/v1/leads/3d51f0c2-9a18-4b7e-8c56-1e2d3f4a5b6cEl cuerpo admite estos campos, y solo estos:
{ "state": "BOOKED", "addTags": ["caliente", "9f1c8a4e-2b77-4d0a-9d1c-3f2b6a5c7e10"], "removeTags": ["frío"], "phone": "+34 600 111 222"}La respuesta es el lead tal como queda después del cambio, con la misma forma que GET /leads/{id}.
La etapa del embudo, por su nombre:
| Etapa en la app | state |
|---|---|
| Abierta | OPEN_CONVERSATION |
| Descubriendo | IN_PROGRESS |
| Cualificando | QUALIFYING |
| Propuesta | LEAD |
| Agendado | BOOKED |
| Pagado | CLIENT |
| Pospuesto | POSTPONED |
| No cualificado | NO_QUALIFY |
| Perdido | LOST |
Cambiar la etapa desde la API se comporta exactamente igual que cambiarla a mano desde el dashboard:
- Queda registrado en el historial de estados del lead, con el motivo «El estado se cambió a través de la API de ZeroChats».
- Los Follow-Ups reaccionan a la nueva etapa como a cualquier otro cambio.
- Los CRMs conectados reciben su etiqueta de estado, y el Webhook personalizado recibe
lead.state_changed.
Mandar la etapa que el lead ya tiene no hace nada: no se registra ningún cambio ni se avisa a nadie.
addTags y removeTags
Sección titulada «addTags y removeTags»Listas de etiquetas que añadir o quitar, con un máximo de 50 en cada una. Cada elemento puede ser el id de la etiqueta o su nombre, sin distinguir mayúsculas.
Las etiquetas tienen que existir ya en tu negocio: la API nunca crea etiquetas. Si alguna referencia no se reconoce, la petición entera falla con un 400 que te dice cuáles, y no se cambia nada; ni siquiera las que sí existían. Crea la etiqueta desde la app y repite la llamada.
Las etiquetas que añades se comportan como las que asigna la IA: si la etiqueta tiene activada la notificación, se envía; los CRMs conectados y el Webhook personalizado reciben lead.tagged. Quitar una etiqueta es silencioso. Si mandas los dos campos, primero se añaden y después se quitan.
email y phone
Sección titulada «email y phone»Texto para escribir el dato, o null para borrarlo. Un email con un formato que no es un email es un 400.
Errores al editar
Sección titulada «Errores al editar»| Código | error.code | Cuándo |
|---|---|---|
| 404 | not_found | El lead no existe o no es de tu negocio. |
| 400 | invalid_request | Cuerpo vacío, JSON mal formado, un campo que no existe, una etapa que no está en la tabla, una etiqueta desconocida o un email inválido. |
Resolver el utm_user de un enlace
Sección titulada «Resolver el utm_user de un enlace»Mientras el Script de seguimiento de conversiones está activo, todos los enlaces que ZeroChats envía a un lead llevan un parámetro utm_user con un identificador cifrado del lead: un valor opaco de 22 caracteres que solo ZeroChats puede descifrar. GET /leads/resolve convierte ese valor en el lead, para que puedas reconocerlo desde tu propio servidor sin instalar el script.
curl -H "Authorization: Bearer zcst_tu_clave" \ "https://app.zerochats.com/api/v1/leads/resolve?utm_user=qdfomARk3BiETYsZgmtDbg"data es el lead, con la misma forma que GET /leads/{id}:
{ "success": true, "data": { "id": "3d51f0c2-9a18-4b7e-8c56-1e2d3f4a5b6c", "name": "Ada Lovelace", "username": "ada", "platform": "INSTAGRAM", "externalId": "17841400000000000", "phone": null, "state": "IN_PROGRESS", "tags": ["caliente"], "createdAt": "2026-08-11T09:12:30.000Z" }}Si el valor llega con una @ delante —utm_user=@qdfomARk3BiETYsZgmtDbg— se acepta igual. Y desactivar el seguimiento de conversiones no invalida nada: los valores de los enlaces enviados antes siguen resolviéndose.
Errores al resolver
Sección titulada «Errores al resolver»| Código | error.code | Cuándo |
|---|---|---|
| 400 | invalid_request | Falta utm_user, o el valor no es uno que ZeroChats produzca. El mensaje es utm_user: not a value produced by ZeroChats. |
| 404 | not_found | El seguimiento de conversiones nunca se ha activado en tu negocio —el mensaje te lo dice—, o el valor no corresponde a ningún lead tuyo: viene de los enlaces de otro negocio o está alterado. Este último caso se ve igual que un lead desconocido, a propósito. |
Integración personalizada
Sección titulada «Integración personalizada»El flujo completo, sin el script:
-
Captura el
utm_useren tu página. Léelo de la URL con la que aterriza el visitante y guárdalo en un campo oculto del formulario, o pásaselo a tu herramienta de reservas o de pago como dato extra. -
Mándalo a tu servidor. Con el envío del formulario, o dentro del aviso que tu herramienta emite cuando alguien reserva o paga.
-
Resuelve el lead. Desde el servidor,
GET /leads/resolve?utm_user=...con tu clave. Si recibes un404, el visitante no llegó desde un enlace de ZeroChats; registra el caso y termina el flujo. -
Edita el lead.
PATCH /leads/{id}con elidque acabas de recibir: la etapa, las etiquetas, el email o el teléfono que quieras guardar.
Ejemplo práctico: mover al lead cuando reserva
Sección titulada «Ejemplo práctico: mover al lead cuando reserva»Un caso completo en n8n o Make: tu herramienta de reservas —o tu pasarela de pago— avisa por webhook cada vez que alguien reserva, y quieres que ese lead pase a Agendado en ZeroChats con la etiqueta reservó puesta, sin que nadie entre en el dashboard.
-
Recibe el aviso de tu herramienta. Casi todas incluyen el email con el que la persona reservó; es lo que vas a usar para encontrarla.
-
Busca el lead por email. Un nodo HTTP Request con método
GETy URL:https://app.zerochats.com/api/v1/leads?email={{ $json.email }}Con la misma credencial de cabecera que usas para el resto de la API.
-
Comprueba que hay resultado. Si
dataviene vacío, la persona reservó con un email que no dejó en la conversación; registra el caso y termina el flujo. Si vienen varios, quédate con el primero: es el más reciente. -
Edita el lead. Otro HTTP Request, esta vez
PATCH, contrahttps://app.zerochats.com/api/v1/leads/{{ $json.data[0].id }}, con cabeceraContent-Type: application/jsony este cuerpo:{ "state": "BOOKED", "addTags": ["reservó"] } -
Listo. El lead aparece en Agendado, con la etiqueta puesta y el cambio en su historial. Si tienes un CRM conectado o el Webhook personalizado activo, reciben el cambio igual que si lo hubieras hecho a mano.
Para una pasarela de pago, el flujo es el mismo cambiando BOOKED por CLIENT. Y si tu herramienta te da el teléfono en vez del email, busca con phone en lugar de email.
Buenas prácticas
Sección titulada «Buenas prácticas»- Guarda el
id, no el usuario. El nombre de usuario de Instagram puede cambiar; elidno. Si tu flujo recibe el lead por webhook, ya tienes elidy te ahorras la búsqueda. - Trata la lista de búsqueda como lista. Aunque casi siempre traiga un resultado, decide qué hacer cuando traiga cero o varios antes de que pase en producción.
- Manda solo lo que cambia. Un
PATCHcon la etapa que el lead ya tiene no rompe nada, pero tampoco aporta; y unPATCHcon el email actual reescribe lo mismo. - Deja la etapa a la IA salvo que tengas mejor información. La API es para reflejar hechos que pasan fuera —una reserva, un pago—, no para mover leads por rutina desde un flujo.
Problemas comunes
Sección titulada «Problemas comunes»Recibo 404 con un id que tengo guardado
Sección titulada «Recibo 404 con un id que tengo guardado»O el id es de otro negocio —una clave solo ve sus propios leads— o el lead se ha eliminado desde la app. En ambos casos la respuesta es la misma a propósito. Comprueba que estás usando la clave del negocio correcto.
La búsqueda devuelve [] y sé que el lead existe
Sección titulada «La búsqueda devuelve [] y sé que el lead existe»Repasa el criterio:
- Con
username, el nombre de usuario tiene que ser exacto: sin@, sin espacios y con la ortografía de la plataforma. - Con
email, tiene que ser el email tal como lo escribió el lead en la conversación. Si lo dio con una errata, la búsqueda necesita la misma errata. - Con
phone, incluye el prefijo de país.600111222no encuentra a un lead guardado como34600111222.
Recibo 400 al añadir una etiqueta
Sección titulada «Recibo 400 al añadir una etiqueta»El mensaje te dice qué referencias no se han reconocido. La API no crea etiquetas: créala primero desde la app, con el mismo nombre que usas en el flujo, y vuelve a lanzar la llamada. Hasta entonces no se cambia nada en el lead, ni siquiera el resto de campos del mismo PATCH.