Payloads

Esta página documenta los payloads de los eventos normales de webhooks:

  • conversation.*
  • message.*
  • client.*

Para WhatsApp Flows (whatsapp.flows.*), consulta WhatsApp Flows , porque esos eventos requieren que tu endpoint responda con datos para continuar el flow.

Envelope común

Todos estos eventos llegan a tu endpoint con un POST JSON usando esta estructura:

{
  "id": "9f8c...",
  "event": "conversation.created",
  "workspaceId": "ws_456",
  "timestamp": "2026-05-06T19:00:00.000Z",
  "source": "system.conversation.created",
  "resourceType": "conversation",
  "resourceId": "conv_123",
  "changes": null,
  "data": {}
}
CampoTipoDescripción
idstringIdentificador único del evento
eventstringNombre del evento que disparó el webhook
workspaceIdstringWorkspace donde ocurrió el evento
timestampstringFecha ISO-8601 del evento
sourcestringOrigen del evento — ver Campo source
resourceTypeconversation \| message \| clientTipo de recurso afectado
resourceIdstring \| nullIdentificador del recurso afectado
changesobject \| nullCambios detectados. Es null en eventos de creación / eliminación
dataobjectSnapshot normalizado del recurso

Campo source

Identifica el origen del evento. Su valor sigue el formato:

<origin>.<resource>.<action>[.<qualifier>]
  • origin: uno de api, dashboard, inbound, agent, system, scheduler, campaign.
  • resource: client, conversation, message, audience, scheduled_event, webhook, moderation, supervisor.
  • action: verbo / estado (created, updated, deleted, processed, dispatched, etc.).
  • qualifier (opcional): bulk, auto, campaign.

Usa el source sólo para auditoría / debugging. La identidad lógica del evento (qué cambió y qué recursos afecta) está en event, resourceType, resourceId y changes. La plataforma del canal (whatsapp, instagram, …) se lee en data.conversation.platform, no en source.

Ejemplos comunes por evento:

eventPosibles valores de source
client.createdapi.client.created, inbound.client.created, campaign.client.created
client.updatedapi.client.updated, dashboard.client.updated, inbound.client.profile_updated, inbound.client.refreshed, inbound.client.identifiers_updated, agent.supervisor.applied, system.moderation.applied
client.tags.updatedapi.client.tagged.bulk, api.audience.added, api.audience.removed, api.audience.cleared, dashboard.client.updated
client.customFields.updatedapi.client.custom_fields_updated, inbound.client.custom_fields_updated, agent.client.confirmation_saved, dashboard.client.updated
conversation.createdsystem.conversation.created, dashboard.conversation.created, campaign.conversation.created
conversation.status.updatedapi.conversation.updated, api.conversation.finished, api.conversation.marked_spam, api.conversation.archived, api.conversation.reactivated, dashboard.conversation.stopped, campaign.conversation.updated
conversation.expiredsystem.conversation.expired_by_inactivity, agent.conversation.expired, api.conversation.archived.auto, api.conversation.updated
conversation.owners.updatedapi.conversation.operator_assigned, api.conversation.operators_set, dashboard.conversation.assistance_accepted
conversation.tags.updatedapi.conversation.tagged, dashboard.conversation.assistance_requested
message.createdinbound.message.received, agent.message.generated, agent.message.processed, agent.message.dispatched, agent.message.service_sent, agent.message.summary_generated, campaign.message.received, dashboard.message.system_inserted, scheduler.scheduled_event.created
message.updatedinbound.message.delivery_updated, inbound.message.delivery_failed, inbound.message.delivery_updated.campaign, agent.message.processed, agent.message.status_updated, agent.message.dispatched, dashboard.message.resent, scheduler.scheduled_event.processing, scheduler.scheduled_event.sent, scheduler.scheduled_event.failed, api.scheduled_event.cancelled

Conversaciones

Eventos:

Eventochanges
conversation.creatednull
conversation.status.updated{ "status": { "before", "after" } }
conversation.operation.updated{ "operation": { "before", "after" } }
conversation.owners.updated{ "owners": { "added": [], "removed": [] } }
conversation.tags.updated{ "tags": { "added": [], "removed": [] } }
conversation.expired{ "isFinished": { "before", "after": true } }

En eventos conversation.*, data siempre tiene:

