Skip to content

Canal API

El canal API es un canal genérico basado en webhooks que te permite integrar cualquier plataforma de mensajería o sistema externo con Zelta Chat. Es la opción ideal cuando necesitas conectar un canal que no tiene integración nativa o cuando quieres crear un flujo de comunicación personalizado.

Casos de uso

Caso de usoDescripción
Canal de mensajería propioIntegra tu propia app de chat o sistema de mensajería interno
Plataformas sin integración nativaConecta servicios de mensajería que no tienen canal dedicado
Sistemas de ticketsRecibe notificaciones de sistemas externos como conversaciones
Formularios webConvierte envíos de formularios en conversaciones de soporte
Integraciones a medidaCualquier sistema que pueda enviar y recibir HTTP

Crear un canal API

  1. Ve a Ajustes > Canales > + Agregar canal.
  2. Selecciona API.
  3. Completa los campos:
CampoDescripción
Nombre del canalNombre descriptivo (ej: "Canal Personalizado - App Móvil")
URL de webhook (opcional)URL donde Zelta Chat enviará los mensajes salientes
  1. Haz clic en Crear canal.

En la configuración del canal encontrarás el Identificador de bandeja de entrada (inbox_identifier). Es el valor que autentica a tus clientes API y el que va en la URL de todas las peticiones.

Trata el identificador como un secreto

Cualquiera que tenga el inbox_identifier puede crear contactos y mensajes en esta bandeja. No lo publiques en código de cliente que no controles.

Autenticación

El canal API no usa el header api_access_token. La autenticación va en la propia URL: el inbox_identifier de la bandeja, y a partir de él el contact_identifier del contacto.

ValorQué esDónde se obtiene
inbox_identifierIdentifica la bandeja del canal APIAjustes > Canales > [tu canal API]
contact_identifierIdentifica al contacto en la bandeja (source_id)Lo devuelve la creación del contacto
conversation_idNúmero de la conversaciónLo devuelve la creación de la conversación

Enviar mensajes a Zelta Chat

El flujo tiene tres pasos: crear el contacto, crear la conversación y crear el mensaje. Cada uno es una llamada distinta.

Paso 1: Crear el contacto

bash
curl -X POST https://chat.zelta.dev/public/api/v1/inboxes/{inbox_identifier}/contacts \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "cliente_12345",
    "name": "Juan Pérez",
    "email": "juan@ejemplo.com",
    "phone_number": "+50760000000",
    "custom_attributes": {
      "order_id": "ORD-2024-001",
      "source": "app_movil"
    }
  }'

La respuesta incluye el source_id del contacto, que es el contact_identifier de las siguientes llamadas.

CampoTipoRequeridoDescripción
identifierstringNoIdentificador único del contacto en tu sistema
identifier_hashstringDependeHMAC del identifier. Obligatorio si activaste la validación de identidad
namestringNoNombre del contacto
emailstringNoCorreo electrónico del contacto
phone_numberstringNoTeléfono en formato E.164
avatar_urlstringNoURL de la foto de perfil
custom_attributesobjectNoAtributos personalizados en formato clave-valor

Paso 2: Crear la conversación

bash
curl -X POST https://chat.zelta.dev/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations \
  -H "Content-Type: application/json" \
  -d '{
    "custom_attributes": {
      "order_id": "ORD-2024-001"
    }
  }'

La respuesta incluye el id de la conversación, que usarás para enviar mensajes.

Paso 3: Crear el mensaje

bash
curl -X POST https://chat.zelta.dev/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/messages \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Hola, necesito ayuda con mi pedido"
  }'
CampoTipoRequeridoDescripción
contentstringContenido del mensaje
echo_idstringNoIdentificador temporal para reconciliar el mensaje en tu cliente

Los mensajes creados por esta vía son siempre entrantes: se registran como mensajes del contacto.

Enviar archivos adjuntos

Usa multipart/form-data en la misma URL de mensajes:

bash
curl -X POST https://chat.zelta.dev/public/api/v1/inboxes/{inbox_identifier}/contacts/{contact_identifier}/conversations/{conversation_id}/messages \
  -F "content=Adjunto mi comprobante de pago" \
  -F "attachments[]=@/ruta/al/archivo.pdf"

Otras operaciones disponibles

