API público y webhooks
La API REST permite que su CRM, el backend de su sitio web o su propio bot trabajen con los leads y diálogos de cenaly.com igual que lo hace el panel: leer fichas, cambiar propiedades, enviar eventos, responder en diálogos. Los webhooks, en cambio, informan por sí mismos a su servidor sobre nuevos leads, eventos y mensajes.
Qué se necesita para empezar#
| Qué | Dónde |
|---|---|
| Rol | propietario de la cuenta — la clave actúa en su nombre; los empleados no ven las claves |
| Sección | chat habilitado (los leads y diálogos viven en él) |
| Clave | Integraciones → API y webhooks → Claves → Crear clave |
| Dirección de la API | https://api.cenaly.com/public-api/v1 |
| Referencia de métodos | api.cenaly.com/public-api/v1/docs |
| OpenAPI 3.1 | api.cenaly.com/public-api/v1/openapi.json |
Paso 1. Clave y permisos#
- Abra Integraciones y pulse API y webhooks en la cabecera de la página.
- En la pestaña Claves, pulse Crear clave, introduzca un nombre («CRM», «Backend del sitio», «Bot de Telegram»).
- Elija un conjunto de permisos o marque los permisos uno a uno; si hay varios locales, indique a qué locales tiene acceso la clave, y el plazo.
- Copie el secreto
cnk_…. Se muestra una sola vez: solo guardamos su huella (SHA-256).
| Permiso | Qué permite |
|---|---|
contacts:read |
lista, búsqueda y ficha del lead, propiedades |
contacts:write |
cambiar propiedades y etiquetas, fusionar, eliminar a petición del interesado |
events:read |
flujo de eventos del lead |
events:write |
enviar eventos (compra, registro, cualquiera propio) |
conversations:read |
diálogos y mensajes |
conversations:write |
respuesta del operador, nota, estado, responsable, etiquetas |
messages:send |
mensaje al lead en su último diálogo |
segments:read |
segmentos guardados y sus participantes |
Conjuntos: CRM / backend (leads, propiedades, eventos, lectura de diálogos y segmentos), Bot propio (lee leads, responde en diálogos, escribe a leads), Solo lectura, Todo.
- Permisos cambia los permisos y los locales de la clave al vuelo — la integración sigue funcionando.
- Reemitir emite un nuevo secreto; el antiguo deja de funcionar de inmediato, los permisos y el registro se mantienen.
- Revocar — de inmediato y para siempre; la siguiente solicitud recibirá
401.
La clave se transmite en la cabecera Authorization: Bearer cnk_… (o X-Api-Key: cnk_…). Guárdela solo en el servidor — no en el navegador ni en la aplicación móvil.
Paso 2. Primeras solicitudes#
KEY=cnk_ваш_ключ
API=https://api.cenaly.com/public-api/v1
# что умеет ключ: права, точки, лимит
curl -H "Authorization: Bearer $KEY" "$API/me"
# лиды, свежие сверху (20 на страницу)
curl -H "Authorization: Bearer $KEY" "$API/contacts?limit=20"
# поиск по имени, почте или телефону
curl -H "Authorization: Bearer $KEY" "$API/contacts?q=anna"
La respuesta es siempre un sobre:
{
"meta": { "status": 200, "requestId": "…", "nextCursor": "WyIyMDI2LTEwLTA0…" },
"data": { "total": 128, "contacts": [ { "id": "…", "status": "lead", "name": "Анна", "updatedAt": "2026-10-04T09:12:00Z" } ] }
}
La página siguiente es la misma solicitud con cursor=<meta.nextCursor>; si no hay nextCursor, no hay más páginas. Error — {"meta": {...}, "error": {"code": "…"}}.
Local. Si la clave tiene acceso a un solo local, no hace falta indicarlo. Si hay varios, añada location=<id del local> (la lista — GET /v1/locations); de lo contrario, la respuesta será 400 location_required.
Propiedades y eventos por su user_id. No es obligatorio guardar nuestro id del lead: añada ?by=user_id y la dirección pasará a ser su identificador de usuario. Si aún no existe un lead con ese user_id, lo crearemos.
# свойства: операции update_or_create, set_once, add, delete, append, union, exclude
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"ops":[{"op":"update_or_create","key":"$email","value":"anna@example.com"},
{"op":"add","key":"orders","value":1},
{"op":"union","key":"interests","value":"yoga"}]}' \
"$API/contacts/u-1001/props?by=user_id"
# события пачкой (до 100), повтор с тем же id засчитывается один раз
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"events":[{"id":"ord-5531","userId":"u-1001","name":"order_paid","props":{"sum":45,"currency":"GEL"}}]}' \
"$API/events"
# ответ в диалоге и смена статуса
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"text":"Здравствуйте! Заказ передан курьеру."}' "$API/conversations/<id>/reply"
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"status":"resolved"}' "$API/conversations/<id>/status"
El registro pasa por la misma cola que las ediciones del panel: los cambios se aplican en segundos, y la respuesta a una escritura por user_id es 202 con pending. En la ficha del lead, el autor de la edición aparece como «API:
Métodos#
| Método | Qué hace | Permiso |
|---|---|---|
GET /v1/me, GET /v1/locations |
clave, permisos, locales | — |
GET /v1/contacts |
leads: status (anon, lead, user), q, limit, cursor |
contacts:read |
GET /v1/contacts/{id} |
ficha: propiedades, etiquetas, resumen de eventos | contacts:read |
POST /v1/contacts/{id}/props |
propiedades mediante operaciones (hasta 250) o {"props":{…}} |
contacts:write |
POST / DELETE /v1/contacts/{id}/tags |
etiquetas del lead | contacts:write |
POST /v1/contacts/merge |
fusión de dos fichas: vista previa y luego "apply": true |
contacts:write |
POST /v1/contacts/{id}/erase |
eliminación a petición del interesado ("confirm": true) |
contacts:write |
GET /v1/contacts/{id}/events |
flujo de eventos del lead | events:read |
POST /v1/contacts/{id}/events, POST /v1/events |
eventos (hasta 100 a la vez) | events:write |
GET /v1/contacts/{id}/conversations |
conversaciones del lead | conversations:read |
POST /v1/contacts/{id}/messages |
escribir al lead en su última conversación | messages:send |
GET /v1/conversations, /{id}, /{id}/messages |
conversaciones y mensajes | conversations:read |
POST /v1/conversations/{id}/reply |
respuesta del operador o nota ("type": "note") |
conversations:write |
POST /v1/conversations/{id}/status, /assign, /tags |
estado, responsable, etiquetas | conversations:write |
GET /v1/segments, GET /v1/segments/{id}/contacts |
segmentos y sus participantes | segments:read |
Los campos, ejemplos y códigos de error de cada método están en el manual de referencia.
Paso 3. Webhooks#
Los webhooks se configuran en el local: Chat → Ajustes → Webhooks — dirección https, eventos y secreto de firma. A los eventos de conversación (nueva conversación, nuevo mensaje, estado, valoración, etiquetas) se han añadido los eventos de lead:
| Evento | Cuándo llega |
|---|---|
lead.created |
ha aparecido un nuevo lead (contacto dejado en el chat, formulario o creado a través del API) |
lead.props_changed |
han cambiado las propiedades del lead — la lista de claves está en changedProps |
lead.event_tracked |
el lead tiene un evento; por defecto, todos sus eventos sin los del sistema ($…), la lista de nombres se define con el campo «Qué eventos enviar» |
message.goal_reached |
el lead ha alcanzado el objetivo del mensaje (pop-up, envío masivo) |
{
"event": "lead.event_tracked",
"deliveryId": "WD…",
"at": "2026-10-04T09:12:03Z",
"webhookId": "…",
"locationId": "…",
"contact": { "id": "…", "status": "user", "userId": "u-1001", "props": { "orders": 3 }, "tags": ["vip"] },
"leadEvent": { "name": "order_paid", "at": "2026-10-04T09:12:00Z", "props": { "sum": 45 } }
}
El nombre, el correo y el teléfono del lead llegan al cuerpo solo si el webhook tiene marcada la casilla «Transmitir los contactos del cliente». La verificación de la firma es HMAC-SHA256 del cuerpo con el secreto del webhook:
# заголовок X-Meni-Signature: sha256=<hex>
echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET"
Responda 2xx en 5 segundos — de lo contrario, la entrega se repetirá; tras una larga serie de fallos, el webhook se desactiva y esto se ve en los ajustes. Filtre los duplicados por deliveryId.
Limitaciones#
- 60 solicitudes por minuto por clave; por encima —
429con la cabeceraRetry-After. El resto — enX-RateLimit-*. - Hasta 200 registros por página (por defecto 50), hasta 100 eventos por lote, hasta 250 operaciones con propiedades por solicitud, hasta 30 etiquetas a la vez.
- Hasta 20 claves activas por cuenta.
- El registro de llamadas de la clave se conserva 30 días: hora, método, plantilla de ruta (sin id ni cuerpo), código de respuesta, duración.
- Los eventos enviados a través del API se guardan en la ficha del lead (flujo, resumen, segmentos), pero no en el registro sin procesar de la analítica de visitas.
- Solo se puede escribir al lead en una conversación existente: si no hay conversación — respuesta
409 no_conversation.
Solución de problemas#
| Respuesta | Qué significa | Qué hacer |
|---|---|---|
401 api_key_missing / api_key_invalid |
la clave no está en la solicitud, o es incorrecta, ha sido revocada o ha caducado | compruebe la cabecera; si es necesario, vuelva a emitir la clave |
403 scope_required |
la clave no tiene el permiso necesario | Permisos de la clave — añada el permiso |
400 location_required |
la clave tiene acceso a varios locales | añada location=<id> |
404 location_not_found |
el local no está entre los locales de la clave | compruébelo con GET /v1/locations |
404 not_found |
el lead o la conversación no existen | para sus propios id utilice ?by=user_id |
429 rate_limited |
más de 60 solicitudes por minuto | espere Retry-After segundos, agrupe los eventos en lotes |
502 upstream_error |
fallo temporal | repita la solicitud más tarde |
Cada solicitud se ve en la pestaña Registro de llamadas — con el código de respuesta y el nombre de la clave.
Preguntas frecuentes#
¿Se puede llamar al API desde el navegador? No: la clave da acceso a todos los leads del local. Llame al API desde su propio servidor.
¿En qué se diferencia user_id del id del lead? El id del lead es nuestro identificador de la ficha. user_id es su identificador de usuario (número en el CRM, id de la cuenta en el sitio web); con ?by=user_id se puede dirigir la ficha por él.
¿Qué ocurre al reenviar un evento? El evento con el mismo id se cuenta una sola vez — puede repetir la solicitud sin problema en caso de error de red.
¿Hace falta una clave distinta para cada local? No: una clave puede trabajar con varios locales, el local se elige con el parámetro location.
Secciones relacionadas#
- Chat con clientes — conversaciones, leads y ajustes de webhooks
- Solicitudes — flujo de entrada unificado
- API y MCP de su centralita — API de telefonía independiente
- Integraciones