Contactos
Busca, crea y gestiona los contactos de tu empresa. Cada persona que interactua con tu chatbot o agentes se convierte en un contacto.
En Cliengo, un contacto representa a una persona que interactuo con tu negocio a traves de cualquier canal: el chat web de tu sitio, WhatsApp, Facebook Messenger o Instagram. Cuando un visitante deja sus datos (nombre, email, telefono) en una conversacion, Cliengo crea automaticamente un contacto asociado. Tambien podes crear contactos manualmente via API para importar leads desde otras fuentes.
Los contactos son el eje central del CRM de Cliengo: podes asignarles agentes, etiquetarlos, cambiar su estado segun avancen en tu pipeline de ventas, y consultar todo su historial de interacciones.
Buscar contactos
El endpoint GET /v1/contacts soporta busqueda fuzzy y filtros avanzados. La busqueda fuzzy significa que no necesitas escribir el nombre exacto — con una parte o una aproximacion alcanza. Podes combinar cualquier cantidad de filtros para encontrar exactamente los contactos que necesitas.
Busqueda simple
curl "https://connect.cliengo.com/v1/contacts?search=juan" \ -H "Authorization: Bearer TU_TOKEN"
Busca en nombre, email y telefono de forma fuzzy (no necesita match exacto).
Filtros avanzados
| Parametro | Tipo | Descripcion |
|---|---|---|
search | string | Busqueda fuzzy en nombre, email, telefono |
email | string | Filtrar por email (parcial) |
name | string | Filtrar por nombre (parcial) |
phone | string | Filtrar por telefono (parcial) |
status | enum | new, active, client, long_term |
websiteId | string | IDs de canal separados por coma |
assignedTo | string | ID del agente o UNASSIGNED |
entryMethod | string | WEB, WHATSAPP, FACEBOOK, INSTAGRAM |
conversationTags | string | Tags separados por coma |
dateFrom / dateTo | date-time | Rango de fecha de creacion (ISO 8601) |
page | integer | Pagina (default 1) |
limit | integer | Resultados por pagina |
orderBy | string | Campo para ordenar (default _id) |
order | enum | asc o desc (default desc) |
Ejemplo con filtros combinados
curl "https://connect.cliengo.com/v1/contacts?status=new&entryMethod=WHATSAPP&dateFrom=2026-03-01T00:00:00Z&limit=50" \ -H "Authorization: Bearer TU_TOKEN"
Crear y actualizar contactos
Ademas de los contactos que Cliengo crea automaticamente desde las conversaciones, podes crear contactos via API. Esto es util para importar leads desde un CRM externo, un formulario de tu sitio, o cualquier otra fuente de datos.
Crear un contacto
curl -X POST https://connect.cliengo.com/v1/contacts \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"websiteId": "TU_CHANNEL_ID",
"name": "Maria Lopez",
"email": "[email protected]",
"phone": "+5491155551234",
"status": "new"
}'
Campos obligatorios: websiteId y name. Opcionalmente podes incluir email, phone, message, status, assignedTo, note, y customFields (campos personalizados como objeto).
Actualizar un contacto
curl -X PATCH https://connect.cliengo.com/v1/contacts/CONTACT_ID \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Maria Lopez Garcia",
"status": "client"
}'
Solo se actualizan los campos enviados; el resto se mantiene.
Eliminar contactos
curl -X DELETE https://connect.cliengo.com/v1/contacts/CONTACT_ID \ -H "Authorization: Bearer TU_TOKEN"
curl -X POST https://connect.cliengo.com/v1/contacts/bulk-delete \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"contacts": ["CONTACT_ID_1", "CONTACT_ID_2"]}'
Tags y notas
Los tags son etiquetas libres que podes asignar a un contacto para clasificarlo (ej: "vip", "interesado-plan-premium", "requiere-seguimiento"). Las notas son comentarios internos visibles solo para tu equipo — ideales para dejar registro de llamadas, acuerdos, o cualquier contexto relevante sobre el contacto.
Agregar tag
curl -X POST https://connect.cliengo.com/v1/contacts/CONTACT_ID/tags \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"tag": "vip"}'
Agregar nota
curl -X POST https://connect.cliengo.com/v1/contacts/CONTACT_ID/notes \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "Cliente interesado en plan premium"}'
Ver historial del contacto
curl https://connect.cliengo.com/v1/contacts/CONTACT_ID/history \ -H "Authorization: Bearer TU_TOKEN"
Devuelve logs y notas combinados en orden cronologico.
Estados de un contacto
Cada contacto tiene un status que representa en que etapa del ciclo de vida comercial se encuentra. Esto permite a tu equipo priorizar la gestion y hacer seguimiento del avance de cada lead:
| Estado | Descripcion |
|---|---|
new | Contacto recien creado, sin gestion |
active | En gestion activa por el equipo |
client | Convertido en cliente |
long_term | Seguimiento a largo plazo |
El cambio de estado dispara el evento contact/status_changed si tenes un webhook configurado.
Origen y atribucion de leads
Cada contacto y cada conversacion traen datos de origen y canal de adquisicion, para que puedas medir la efectividad de cada canal y distinguir el trafico organico del pago.
Campos disponibles
| Campo | Disponible en | Descripcion |
|---|---|---|
entryMethod | contacto | Canal de ingreso: WEB, WHATSAPP, FACEBOOK, INSTAGRAM. |
channel | conversacion | Canal de la conversacion (mismo conjunto de valores). |
utmSource | contacto y conversacion | Fuente de la campaña (ej. google, facebook). |
utmMedium | contacto y conversacion | Medio (ej. cpc, organic, social). |
utmCampaign | contacto y conversacion | Nombre de la campaña. |
gclid | contacto | Google Click ID — su presencia indica trafico de Google Ads (pago). |
gaClientId | conversacion | Google Analytics Client ID. |
conversionUrl | contacto | Pagina donde se genero el lead. |
refererTracking | contacto | Referer completo de origen. |
device | contacto | Dispositivo de origen. |
Filtrar conversaciones por campaña
El listado de conversaciones permite filtrar por los parametros UTM:
curl "https://connect.cliengo.com/v1/conversations?utmSource=facebook&utmMedium=cpc" \ -H "Authorization: Bearer TU_TOKEN"
Distinguir organico vs pago
Con estos campos podes clasificar el origen del lado del cliente y cruzarlo con la etapa de conversion (status), oportunidades y ventas. Heuristica simple:
| Origen | Como detectarlo |
|---|---|
| Pago | gclid presente, o utmMedium en (cpc, paid, ads), o utmSource de una plataforma de ads. |
| Organico | Sin gclid y sin UTMs de pago (utmMedium vacio u organic). |
| Canal | entryMethod / channel (WhatsApp, Web, Instagram, Facebook). |
Nota: hoy el filtrado por UTM/gclid esta disponible a nivel conversaciones. En contactos, los campos de atribucion vienen en la respuesta pero el filtrado por UTM/gclid todavia no esta soportado (en contactos podes filtrar por entryMethod). La clasificacion automatica "organico vs pago" y la distincion de Click-to-WhatsApp entre Instagram y Facebook estan en evaluacion como mejora de backend.
Conversaciones y Mensajes
Las conversaciones son el nucleo de Cliengo. Cada interaccion con un visitante — por Web, WhatsApp, Facebook o Instagram — es una conversacion con su historial de mensajes.
Una conversacion en Cliengo representa un hilo de comunicacion entre un visitante y tu equipo. Puede originarse en el chat web de tu sitio, en WhatsApp, Facebook Messenger o Instagram Direct. Cada conversacion tiene participantes (el visitante, agentes humanos, y/o el chatbot), un historial de mensajes, y un estado (abierta o cerrada).
Las conversaciones se pueden asignar a agentes especificos, transferir entre operadores, clasificar con tags, y organizar en fases de un pipeline visual (como un CRM tipo kanban). Cuando un visitante deja datos de contacto en la conversacion, Cliengo asocia automaticamente un contacto.
Listar y filtrar conversaciones
curl "https://connect.cliengo.com/v1/conversations?status=opened&channel=WHATSAPP&limit=20" \ -H "Authorization: Bearer TU_TOKEN"
Filtros disponibles
| Parametro | Tipo | Descripcion |
|---|---|---|
status | enum | opened, closed, inactive |
channel | string | WEB, WHATSAPP, FACEBOOK, INSTAGRAM (separados por coma) |
tags | string | Tags separados por coma |
agent | string | ID del agente que intervino la conversacion (ultimo operador). Trae las conversaciones intervenidas por ese agente. Usa robot para las atendidas solo por el bot. |
preAssigned | string | ID del agente asignado (pre-asignacion). Trae las conversaciones asignadas a ese agente. |
condition | string | Estado de intervencion (separados por coma): intervened, not_intervened, lead. |
phase | string | ID de la fase del pipeline |
search | string | Busqueda libre |
fromDate / toDate | date-time | Rango de fechas (ISO 8601) |
sort | string | Ej: createdAt:desc |
limit, offset | integer | Paginacion |
Conversaciones de un contacto
curl https://connect.cliengo.com/v1/conversations/contact/CONTACT_ID \ -H "Authorization: Bearer TU_TOKEN"
Conversaciones por fase
curl https://connect.cliengo.com/v1/conversations/by-phase \ -H "Authorization: Bearer TU_TOKEN"
Agrupa las conversaciones por fase del pipeline. Util para dashboards tipo kanban.
Metricas de asignacion e intervencion
Connect expone los datos necesarios para medir quien atiende cada conversacion y construir metricas de SLA (tiempos de gestion, primera respuesta humana, seguimiento por agente) sin interpretar logs manualmente.
Asignacion vs. intervencion
Son dos conceptos distintos y cada uno tiene su propio campo y filtro:
| Concepto | Campo en la conversacion | Filtro | Significado |
|---|---|---|---|
| Asignado | preAssigned | ?preAssigned=AGENT_ID | Agente al que se le asigno la conversacion (pre-asignacion automatica), haya intervenido o no. |
| Intervenido | persistentLastOperator | ?agent=AGENT_ID | Ultimo agente humano que envio un mensaje en la conversacion. |
| Asignado actual | assignedTo | — | Agente actualmente asignado (vacio si nadie la tiene). |
Listar conversaciones por agente
# Conversaciones INTERVENIDAS por un agente curl "https://connect.cliengo.com/v1/conversations?agent=AGENT_ID" \ -H "Authorization: Bearer TU_TOKEN" # Conversaciones ASIGNADAS a un agente curl "https://connect.cliengo.com/v1/conversations?preAssigned=AGENT_ID" \ -H "Authorization: Bearer TU_TOKEN" # Solo las que tuvieron intervencion humana (cualquier agente) curl "https://connect.cliengo.com/v1/conversations?condition=intervened" \ -H "Authorization: Bearer TU_TOKEN"
Fecha y hora de intervencion humana
Cada conversacion trae el array participants, donde cada participante incluye firstMessageAt y lastMessageAt. Los participantes humanos tienen type igual a user (operador interno) o externalOperator (operador externo). De ahi se derivan los dos datos clave para SLA:
| Metrica | Como obtenerla |
|---|---|
| Fecha y hora de intervencion humana | El firstMessageAt mas temprano entre los participantes humanos (user/externalOperator) — el momento en que el primer agente intervino. |
| Fecha y hora del ultimo mensaje del agente que intervino | El lastMessageAt mas reciente entre los participantes humanos — corresponde al persistentLastOperator, que por definicion es el ultimo agente en enviar un mensaje. |
// conversation = un item del listado de /v1/conversations const humano = conversation.participants .filter(p => p.type === 'user' || p.type === 'externalOperator'); // Fecha y hora de la primera intervencion humana const intervencionHumanaAt = humano .map(p => p.firstMessageAt) .filter(Boolean) .sort()[0]; // Fecha y hora del ultimo mensaje del agente que intervino const ultimoMsgAgente = humano .map(p => p.lastMessageAt) .filter(Boolean) .sort() .at(-1);
Tip: combinando ?agent=AGENT_ID (o ?preAssigned=AGENT_ID) con fromDate/toDate podes armar el listado de gestion de cada agente en un rango, y con firstMessageAt/lastMessageAt calcular tiempos de primera respuesta y de gestion.
Mensajes
Cada conversacion tiene un historial de mensajes ordenado cronologicamente. Los mensajes pueden ser de texto plano, imagenes, o notas internas. Podes leer todo el historial y enviar nuevos mensajes en nombre de un agente o un bot.
Leer mensajes
curl https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/messages \ -H "Authorization: Bearer TU_TOKEN"
Enviar un mensaje
curl -X POST https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/messages \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from": "TU_USER_ID",
"fromType": "user",
"type": "text/plain",
"body": "Hola, gracias por contactarnos!"
}'
Tipos de mensaje
| fromType | Descripcion |
|---|---|
user | Mensaje enviado por un agente |
robot | Mensaje enviado por el chatbot |
visitor | Mensaje del visitante/cliente |
Enviar imagen
curl -X POST https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/messages \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from": "TU_USER_ID",
"fromType": "user",
"type": "image",
"message": {
"url": "https://ejemplo.com/foto.jpg",
"caption": "Foto del producto"
}
}'
Asignar, cerrar y notas
Estas operaciones son las que tu equipo de atencion usa dia a dia: asignar conversaciones a agentes especificos, transferirlas cuando otro miembro del equipo puede resolver mejor, cerrarlas cuando se completo la gestion, y dejar notas internas con contexto para el equipo.
Asignar a un agente
curl -X POST https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/assign \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"assignedTo": "AGENT_USER_ID"}'
Transferir a otro agente
curl -X POST https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/transfer \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"transferTo": "OTRO_AGENT_ID"}'
Cerrar conversacion
curl -X POST https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/close \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"isLead": true}'
El campo isLead indica si la conversacion genero un contacto valido.
Reabrir conversacion
curl -X POST https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/open \ -H "Authorization: Bearer TU_TOKEN"
Agregar nota interna
curl -X POST https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/notes \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "El cliente pregunto por el plan enterprise"}'
Las notas son internas — visibles solo para el equipo, no para el visitante.
Fases del pipeline
Cliengo incluye un pipeline visual donde las conversaciones se organizan en fases (columnas tipo kanban). Cada empresa configura sus propias fases — por ejemplo: "Nuevo", "En negociacion", "Propuesta enviada", "Cerrado ganado", "Cerrado perdido". Mover una conversacion de fase es la forma principal de hacer seguimiento comercial.
Asignar fase
curl -X PATCH https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/phases/PHASE_ID \ -H "Authorization: Bearer TU_TOKEN"
Ver fases disponibles
curl https://connect.cliengo.com/v1/phases \ -H "Authorization: Bearer TU_TOKEN"
Cada fase tiene id, name, order, y type (progress, success, lost).
El cambio de fase dispara el evento conversation/phase_changed si tenes un webhook configurado.
Inbox: datos y conceptos para reportes
El Inbox es donde tu equipo gestiona las conversaciones. Si vas a construir reportes (por ejemplo con una IA generadora de apps), conviene entender qué significa cada concepto y dónde vive cada dato, porque algunos están en la conversación y otros en el contacto asociado.
Glosario
| Concepto | Qué es | Dónde está |
|---|---|---|
| Conversación | Un hilo de mensajes con un visitante por un canal (Web, WhatsApp, etc.). | GET /v1/conversations |
| Etapa de inbox | La fase del pipeline donde está la conversación (ej. "Nuevos", "Cotizados", "Pedidos"). En la conversación viene como ID (phaseId), no como nombre. | phaseId en la conversación → nombre con GET /v1/phases |
| Etiquetas de usuario / bot | Las etiquetas de negocio que pone un agente, el bot o la IA (ej. "cotizado", "venta cerrada", "roller"). | operatorTags en la conversación (con source) |
| Etiquetas internas de Cliengo | Etiquetas técnicas que el sistema agrega solo (no son de negocio). | aparecen en el array tags — ver más abajo |
| Valor monetario | El monto del negocio cargado en la conversación. | amount en la conversación |
| Puntuación del lead | Las estrellas (1–5) del lead. | rating en el contacto (no en la conversación) |
| Asignación / intervención | Quién tiene y quién atendió la conversación. | assignedTo / preAssigned / persistentLastOperator (ver Métricas de agentes) |
Etiquetas: de usuario/bot vs. internas de Cliengo
Esta es la confusión más común. El array tags (formato plano, legacy) mezcla tus etiquetas de negocio con etiquetas internas/sistémicas de Cliengo. Las internas siguen patrones reconocibles:
- Prefijos
posted_(ej.posted_email,posted_phone,posted_international_phone) yfired_(ej.fired_new_lead). - Flags de estado:
is_intervened,was_intervened,automatic_transfer,external_robot,post_lead,no_lead.
Recomendado: usá operatorTags en vez de tags. Cada item trae tagName y source (human = agente, chatbot = flujo del bot, ai = auto-etiquetado), y no incluye las internas. Si necesitás usar tags, filtrá las internas:
// Opción A (recomendada): etiquetas de negocio desde operatorTags const etiquetas = (conversation.operatorTags || []).map(t => t.tagName); // Opción B: limpiar el array legacy `tags` const INTERNAS = /^(posted_|fired_|is_intervened|was_intervened|automatic_transfer|external_robot|post_lead|no_lead)/; const tags = Array.isArray(conversation.tags) ? conversation.tags : []; const etiquetasUsuario = tags.filter(t => !INTERNAS.test(t));
Cuidado con el formato: validá que tags sea un arreglo antes de iterarlo (Array.isArray(...)). Si lo recorrés sin chequear podés toparte con (tags || []) is not iterable.
Valor monetario por etapa
El monto del negocio está en amount de cada conversación (puede ser null o 0 si no se cargó). Para sumarlo por etapa, agrupá por phaseId y resolvé el nombre con /v1/phases:
// phases = GET /v1/phases → { id: name }
const nombreEtapa = Object.fromEntries(phases.map(p => [p.id, p.name]));
const montoPorEtapa = {};
for (const c of conversaciones) {
const etapa = nombreEtapa[c.phaseId] || 'Sin etapa';
montoPorEtapa[etapa] = (montoPorEtapa[etapa] || 0) + (Number(c.amount) || 0);
}
Puntuación (estrellas) del lead
El rating (1–5 estrellas, 0 = sin puntuar) vive en el contacto, no en la conversación. Traé el contacto de la conversación por su contactId:
# el rating viene en el contacto curl "https://connect.cliengo.com/v1/contacts?limit=100" \ -H "Authorization: Bearer TU_TOKEN" # cruzá conversation.contactId con contact.id y leé contact.rating
Gotchas de paginación y formato (clave si traés todo el historial):
- El
limitmáximo por request es 100; pedir más no trae más. Paginá conoffset(opage) de a 100 hasta agotar. - El listado de conversaciones devuelve un objeto bajo
dataindexado por ID, no un arreglo. Recorrelo conObject.values(resp.data). - Respetá el rate limit según tu plan; ante
429, reintentá con backoff.
WhatsApp Templates (HSM)
Envia mensajes proactivos a tus clientes via WhatsApp usando templates pre-aprobados por Meta. Ideal para seguimiento, recordatorios y campañas.
WhatsApp tiene reglas estrictas sobre que mensajes pueden enviar las empresas. Existen dos tipos de comunicacion:
Mensajes dentro de la ventana de 24 horas: Cuando un cliente te escribe, tenes 24 horas para responder libremente con cualquier mensaje. Pasadas las 24 horas, la ventana se cierra.
Mensajes fuera de la ventana (templates/HSM): Para contactar a un cliente despues de las 24 horas, necesitas usar un template pre-aprobado por Meta. Estos templates tienen un formato fijo con variables reemplazables y deben pasar un proceso de revision antes de poder usarlos.
Templates
Los templates de WhatsApp (tambien llamados HSM — Highly Structured Messages) son mensajes con un formato aprobado por Meta. Cada template pertenece a una categoria que determina su uso permitido, y puede incluir variables ({{1}}, {{2}}) que se reemplazan con datos reales al momento del envio.
Categorias
| Categoria | Uso | Ejemplo |
|---|---|---|
UTILITY | Notificaciones transaccionales | Confirmacion de pedido, estado de envio |
MARKETING | Promociones y campanas | Ofertas, descuentos, newsletters |
AUTHENTICATION | Verificacion de identidad | Codigos OTP, confirmacion de cuenta |
Listar canales WhatsApp
curl https://connect.cliengo.com/v1/whatsapp \ -H "Authorization: Bearer TU_TOKEN"
Devuelve los canales de tu cuenta que tienen WhatsApp configurado. Necesitas el channelId para las operaciones siguientes.
Listar templates
curl https://connect.cliengo.com/v1/whatsapp/CHANNEL_ID/templates \ -H "Authorization: Bearer TU_TOKEN"
Respuesta:
{
"result": [
{
"id": 170,
"elementName": "promo_belleza_personalizada",
"status": "APPROVED",
"category": "MARKETING",
"text": "Hola {name}, tenemos una oferta especial para vos!",
"language": "es_AR",
"components": [...]
}
]
}
Solo los templates con status: "APPROVED" pueden usarse para enviar mensajes. Los templates PENDING estan en revision por Meta y los REJECTED fueron rechazados.
Crear template
curl -X POST https://connect.cliengo.com/v1/whatsapp/CHANNEL_ID/templates \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "seguimiento_venta",
"language": "es_AR",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Hola {{1}}, tu pedido #{{2}} esta en camino."
}
]
}'
El template se envia a Meta para aprobacion. El proceso puede tardar de minutos a horas.
Enviar mensajes
Una vez que tenes un template aprobado, podes enviar mensajes a contactos existentes de Cliengo (usando su ID) o directamente a un numero de telefono. En ambos casos necesitas el channelId de tu linea de WhatsApp.
A contactos de Cliengo
curl -X POST https://connect.cliengo.com/v1/whatsapp/CHANNEL_ID/messages \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"templateId": "170",
"contacts": ["CONTACT_ID_1", "CONTACT_ID_2"]
}'
A un numero directo (notificacion)
curl -X POST https://connect.cliengo.com/v1/whatsapp/CHANNEL_ID/notifications \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"templateId": "170",
"destination": "+5491155551234",
"variables": {
"1": "Maria",
"2": "ORD-5678"
}
}'
Las variables reemplazan los placeholders {{1}}, {{2}} del template.
Ventana de 24 horas: Para enviar mensajes libres (no-template) a un contacto, la conversacion debe tener actividad en las ultimas 24 horas. Fuera de esa ventana, solo podes usar templates aprobados.
Campañas
Las campañas permiten enviar un template masivamente a una lista de contactos. Connect expone las campañas en modo solo lectura: podes consultar su estado y su reporte detallado de envios (enviado, entregado, leido, fallido).
La creacion de campañas no se expone por la API publica. Las campañas se crean desde el asistente de WhatsApp del panel de Cliengo (la carga de la lista de contactos es parte de ese asistente). Por API, la forma de enviar un HSM es enviar un mensaje de template con POST /v1/whatsapp/CHANNEL_ID/template-messages (individual o a un numero directo). Los endpoints de crear lista, crear campaña, cancelar y reenviar devuelven 404.
Estados de campana
| Estado | Descripcion |
|---|---|
SCHEDULED | Programada para envio futuro |
SENDING | En proceso de envio |
SENT | Todos los mensajes enviados |
STOPPED | Detenida por error o limite |
CANCELLED | Cancelada manualmente |
Ver reporte de campana
curl https://connect.cliengo.com/v1/whatsapp/CHANNEL_ID/campaigns/CAMPAIGN_ID/full \ -H "Authorization: Bearer TU_TOKEN"
Devuelve el detalle de la campana con contadores por estado (sent, delivered, read, failed) y el detalle de cada mensaje individual. Tambien disponible en version resumida con /simple en lugar de /full.
Webhooks
Recibe notificaciones en tiempo real cuando ocurren eventos en tus conversaciones y contactos.
Que son los webhooks
Los webhooks permiten que tu servidor reciba notificaciones automaticas cuando ocurren eventos en Cliengo. En vez de hacer polling periodico a la API, Cliengo envia un POST HTTP a una URL que vos configuras cada vez que pasa algo relevante — por ejemplo, cuando se crea una conversacion nueva o un contacto cambia de estado.
Configuracion rapida
1. Crear un endpoint en tu servidor
Tu servidor necesita un endpoint HTTP que acepte POST y responda 200 OK:
app.post('/webhook', (req, res) => {
const { event, data, test } = req.body;
// Ignorar eventos de prueba si queres
if (test) {
return res.status(200).json({ received: true, test: true });
}
console.log(`Evento recibido: ${event}`, data);
// Responder 200 inmediatamente
res.status(200).json({ received: true });
// Procesar el evento de forma asincrona
processEvent(event, data).catch(console.error);
});
2. Crear el webhook via API
curl -X POST https://connect.cliengo.com/v1/webhooks \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tu-servidor.com/webhook",
"eventTypes": ["conversation/created", "contact/created"],
"retryCount": 5
}'
Respuesta:
{
"id": "60a1b2c3d4e5f6a7b8c9d0e1",
"url": "https://tu-servidor.com/webhook",
"eventTypes": ["conversation/created", "contact/created"],
"isActive": true,
"secret": "whsec_aBcDeFgHiJkLmNoPqRsTuVwXyZ123456",
"retryCount": 5,
"customHeaders": {},
"createdAt": "2026-03-29T12:00:00.000Z",
"updatedAt": "2026-03-29T12:00:00.000Z"
}
3. Verificar conectividad
Antes o despues de crear el webhook, podes probar que tu URL es accesible:
curl -X POST https://connect.cliengo.com/v1/webhooks/ping \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://tu-servidor.com/webhook"}'
Eventos disponibles
Los eventos siguen el formato categoria/accion. Podes suscribirte a cualquier combinacion.
Conversaciones (11 eventos)
| Evento | Se dispara cuando... |
|---|---|
conversation/created | Se crea una nueva conversacion (visitante inicia chat) |
conversation/updated | Se actualizan datos de la conversacion (nombre, telefono, etc.) |
conversation/deleted | Se elimina una conversacion |
conversation/operator_assigned | Se asigna o transfiere un agente a la conversacion |
conversation/message | Se envia o recibe un mensaje en la conversacion |
conversation/note | Se crea una nota interna en la conversacion |
conversation/archived | Se cierra/archiva la conversacion |
conversation/contact | Se asocia un contacto a la conversacion |
conversation/phase_changed | La conversacion cambia de fase en el pipeline |
conversation/tag_added | Se agrega una etiqueta a la conversacion |
conversation/tag_removed | Se quita una etiqueta de la conversacion |
Contactos (6 eventos)
| Evento | Se dispara cuando... |
|---|---|
contact/created | Se crea un nuevo contacto |
contact/updated | Se actualizan datos del contacto (nombre, email, telefono, campos custom) |
contact/status_changed | El contacto cambia de estado (ej: new a client) |
contact/operator_assigned | Se asigna un agente al contacto |
contact/tag_added | Se agrega una etiqueta al contacto |
contact/deleted | Se elimina un contacto |
Payload de eventos
Cada entrega es un POST HTTP con los siguientes headers y body:
Headers
| Header | Valor |
|---|---|
Content-Type | application/json |
X-Webhook-Secret | El secret del webhook (para verificar autenticidad) |
X-Webhook-Event | El tipo de evento entregado (ej. conversation/message), igual al campo event del body |
| Headers personalizados | Los que hayas configurado en customHeaders |
Body
{
"event": "conversation/created",
"timestamp": "2026-03-29T12:00:00.000Z",
"data": {
"id": "507f1f77bcf86cd799439011",
"companyId": "507f191e810c19729de860ea",
"websiteId": "507f191e810c19729de860eb",
"visitorName": "Juan Perez",
"visitorEmail": "[email protected]",
"channel": "WEBSITE",
"status": "ACTIVE",
"assignedTo": null,
"createdAt": "2026-03-29T12:00:00.000Z"
}
}
{
"event": "contact/status_changed",
"timestamp": "2026-03-29T14:30:00.000Z",
"data": {
"id": "507f1f77bcf86cd799439012",
"companyId": "507f191e810c19729de860ea",
"name": "Juan Perez",
"email": "[email protected]",
"status": "client",
"previousStatus": "new",
"changes": {
"status": { "before": "new", "after": "client" }
}
}
}
Eventos de prueba
Los eventos enviados con el endpoint /test incluyen "test": true:
{
"event": "conversation/created",
"webhookId": "60a1b2c3d4e5f6a7b8c9d0e1",
"timestamp": "2026-03-29T12:00:00.000Z",
"test": true,
"data": {
"id": "test-1711713600000",
"message": "This is a test event from Cliengo"
}
}
Seguridad
Verificar el secret
Cada webhook tiene un secret unico (formato whsec_...) que se envia en el header X-Webhook-Secret de cada entrega. Verificalo en tu servidor para asegurarte de que la request viene de Cliengo:
app.post('/webhook', (req, res) => {
const secret = req.headers['x-webhook-secret'];
if (secret !== process.env.WEBHOOK_SECRET) {
return res.status(401).json({ error: 'Invalid secret' });
}
// Procesar evento...
res.status(200).json({ received: true });
});
Reintentos
Si tu servidor no responde o devuelve un error, Cliengo reintenta la entrega automaticamente:
| Aspecto | Detalle |
|---|---|
| Estrategia | Backoff exponencial (1s, 2s, 4s, 8s, 16s...) |
| Maximo de reintentos | Configurable al crear el webhook (retryCount, 0-10, default 5) |
| Errores que se reintentan | 5xx, timeouts, errores de red |
| Errores que NO se reintentan | 4xx (error del cliente, tu servidor rechazo la request) |
| Timeout | 10 segundos por intento |
Cada intento (exitoso o no) se registra en el log de entregas.
Historial de entregas
Consulta el historial de entregas de un webhook para debugging:
curl https://connect.cliengo.com/v1/webhooks/WEBHOOK_ID/logs \ -H "Authorization: Bearer TU_TOKEN"
Cada entrada del log incluye:
| Campo | Descripcion |
|---|---|
event | Tipo de evento (ej: conversation/created) |
statusCode | HTTP status de la respuesta de tu servidor |
success | true si tu servidor respondio 2xx |
errorMessage | Mensaje de error si fallo |
responseBody | Body de la respuesta de tu servidor |
createdAt | Fecha y hora del intento |
Gestion de webhooks
Actualizar eventos suscritos
curl -X PUT https://connect.cliengo.com/v1/webhooks/WEBHOOK_ID \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"eventTypes": ["conversation/created", "conversation/archived", "contact/status_changed"]
}'
Desactivar temporalmente
curl -X PATCH https://connect.cliengo.com/v1/webhooks/WEBHOOK_ID \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"isActive": false}'
Eliminar
curl -X DELETE https://connect.cliengo.com/v1/webhooks/WEBHOOK_ID \ -H "Authorization: Bearer TU_TOKEN"
Buenas practicas
- Responde 200 inmediatamente — procesa el evento de forma asincrona. Si tu servidor tarda mas de 10 segundos, Cliengo lo interpreta como timeout y reintenta.
- Implementa idempotencia — por los reintentos, es posible que recibas el mismo evento mas de una vez. Los eventos reales no incluyen un id de entrega en el body; deduplica usando un identificador estable del propio evento (por ejemplo
data.message.idenconversation/message, odata.id) combinado con el headerX-Webhook-Event. El campowebhookIdsolo aparece en los eventos de prueba ("test": true). - Verifica el secret — siempre compara el header
X-Webhook-Secretcon el secret de tu webhook para evitar requests falsas. - Usa HTTPS — tu endpoint debe usar HTTPS para proteger los datos en transito.
- Monitorea los logs — revisa periodicamente el historial de entregas para detectar fallos.
- Maneja eventos de prueba — los eventos con
"test": truese envian desde el boton "Probar" del dashboard. Podes ignorarlos o procesarlos como quieras.
Fulfillment: control externo del chatbot
Toma el control de una conversacion desde tu propio sistema: pausa el bot, consulta tu backend en tiempo real y responde de forma dinamica. Es la forma correcta de implementar un flujo de fulfillment (por ejemplo: consultar el estado de un pedido, validar un DNI, cotizar en vivo) sobre el chat de Cliengo.
Los webhooks nativos sirven para enviar datos hacia afuera (registrar el lead en tu CRM al cerrar la charla), pero no pausan la conversacion ni reescriben la respuesta del bot. Para eso se usa la Connect API: tu sistema recibe cada mensaje del visitante, apaga el bot cuando lo necesitas, consulta lo que tengas que consultar, y escribe la respuesta en la conversacion. Todo lo que sigue funciona sobre endpoints que ya estan disponibles.
Existe una alternativa donde el bot nativo llama a tu endpoint (en vez de que vos manejes la charla desde afuera): es el fulfillment nativo del chatbot. Suele ser la mejor opcion cuando queres que el bot siga a cargo y solo consultar tu sistema en puntos concretos.
Conceptos clave
Antes de armar el flujo, tres piezas que conviene tener claras:
| Pieza | Que es |
|---|---|
conversation/message | El evento de webhook que se dispara con cada mensaje de una conversacion (del visitante, del bot o de un agente). Es tu disparador en tiempo real. |
chatbotState | Estado del bot en esa conversacion: "ON" (el bot responde) u "OFF" (el bot esta pausado / intervenido). Viene en el objeto conversation de cada evento, y lo controlas vos. |
sender.type | Quien envio el mensaje: visitor (el cliente), robot (el bot), user o external_operator (un agente). Clave para no entrar en loops. |
Dos patrones
Segun cuanto control quieras tomar, hay dos formas de encararlo. Podes combinarlas.
| Patron A — Bot externo (custom) | Patron B — Pausa puntual | |
|---|---|---|
| Idea | Tu sistema es el bot. El bot nativo queda apagado y vos respondes todos los mensajes por API. | El bot nativo maneja la charla y vos solo intervenis en un paso concreto (pausar → consultar → responder → reanudar). |
| Cuando usarlo | Logica conversacional propia, o cuando necesitas control total sin competir con el bot nativo. | Ya tenes un bot armado en Cliengo y solo queres enriquecer un momento puntual con datos de tu sistema. |
| Timing | Sin carreras: no hay bot nativo compitiendo por responder. | Hay que tener en cuenta el timing (ver mas abajo): el webhook corre en paralelo al bot nativo. |
| Complejidad | Mayor (implementas toda la conversacion). | Menor (solo el paso de fulfillment). |
El disparador: webhook conversation/message
Suscribite al evento conversation/message (ver como crear un webhook). Cliengo hara un POST a tu servidor con cada mensaje. Este es el payload real de un mensaje entrante del visitante:
{
"event": "conversation/message",
"timestamp": "2026-07-22T14:03:11.000Z",
"data": {
"conversation": {
"id": "507f1f77bcf86cd799439011",
"companyId": "507f191e810c19729de860ea",
"websiteId": "507f191e810c19729de860eb",
"channel": "WEBSITE",
"status": "opened",
"chatbotState": "ON",
"contactId": "507f191e810c19729de860ec",
"visitorName": "Juan Perez",
"visitorEmail": "[email protected]",
"visitorPhone": "+5491122334455"
},
"message": {
"id": "6a2b1ec38380772d8a300c2b",
"message": { "text": "Quiero el estado de mi pedido 12345", "type": "text/plain" },
"sender": { "id": "507f191e810c19729de860ec", "type": "visitor", "initial": "J", "name": "Juan Perez" },
"sendAt": "2026-07-22T14:03:11.000Z",
"sentAt": "2026-07-22T14:03:11.000Z"
},
"processMessage": true,
"chatbotId": "507f191e810c19729de860ed"
}
}
| Campo | Uso en fulfillment |
|---|---|
data.conversation.id | El conversationId que vas a usar para pausar el bot y responder. |
data.conversation.chatbotState | Si el bot esta ON u OFF en ese momento. |
data.message.message.text | El texto del mensaje (ojo: viene anidado en message.message.text). |
data.message.sender.type | Quien lo envio. Filtra por "visitor" para reaccionar solo a mensajes del cliente. |
data.message.id | Id unico del mensaje — usalo como clave de idempotencia. |
Evita loops. Como el evento se dispara con todos los mensajes, tus propias respuestas (y las del bot) tambien te llegan de vuelta. Reacciona unicamente cuando data.message.sender.type === "visitor", o vas a responderte a vos mismo en un ciclo infinito.
Pausar y reanudar el bot
Para que el bot nativo deje de responder en una conversacion, apagalo con un PATCH:
curl -X PATCH https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/chatbot-state \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"chatbotState": "OFF"}'
Para devolverle el control al bot nativo cuando terminaste, volve a prenderlo:
curl -X PATCH https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/chatbot-state \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"chatbotState": "ON"}'
Atajo: enviar un mensaje como agente (fromType: "user", ver abajo) tambien pausa el bot automaticamente — no necesitas un chatbot-state OFF previo. El PATCH explicito sirve sobre todo para reanudar el bot al terminar el fulfillment, o para apagarlo antes de que el bot conteste.
Responder en la conversacion
Escribi tu respuesta en la charla con un POST. Usa fromType: "user" (mensaje de agente): se muestra al visitante y, de paso, pausa el bot nativo.
curl -X POST https://connect.cliengo.com/v1/conversations/CONVERSATION_ID/messages \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from": "TU_USER_ID",
"fromType": "user",
"type": "text/plain",
"body": "Tu pedido 12345 esta en camino, llega manana entre las 9 y las 13 hs."
}'
Si el bot esta pausado (chatbotState: "OFF" / intervenido), los mensajes enviados con fromType: "robot" se rechazan con HTTP 409 ("Conversation is intervened, chatbot message cannot be sent"). Para respuestas de fulfillment usa fromType: "user".
Patron A: bot externo (custom)
Tu sistema responde toda la conversacion. El flujo por cada mensaje del visitante es directo: recibis el webhook, calculas la respuesta y la escribis. Como sos el unico que responde, no hay carrera con el bot nativo.
Los ejemplos usan fetch: viene incluido de forma global en Node.js ≥ 18. En versiones anteriores instalalo con node-fetch o undici. Aplica a los dos patrones.
app.post('/cliengo-webhook', async (req, res) => {
// 1. Verifica el secret y responde 200 rapido
if (req.headers['x-webhook-secret'] !== process.env.WEBHOOK_SECRET) {
return res.status(401).end();
}
res.status(200).json({ received: true });
const { event, data } = req.body;
if (event !== 'conversation/message') return;
// 2. Solo reacciona a mensajes del visitante (evita loops)
if (data.message.sender.type !== 'visitor') return;
const conversationId = data.conversation.id;
const texto = data.message.message.text;
// 3. Tu logica: consulta tu backend, arma la respuesta
const respuesta = await miBackend.responder(texto, data.conversation);
// 4. Escribi la respuesta en la charla (como agente)
await fetch(`https://connect.cliengo.com/v1/conversations/${conversationId}/messages`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CLIENGO_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: process.env.CLIENGO_USER_ID,
fromType: 'user',
type: 'text/plain',
body: respuesta,
}),
});
});
Si queres que el bot nativo no intervenga nunca en estas conversaciones, apagalo apenas empiezan: suscribite tambien a conversation/created y haz un chatbot-state OFF al recibirlo (o el primer fromType: "user" ya lo apaga solo).
Patron B: pausa puntual
El bot nativo maneja la charla normalmente. En un paso concreto (por ejemplo, cuando el visitante pide el estado de un pedido) tu sistema toma el control por un momento: pausa, consulta y responde.
app.post('/cliengo-webhook', async (req, res) => {
if (req.headers['x-webhook-secret'] !== process.env.WEBHOOK_SECRET) {
return res.status(401).end();
}
res.status(200).json({ received: true });
const { event, data } = req.body;
if (event !== 'conversation/message') return;
if (data.message.sender.type !== 'visitor') return;
const texto = data.message.message.text;
// Solo intervenimos si el mensaje aplica a fulfillment
if (!necesitaFulfillment(texto)) return;
const conversationId = data.conversation.id;
const auth = { 'Authorization': `Bearer ${process.env.CLIENGO_TOKEN}`, 'Content-Type': 'application/json' };
// 1. Pausa el bot para que no conteste
await fetch(`https://connect.cliengo.com/v1/conversations/${conversationId}/chatbot-state`, {
method: 'PATCH', headers: auth, body: JSON.stringify({ chatbotState: 'OFF' }),
});
// 2. Consulta tu sistema
const respuesta = await miBackend.consultar(texto, data.conversation);
// 3. Responde en la charla
await fetch(`https://connect.cliengo.com/v1/conversations/${conversationId}/messages`, {
method: 'POST', headers: auth,
body: JSON.stringify({ from: process.env.CLIENGO_USER_ID, fromType: 'user', type: 'text/plain', body: respuesta }),
});
// 4. (Opcional) Devolvele el control al bot nativo
await fetch(`https://connect.cliengo.com/v1/conversations/${conversationId}/chatbot-state`, {
method: 'PATCH', headers: auth, body: JSON.stringify({ chatbotState: 'ON' }),
});
});
Sobre el timing. El webhook se dispara en paralelo al procesamiento del bot nativo, asi que puede haber una carrera: el bot podria empezar a responder el mismo mensaje antes de que tu chatbot-state OFF llegue. Para un corte limpio, diseña el flujo del bot para que haga el handoff en ese punto (por ejemplo, que el bot diga "dame un momento que lo consulto" y ahi termine su paso), y que tu sistema responda a continuacion. Si necesitas control total y determinista, usa el Patron A.
Buenas practicas
- Responde 200 al webhook enseguida y procesa en segundo plano. Si tardas mas de 10 s, Cliengo lo toma como timeout y reintenta.
- Filtra por
sender.type === "visitor"siempre. Es la unica forma de no reaccionar a tus propias respuestas ni a las del bot. - Deduplica con
data.message.id: por los reintentos podes recibir el mismo mensaje mas de una vez. - Reanuda el bot con
chatbot-state ONcuando termines (si querias que el bot nativo siguiera). Nota: quitar el operador asignado no reactiva el bot por si solo. - Responde como
user, no comorobot: los mensajes de robot se rechazan con 409 mientras el bot esta pausado. - Verifica el
X-Webhook-Secreten cada request y usa HTTPS.
Fulfillment nativo del chatbot
La otra forma de hacer fulfillment: en vez de tomar el control desde afuera, el chatbot nativo de Cliengo llama a tu endpoint en el momento justo, usa tu respuesta para contestar y sigue el flujo. Es lo que usamos para los "chatbots custom".
¿Cuál elijo? Si querés que el bot siga manejando la conversación y solo consultar tu sistema en ciertos puntos (de forma sincrónica, sin carreras), usá fulfillment nativo (esta sección). Si querés manejar vos toda la conversación desde tu backend, usá control externo vía Connect (sección anterior).
Los dos modos
| Modo | Propiedad | Cuándo dispara |
|---|---|---|
| Global | global_fulfillment_url (nivel config) | En cada mensaje del visitante, sin importar el nodo. |
| Por nodo | fulfillment_url (en una pregunta del question_list) | Solo cuando el bot procesa ese nodo puntual. |
Los dos usan el mismo contrato (request y response de abajo). Si están configurados los dos, se llaman en paralelo y gana la respuesta del nodo.
Cómo funciona
Cuando el bot llega a un mensaje que tiene fulfillment, hace un POST a tu endpoint (a través de la capa de integración de Cliengo, que actúa de intermediaria). Vos consultás lo que necesites y devolvés un JSON; el bot toma response.text como su próxima respuesta y sigue el flujo según lo que devuelvas.
El bot espera tu respuesta de forma sincrónica con un timeout de ~8 segundos. Si tu endpoint tarda más, el bot sigue sin tu respuesta. Devolvé rápido.
El request a tu endpoint
POST con el estado de la sesión del chatbot en el body. Campos principales:
{
"channel": "WEBSITE",
"visitorId": "507f191e810c19729de860ec",
"chatbotName": "Bot de Ventas",
"flow": 12,
"status": "active",
"collected_data": {
"name": { "value": "Juan Perez" },
"email": { "value": "[email protected]" },
"phone": { "national_format": "11 2233-4455" },
"custom": { "nro_pedido": "12345" }
},
"conversation_context": {},
"chat_log": [
{ "sender": "visitor", "message": "Quiero el estado de mi pedido 12345" }
],
"response_log": [],
"tags": ["ventas"],
"visitorData": { "url": "https://tienda.com/checkout", "igUsername": null },
"creation_date": "2026-07-22T14:00:00.000Z",
"last_interaction_date": "2026-07-22T14:03:11.000Z"
}
| Campo | Qué es |
|---|---|
collected_data | Los datos que el bot fue recolectando (slots): name, email, phone, más lo que hayas guardado en custom. |
chat_log | Historial de mensajes del visitante. El último mensaje del visitante está acá. |
response_log | Historial de respuestas del bot. |
flow | Id del nodo/pregunta actual del flujo. |
channel | Canal: WEBSITE, WHATSAPP, etc. |
visitorId | Identificador del visitante. |
tags, conversation_context | Tags de la conversación y contexto acumulado. |
La respuesta que devolvés
Un JSON. Todo es opcional; response.text es lo que el bot le dice al visitante. Con go_to_name / flow podés saltar a otro nodo, y con stopChat frenar la respuesta automática del bot.
{
"response": {
"text": ["Tu pedido 12345 está en camino, llega mañana entre las 9 y 13 hs."],
"response_type": "TEXT",
"response_options": [],
"stopChat": false,
"go_to_name": "pedido_en_camino"
},
"custom": { "estado_pedido": "en_camino" }
}
| Campo | Efecto |
|---|---|
response.text | Array de strings: los mensajes que el bot le envía al visitante. |
response.response_type | Tipo de respuesta (TEXT, LIST, NONE, …). |
response.response_options | Botones de respuesta rápida (quick replies). |
response.stopChat | Si es true, el bot no manda su pregunta automática ese turno (útil si respondés vos por otro lado). |
response.go_to_name / response.flow | Salta el flujo a otro nodo: por internal_name (go_to_name, tiene prioridad) o por id (flow). |
name / email / phone | Escribís slots del contacto: { "value": "..." } (phone: { "national_format", "international_format" }). |
custom | Objeto libre que se mergea en collected_data.custom para los próximos turnos. |
Cómo se habilita (por API)
Podés apuntar el fulfillment a tu endpoint vos mismo, desde la Connect API, usando los endpoints de configuración del chatbot. Necesitás websites_write para escribir (websites_read para leer). El CHATBOT_ID lo obtenés de GET /v1/chatbots (o es el channelId del canal, según tu cuenta).
Estos son los endpoints reales de configuración del bot, no un recurso acotado a fulfillment. settings acepta otros campos y question-list reemplaza el flujo completo. Modificás lo que mandás bajo tu responsabilidad. (Siempre acotado a tu propia empresa: no podés tocar el bot de otra cuenta.)
Fulfillment global — PATCH .../settings
La URL global (global_fulfillment_url) es un setting: el bot la llama en cada mensaje.
curl -X PATCH https://connect.cliengo.com/v1/chatbots/CHATBOT_ID/chatbot-config/settings \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"global_fulfillment_url": "https://mi-sistema.com/fulfillment"}'
Fulfillment por nodo — GET + PUT .../question-list
El fulfillment_url por nodo vive dentro del question_list, y el PUT reemplaza la lista entera. El flujo seguro es leer, modificar el nodo que querés y volver a escribir toda la lista.
curl https://connect.cliengo.com/v1/chatbots/CHATBOT_ID/chatbot-config/question-list \ -H "Authorization: Bearer TU_TOKEN"
Cada nodo trae su internal_name (y todos sus demás campos). Cambiá el fulfillment_url del nodo que quieras y mandá la lista completa de vuelta:
curl -X PUT https://connect.cliengo.com/v1/chatbots/CHATBOT_ID/chatbot-config/question-list \
-H "Authorization: Bearer TU_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "internal_name": "salute", ... },
{ "internal_name": "estado_pedido", "fulfillment_url": "https://mi-sistema.com/pedido", ... }
]'
El PUT es un reemplazo total: mandá todos los nodos con todos sus campos (los que devolvió el GET), o vas a perder lo que omitas.
Para desactivar un fulfillment, mandá la URL como cadena vacía ("").
Seguridad
El POST a tu endpoint no lleva hoy una firma ni token de Cliengo que puedas validar. Si necesitás verificar el origen, usá una URL con un secreto embebido (por ejemplo un path o query token difícil de adivinar) y/o restringí por IP. No expongas lógica sensible sin alguna de esas medidas.