OperaciónMétodo y ruta (relativa a /public/api/v1/inboxes/{inbox_identifier})
Consultar un contactoGET /contacts/{contact_identifier}
Actualizar un contactoPATCH /contacts/{contact_identifier}
Listar conversaciones del contactoGET /contacts/{contact_identifier}/conversations
Consultar una conversaciónGET /contacts/{contact_identifier}/conversations/{conversation_id}
Resolver una conversaciónPOST .../conversations/{conversation_id}/toggle_status
Indicar que el contacto está escribiendoPOST .../conversations/{conversation_id}/toggle_typing con typing_status en on u off
Marcar como leídoPOST .../conversations/{conversation_id}/update_last_seen
Listar mensajesGET .../conversations/{conversation_id}/messages

Validación de identidad (HMAC)

Si quieres garantizar que un cliente no puede suplantar el identifier de otro contacto, activa Forzar validación de identidad de usuario en la configuración del canal. Con esa opción, las peticiones que no incluyan identifier_hash se rechazan.

El identifier_hash es el HMAC-SHA256 del identifier, firmado con el token HMAC del canal:

javascript
const crypto = require('crypto');

const identifierHash = crypto
  .createHmac('sha256', HMAC_TOKEN_DEL_CANAL)
  .update(identifier)
  .digest('hex');

Seguridad

Calcula el identifier_hash en tu servidor. Si el token HMAC viaja al cliente, la validación deja de aportar seguridad.

Recibir mensajes de Zelta Chat

Cuando un agente responde a una conversación del canal API, Zelta Chat envía una petición POST a la URL de webhook que configuraste en el canal.

Formato del webhook saliente

El payload es plano: los datos del mensaje van en la raíz y los del contexto en objetos anidados. Este ejemplo está abreviado —la petición real incluye además los objetos account, inbox y contact—:

json
{
  "event": "message_created",
  "id": 5678,
  "content": "Hola Juan, con gusto te ayudo. ¿Puedes compartirme tu número de pedido?",
  "content_type": "text",
  "message_type": "outgoing",
  "private": false,
  "created_at": "2024-03-15T10:30:00Z",
  "source_id": null,
  "conversation": {
    "display_id": 567,
    "status": "open"
  },
  "sender": {
    "id": 12,
    "name": "María López"
  }
}

Cuando el mensaje lleva adjuntos, el payload incluye además un array attachments.

Verificar la firma del webhook

Las peticiones firmadas incluyen tres encabezados:

EncabezadoContenido
X-Zelta-Signaturesha256= seguido del HMAC-SHA256 de timestamp.cuerpo
X-Zelta-TimestampMarca de tiempo Unix usada en la firma
X-Zelta-DeliveryIdentificador único de la entrega

La firma se calcula sobre la concatenación del timestamp, un punto y el cuerpo de la petición:

javascript
const crypto = require('crypto');

function verificarFirma(cuerpo, timestamp, firma, claveSecreta) {
  const hash = crypto
    .createHmac('sha256', claveSecreta)
    .update(`${timestamp}.${cuerpo}`)
    .digest('hex');
  return firma === `sha256=${hash}`;
}

Seguridad

Verifica siempre la firma antes de procesar la petición, y rechaza los timestamps demasiado antiguos para evitar reenvíos.

Eventos del webhook

EventoDescripción
message_createdSe creó un mensaje en la conversación
message_updatedSe actualizó un mensaje
conversation_createdSe creó una nueva conversación
conversation_updatedCambió algún dato de la conversación
conversation_status_changedEl estado de la conversación cambió (abierta, pendiente, pospuesta, resuelta)

Solución de problemas

ProblemaSolución
Error 404Revisa el inbox_identifier y el contact_identifier de la URL; son la vía de autenticación
Error 422Revisa que el cuerpo de la petición tenga los campos requeridos
HMAC failed: Invalid Identifier Hash ProvidedEl identifier_hash no coincide. Recalcúlalo en tu servidor con el token HMAC del canal
Webhook no recibe eventosConfirma que la URL de webhook sea accesible públicamente y responda con 2xx
Conversaciones duplicadasReutiliza el contact_identifier devuelto al crear el contacto en lugar de crear uno nuevo cada vez
Archivos no se envíanUsa multipart/form-data en lugar de application/json para adjuntos

Consejo

Guarda en tu sistema el contact_identifier y el conversation_id que devuelven las dos primeras llamadas. Con ellos puedes seguir enviando mensajes a la misma conversación sin volver a crear nada.

Documentación oficial de Zelta