El sobre
Todos los eventos tienen la misma forma exterior. Solodata cambia de un tipo a otro.
Todos los tipos de evento
Los eventos de contacto llevan el contacto completo, así que la mayoría de las integraciones nunca necesitan volver a llamar a la API. Los eventos de conversación y de mensaje llevan solo ids: lee la conversación o sus mensajes desde la API cuando necesites el contenido.
Con el tiempo se agregan nuevos tipos de evento. Un endpoint suscrito a todos los eventos empieza a recibirlos automáticamente. Responde
200 a cualquier type que no manejes.El objeto contacto
Los eventos de contacto incluyen un objetocontact con los mismos campos que GET /v1/contacts/{id}:
propertiescontiene tus propiedades personalizadas, indexadas por la clave de la propiedad. Leekindpara saber qué campovalue_*contiene el valor.- Las propiedades de archivo se omiten. El enlace de descarga de un archivo es temporal y expiraría antes de que llegue una entrega reintentada. Lee el contacto desde la API para obtener un enlace nuevo.
- El contacto es una foto tomada cuando Ciarem prepara el evento, un momento después del cambio. Si el contacto cambió dos veces seguidas, ambos eventos pueden mostrar el estado más reciente. Un reintento envía la misma foto otra vez, no una nueva.
Eventos de contacto
contact.created
Se agregó un contacto: lo hizo un compañero, una importación, la API, una integración como HubSpot, o una persona nueva escribió por un canal.contact.updated
Cambiaron los campos propios de un contacto (nombre, correo, teléfono, ciudad, etc.) o una de sus propiedades personalizadas.changed_fields lista qué cambió:
- Un campo simple aparece por su nombre:
"first_name","email". - Una propiedad personalizada aparece como
properties.<key>:"properties.custom_budget". Cada propiedad personalizada que cambia envía su propio eventocontact.updated.
contact.updated, contact.funnel_stage.changed y contact.owner.changed.
contact.deleted
Se eliminó un contacto. El contacto ya no existe, así que solo se envía su id.contact.merged
Se fusionaron dos contactos.contact es el que sobrevive, con el resultado de la fusión. merged_contact_id es el contacto que se integró en él y ya no existe por separado: actualiza los registros de tu lado que todavía apunten a él.
contact.funnel_stage.changed
Un contacto pasó a otra etapa del embudo, ya sea que lo movió un compañero o el agente de IA. La etapa nueva escontact.funnel_stage_id; la que dejó es previous_funnel_stage_id. Convierte los ids de etapa en nombres con GET /v1/funnel-stages.
contact.owner.changed
Cambió el propietario de un contacto. El propietario nuevo escontact.owner_id; el anterior es previous_owner_id. Cualquiera de los dos puede ser null, lo que significa que el contacto no tiene, o no tenía, propietario.
Eventos de conversación y de mensaje
Estos cuatro eventos comparten el mismodata base:
conversation.created
Empezó una conversación nueva.Ciarem espera unos segundos a que el contacto quede vinculado antes de enviar este evento, así que
contact_id casi siempre está presente. En el caso poco común de que siga en null, lee la conversación desde la API un poco después. Esta breve espera también significa que el primer messages.inbound de una conversación puede llegar antes que su conversation.created.conversation.assigned
Se asignó una conversación: la tomó un compañero, la asignó un administrador, lo hicieron tus reglas de asignación automática, o el agente de IA la transfirió.assignee_id es el compañero y siempre viene. team_id es el equipo de la conversación y puede ser null.
conversation.closed
Se resolvió una conversación, ya sea porque un compañero terminó el chat o porque el agente de IA la cerró. El cuerpo es eldata base de arriba.
messages.inbound
Un cliente envió un mensaje. El evento te dice qué conversación tiene algo nuevo; no contiene el mensaje en sí.Eventos de formularios de WhatsApp
whatsapp_flow.completed
Un cliente envió un formulario de WhatsApp. Se dispara una vez por envío, después de que Ciarem guardó las respuestas.answersusa los nombres internos de los campos del formulario, no el texto de la pregunta. Para leer los valores por propiedad, usaoutcome.saved(los campos y propiedades del contacto donde se escribieron las respuestas) y lee el contacto.outcome.errorslista las respuestas que no se pudieron guardar, por ejemplo un email que pertenece a otro contacto.- Un formulario de reserva también trae
booking_id,start_at,end_at,meeting_urlyattendee_emailenoutcome. - La respuesta de una pregunta de fotos o documentos es una lista de
{"file_name", "mime_type"}, youtcome.filesda su estado:{"q3": {"status": "pending"}}. El evento se envía antes de que se guarden los archivos. Cuando la pregunta se guarda en una propiedad de archivo, los archivos guardados llegan con el eventocontact.updatedque sigue. conversation_idpuede sernullcuando el formulario se envió con una plantilla fuera de una conversación.
El evento de prueba
Una prueba enviada desde el panel o conPOST /v1/webhook-endpoints/{id}/test se ve como un evento real, con dos diferencias:
- Su
idempieza conevt_test_, así que puedes distinguirlo y saltarte cualquier efecto real. - Si no eliges un tipo de evento, su
typeestesty sudataestá vacío:
GET /v1/webhook-event-samples devuelve el cuerpo de ejemplo de cada tipo, lo que resulta útil para escribir pruebas de tu propio handler.
Compatibilidad
Los cuerpos de los webhooks siguen la misma regla que el resto de la API: los cambios son aditivos. En cualquier momento pueden aparecer nuevos tipos de evento, nuevos campos dentro dedata y nuevos valores en campos de texto. Lee los campos que necesitas, ignora el resto, y revisa el changelog cuando algo se vea distinto.