Manual de integración: envío de mensajes, WebSocket de eventos, lectura de mensajes y alta de líneas.
La API usa un token de integración, que es por empresa. Un token da acceso a todas las líneas de tu empresa y a nada más: los datos de otras empresas son inaccesibles aunque conozcas sus identificadores.
Envía el token en la cabecera Authorization. Si tu cliente no te deja controlar esa cabecera, también se acepta X-Api-Key.
Authorization: Bearer chapp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# alternativa
X-Api-Key: chapp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
chapp_xxxx… de arriba es relleno: cada empresa saca el suyo desde
API → Mis tokens con la sesión iniciada, y te lo entrega para la
integración. Se muestra una sola vez, porque en la base queda solo un hash.
Todas las respuestas son JSON y traen el campo ok. A diferencia del endpoint directo (ver Clave de línea), aquí sí puedes confiar en el código HTTP.
// Éxito { "ok": true, "lineas": [ ... ] } // Error { "ok": false, "error": "linea_no_conectada", "mensaje": "La linea existe pero no esta conectada a WhatsApp." }
| HTTP | Significado |
|---|---|
200 / 201 | Todo bien. |
401 | Token faltante, inválido o revocado. |
404 | La línea no existe o no es de tu empresa. |
409 | Conflicto: línea desconectada o número ya registrado. |
422 | Faltan parámetros o son inválidos (detalle en errors). |
429 | Superaste el límite de 120 requests por minuto. |
502 | El servidor de mensajería rechazó la operación. |
{'{'}linea{'}'} acepta indistintamente el uuid o el número de la línea. Usa el que te resulte más cómodo.
Envía texto, una imagen o un documento a un contacto o a un grupo.
| Campo | Descripción | |
|---|---|---|
linea | requerido | Línea emisora: uuid o número. |
fono | requerido | Destino. Número del contacto, o JID del grupo si es_grupo=1. |
mensaje | requerido | Texto del mensaje. Si hay adjunto, va como leyenda. |
es_grupo | opcional | true si el destino es un grupo. |
tipo_adjunto | opcional | PNG, PDF o vacio (por defecto). |
url_adjunto | opcional | URL pública de la imagen cuando tipo_adjunto=PNG. |
url_documento | opcional | URL pública del archivo cuando tipo_adjunto=PDF. |
curl -X POST "https://www.chapp.cl/api/v1/mensajes" \ -H "Authorization: Bearer $CHAPP_TOKEN" \ -H "Accept: application/json" \ -d "linea=__FONO__" \ -d "fono=56911111111" \ -d "mensaje=Hola desde la API de Chapp"
curl -X POST "https://www.chapp.cl/api/v1/mensajes" \ -H "Authorization: Bearer $CHAPP_TOKEN" \ -d "linea=__FONO__" \ -d "fono=56911111111" \ -d "mensaje=Adjunto la cotización" \ -d "tipo_adjunto=PNG" \ -d "url_adjunto=https://midominio.cl/img/cotizacion.png"
<?php $ch = curl_init('https://www.chapp.cl/api/v1/mensajes'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer '.getenv('CHAPP_TOKEN'), 'Accept: application/json', ], CURLOPT_POSTFIELDS => http_build_query([ 'linea' => '__FONO__', 'fono' => '56911111111', 'mensaje' => 'Hola desde PHP', ]), ]); $r = json_decode(curl_exec($ch), true); curl_close($ch); if (!$r['ok']) { error_log('Chapp: '.$r['mensaje']); }
const r = await fetch('https://www.chapp.cl/api/v1/mensajes', { method: 'POST', headers: { 'Authorization': `Bearer ${'{'}process.env.CHAPP_TOKEN{'}'}`, 'Accept': 'application/json', }, body: new URLSearchParams({ linea: '__FONO__', fono: '56911111111', mensaje: 'Hola desde Node', }), }); const data = await r.json(); if (!data.ok) throw new Error(data.mensaje);
+ ni espacios. Si envías 9 dígitos se antepone 56 automáticamente; con 8 dígitos, 569.
409 con error: "linea_no_conectada". Conviene chequear el estado antes de una campaña masiva (ver sección 4).
Conexión en tiempo real para enterarte de mensajes nuevos, cambios de estado de una línea y códigos QR de vinculación. Es solo de lectura: el servidor emite eventos, el cliente no envía comandos.
Cada mensaje es un JSON. Discrimina por el campo evento.
| Evento | Payload | Cuándo |
|---|---|---|
nuevo_mensaje |
fono, fono_origen, fono_destino |
Llegó o se envió un mensaje en la línea fono. |
estado_linea |
fono, estado: conectado · desconectado · esperando_qr |
La línea cambió de estado de sesión. |
qr_linea |
fono, qr (string a renderizar como QR) |
Hay un QR nuevo disponible durante la vinculación. |
let ultimoId = 0; const ws = new WebSocket('wss://api.chapp.cl/ws?token=__TOKEN_WS__'); ws.onmessage = async (e) => { const data = JSON.parse(e.data); if (data.evento === 'nuevo_mensaje') { // El evento avisa, no trae el contenido. Se va a buscar // con el endpoint del punto 3, desde el último id conocido. const r = await fetch( 'https://www.chapp.cl/api/v1/mensajes/__FONO__/nuevos?desde=' + ultimoId, { headers: { 'Authorization': 'Bearer ' + TOKEN } } ); const j = await r.json(); ultimoId = j.ultimo_id; j.mensajes.forEach(procesar); } if (data.evento === 'estado_linea') console.log(data.fono, data.estado); if (data.evento === 'qr_linea') renderizarQr(data.qr); }; ws.onclose = (e) => { if (e.code === 4401) return console.error('Token inválido'); setTimeout(reconectar, 5000); };
nuevo_mensaje es una notificación, no el mensaje. Trae solo los números involucrados. Mantén un respaldo por polling cada 5 s con /nuevos?desde= por si la conexión se cae — es como opera el chat de la plataforma.
Dos endpoints: la conversación con un contacto, y todo lo posterior a un id (para sincronizar).
| Parámetro | Descripción | |
|---|---|---|
limite | opcional | Cuántos mensajes traer, 1–200. Por defecto 50. |
desde | opcional | Solo mensajes con id mayor a este. |
curl "https://www.chapp.cl/api/v1/mensajes/__FONO__/56911111111?limite=20" \ -H "Authorization: Bearer $CHAPP_TOKEN"
Devuelve los mensajes posteriores a desde en orden ascendente, junto con ultimo_id para encadenar la siguiente consulta. Es el endpoint que va de la mano del WebSocket.
{
"ok": true,
"linea": "__FONO__",
"ultimo_id": 1482,
"mensajes": [
{
"id": 1482,
"direccion": "recibido",
"timestamp": 1753012800,
"fecha": "2026-07-20T10:00:00-04:00",
"fono_origen": "56911111111",
"fono_destino": "__FONO__",
"push_name": "Juan Pérez",
"es_grupo": false,
"grupo_jid": null,
"tipo": "text",
"mensaje": "Hola, ¿tienen stock?",
"id_mensaje": "3EB0...",
"visto": null
}
]
}
| Campo | Descripción |
|---|---|
id | Id interno. Es el que se usa para paginar con desde. |
direccion | enviado (saliente) o recibido (entrante). |
timestamp | Epoch Unix en segundos. |
fecha | El mismo instante en ISO 8601, por comodidad. |
push_name | Nombre que el contacto tiene puesto en WhatsApp. |
es_grupo / grupo_jid | Si el mensaje pertenece a un grupo, y cuál. |
tipo | Tipo de contenido (text, imagen, documento). |
mensaje | Contenido, o nombre del archivo si es adjunto. |
id_mensaje | Identificador del mensaje en WhatsApp. |
{
"ok": true,
"lineas": [
{
"uuid": "__UUID__",
"fono": "__FONO__",
"alias": "Ventas",
"puerto": __PUERTO__,
"vinculada": true,
"estado": "conectada"
}
]
}
estado puede ser conectada, desconectada o desvinculada. Consulta una línea puntual con GET /api/v1/lineas/{'{'}linea{'}'}.
| Campo | Descripción | |
|---|---|---|
fono | requerido | Número de la línea, formato internacional sin +. |
alias | requerido | Nombre para identificarla en el panel. |
guardar_mensajes | opcional | Guardar el historial de mensajes. |
guardar_adjuntos | opcional | Guardar los archivos adjuntos. |
curl -X POST "https://www.chapp.cl/api/v1/lineas" \ -H "Authorization: Bearer $CHAPP_TOKEN" \ -d "fono=56922222222" \ -d "alias=Ventas" \ -d "guardar_mensajes=1"
Responde 201. Si el número ya está registrado, 409 con error: "linea_duplicada".
La línea nace sin vincular: hay que escanear un QR desde WhatsApp del teléfono para que pueda enviar. El QR tarda unos segundos en generarse y se renueva periódicamente.
// Todavía no hay QR — reintenta { "ok": true, "estado": "esperando_qr", "qr": null, "mensaje": "…" } // QR listo: renderízalo como código QR en tu interfaz { "ok": true, "estado": "esperando_qr", "qr": "2@AbCd…" } // Ya quedó vinculada: deja de consultar { "ok": true, "estado": "vinculada", "qr": null }
qr es el string crudo de WhatsApp: renderízalo con cualquier librería de QR. También llega por el WebSocket como evento qr_linea, que evita el polling.
| # | Paso |
|---|---|
| 1 | POST /api/v1/lineas — crea la línea y devuelve su uuid. |
| 2 | GET /api/v1/lineas/{'{'}uuid{'}'}/qr — consulta hasta que qr venga no nulo. |
| 3 | Escanea el QR desde WhatsApp del teléfono. |
| 4 | El estado pasa a vinculada / conectada. Ya puede enviar. |
Cada línea tiene su propia clave, generada automáticamente al crearla, que le permite hablar directo con el servidor de mensajería. Es el mecanismo antiguo: solo sirve para enviar, no valida a qué empresa perteneces y responde texto plano. Para integraciones nuevas usa el token de integración y los endpoints de la API v1.
curl -X POST "https://api.chapp.cl:__PUERTO__" \ -d "apikey=__APIKEY__" \ -d "fono=56911111111" \ -d "mensaje=Hola" # Responde texto plano, SIEMPRE con HTTP 200: # "OK MENSAJE ENVIADO CORRECTAMENTE" → enviado # "ERROR AL AUTENTICAR" → apikey incorrecta
OK / ERROR). La API v1 no tiene este problema.
Estas capacidades existen dentro de la plataforma pero aún no están expuestas por la API v1. Si necesitas alguna, avísanos para priorizarla.
| Función | Estado actual |
|---|---|
| Webhook de mensajes entrantes | No existe. Hay que mantener el WebSocket abierto o hacer polling con /nuevos?desde=. Un webhook HTTP sería bastante más simple de integrar. |
| Token del WebSocket por empresa | Hoy es un token global de plataforma, compartido y visible en el navegador. Debería emitirse por empresa, igual que los de la API v1. |
| Contactos (CRUD) | Solo desde el panel: listar, crear, actualizar y bloquear. |
| Pipeline / CRM | Solo desde el panel: etapas, tags, notas y mover contactos. |
| Grupos | Crear grupo, agregar participantes y cambiar imagen existen en el servidor de mensajería, pero no están expuestos en la API v1. |
| Eliminar / desvincular / reiniciar líneas | Solo desde el panel. Son destructivas y conviene exponerlas recién con permisos por alcance. |
| Eliminar mensajes | Solo desde el panel. |
| Descargar adjuntos | Los mensajes traen el nombre del archivo, pero bajarlo requiere sesión del panel. |
| Funciona como canal en la plataforma, pero no tiene API de envío ni de lectura. | |
| Confirmación de entrega | El envío confirma que se aceptó, pero no devuelve el id del mensaje ni informa entregado/leído. |
| Permisos por alcance | Un token da acceso a todo lo de la empresa. No se puede emitir uno de solo lectura, ni acotado a una línea. |