{
  "client": {
    "id": "521234567890",
    "phoneNumber": "521234567890",
    "creationDate": "2026-05-06T19:00:00.000Z",
    "lastUpdate": "2026-05-06T19:00:00.000Z",
    "name": "Juan Pérez",
    "firstname": "Juan",
    "email": null
  },
  "conversation": {
    "id": "conv_123",
    "conversationId": "521234567890",
    "canSendDirectMessage": true,
    "workspaceId": "ws_456",
    "channelId": "521555000111",
    "contactName": "Juan Pérez",
    "phoneNumber": "521234567890",
    "topic": "",
    "platform": "whatsapp",
    "owners": [],
    "tags": [],
    "creationDate": "2026-05-06T19:00:00.000Z",
    "lastUpdate": "2026-05-06T19:00:00.000Z",
    "status": "open",
    "operation": "automatic",
    "messageCount": 1,
    "messages": []
  }
}
CampoTipoDescripción
idstring \| nullIdentificador interno
conversationIdstring \| nullIdentificador externo, normalmente el teléfono
canSendDirectMessagebooleanfalse si la conversación está cerrada, expirada, spam o fuera de la ventana de 24 h en WhatsApp
workspaceIdstring \| nullWorkspace propietario
channelIdstring \| nullCanal donde ocurrió
contactNamestring \| nullNombre del contacto
phoneNumberstring \| nullTeléfono del contacto
topicstringTema de la conversación
platformstring \| nullCanal del contacto: whatsapp, instagram, messenger, sms, etc.
ownersstring[]Propietarios asignados, identificados por email (no UID)
tagsstring[]Etiquetas asignadas
creationDatestring \| nullFecha ISO-8601
lastUpdatestring \| nullFecha ISO-8601
statusstring \| nullEstado actual: open, pending, finished, blocked, spam, expired
operationstring \| nullModo de la conversación: automatic (atendida por bot) o manual (atendida por operador)
messageCountnumberNúmero de mensajes
messagesunknown[]Mensajes serializados. Sólo aparece en eventos conversation.*

Mensajes

Eventos:

Eventochanges
message.creatednull
message.updatedCambios por campo: content, contentBlocks, contentType, direction, lastUpdate, role, status, files, images, owner
message.deletednull — declarado en el catálogo, pero no se emite todavía. Suscribirse no produce eventos hoy.

En eventos message.*, data tiene client, conversation y message. La conversación no incluye messages[]; el mensaje afectado va en data.message.

{
  "client": { "...": "WebhookClientSummary" },
  "conversation": { "...": "WebhookConversation sin messages[]" },
  "message": {
    "id": "msg_789",
    "content": "Hola, necesito ayuda con mi pedido",
    "contentBlocks": [
      { "type": "text", "text": "Hola, necesito ayuda con mi pedido" }
    ],
    "contentType": "text",
    "creationDate": "2026-05-06T19:00:00.000Z",
    "direction": "incoming",
    "files": [],
    "images": [],
    "owner": null,
    "lastUpdate": "2026-05-06T19:00:00.000Z",
    "role": "user",
    "status": "delivered"
  }
}
CampoTipoDescripción
idstring \| nullIdentificador del mensaje
contentstringTexto plano derivado del mensaje
contentBlocksunknown[]Bloques estructurados
contentTypestringtext por defecto
creationDatestring \| nullFecha ISO-8601
directionincoming \| outgoing \| string \| nullSiempre se normaliza a incoming u outgoing. Valores como received o sent se traducen antes de emitirse
filesunknown[]Archivos adjuntos
imagesunknown[]Imágenes adjuntas
owner{ "id": string } \| nullAutor del mensaje. id es el email del operador cuando aplica
lastUpdatestring \| nullFecha ISO-8601
rolestring \| nulluser, assistant, tool, etc.
statusstring \| nullEstado del mensaje. Valores comunes: received, sent, delivered, read, failed

Ejemplo de message.updated:

{
  "event": "message.updated",
  "resourceType": "message",
  "resourceId": "msg_789",
  "changes": {
    "status": {
      "before": "sent",
      "after": "read"
    }
  },
  "data": {
    "client": {},
    "conversation": {},
    "message": {}
  }
}

Clientes

Eventos:

Eventochanges
client.creatednull
client.updatedCambios por campo básico: phoneNumber, name, firstname, lastname, email, birthdate, gender, company, country, state, city, address, postalCode, creationDate, lastUpdate
client.owners.updated{ "owners": { "added": [], "removed": [] } }
client.tags.updated{ "tags": { "added": [], "removed": [] } }
client.customFields.updated{ "customFields": { "<campo>": { "before", "after" } } } o { "customFields": { "<campo>": { "added": [], "removed": [] } } }

En eventos client.*, data es directamente el cliente normalizado:

{
  "id": "521234567890",
  "workspaceId": "ws_456",
  "phoneNumber": "521234567890",
  "name": "Juan Pérez",
  "firstname": "Juan",
  "lastname": "Pérez",
  "email": "juan@empresa.com",
  "birthdate": null,
  "gender": null,
  "company": "Acme Inc.",
  "country": "MX",
  "state": null,
  "city": null,
  "address": null,
  "postalCode": null,
  "tags": ["vip"],
  "owners": ["agente1@empresa.com"],
  "customFields": {
    "placas": "ABC123"
  },
  "creationDate": "2026-05-06T19:00:00.000Z",
  "lastUpdate": "2026-05-06T19:00:00.000Z"
}

Ejemplo de client.customFields.updated:

{
  "event": "client.customFields.updated",
  "resourceType": "client",
  "resourceId": "521234567890",
  "changes": {
    "customFields": {
      "placas": {
        "before": "ABC123",
        "after": "XYZ789"
      },
      "visitas": {
        "added": [
          {
            "id": "visit_5",
            "creationDate": "2026-05-06T19:00:00.000Z",
            "lastUpdate": "2026-05-06T19:00:00.000Z",
            "content": "Visita de seguimiento"
          }
        ],
        "removed": []
      }
    }
  },
  "data": {
    "id": "521234567890",
    "customFields": {
      "placas": "XYZ789",
      "visitas": []
    }
  }
}