Eventos
Conectar vía WebSocket
Upgrade /ws/:instance, protocolo, autenticación, validación de Origin, heartbeat y reconexión
GET
Conectar vía WebSocket
Auth:
Validación de Origin (
Independiente del CORS (que solo afecta al REST), el WebSocket tiene su propia allowlist controlada por la variable de entorno
Sin PONG en 60s → el servidor cierra la conexión. No hay session resume: el cliente debe reconectar con backoff y los eventos perdidos durante el gap no regresan.
La mayoría de las librerías WebSocket (
El servidor no consume mensajes enviados por el cliente (solo PONG y close). Enviar payloads JSON desde el cliente al servidor no tiene efecto.
TokenAccount o TokenInstance (header o query) • Protocolo: WSS/WS • Rate-limit: Global (100/min en el upgrade)
Descripción
Endpoint de upgrade HTTP → WebSocket para recibir eventos en tiempo real. Los frames enviados por el servidor usan el mismo envoltorio que los webhooks: un JSON de texto conevent, data e instanceData.
La configuración (habilitar, filtrar eventos, alternar mediaBase64) se hace en POST /api/events/websocket/:instance. Esta página cubre solo la capa de conexión.
Ejemplos cURL (handshake)
websocat o wscat te dan una experiencia interactiva. cURL solo es útil para inspeccionar el handshake.
Handshake (inspección)
Realiza el handshake bruto de upgrade HTTP→WebSocket solo para inspeccionar status, cabeceras y confirmar que la instancia está aceptando conexiones. No mantiene el canal abierto: es una sonda one-shot.wscat (interactivo)
Abre una sesión WebSocket interactiva real conwscat (o un cliente equivalente en cada lenguaje) e imprime cada frame JSON recibido. La forma más práctica de depurar eventos en tiempo real durante el desarrollo.
Ejemplos de cliente
- Browser (JS)
- Node.js (ws)
- Python (websockets)
- Go (gorilla)
Los navegadores no permiten cabeceras personalizadas en
WebSocket, usa ?token=:Envoltorio de los frames recibidos
Cada frame de texto es un JSON idéntico al webhook:instanceData.token es el token propio de la instancia, útil cuando un cliente consume varias instancias y necesita identificar el origen o realizar llamadas REST de regreso.
Parámetros de ruta
Nombre de la instancia. Debe existir y tener WebSocket habilitado.
Cabeceras
| Nombre | Requerido | Ejemplo | Descripción |
|---|---|---|---|
token o Authorization | sí, salvo si se usa ?token= | token: a1b2c3... | Auth flexible. |
Upgrade | sí | websocket | Requerido por el protocolo. |
Connection | sí | Upgrade | Idem. |
Sec-WebSocket-Key | sí | (generado por el cliente) | Idem. |
Sec-WebSocket-Version | sí | 13 | Único valor aceptado. |
Origin | condicional | https://app.example.com | Validado contra allowlist en navegadores. Los clientes sin Origin son aceptados. |
Parámetros de consulta
Token de autenticación. Requerido cuando el cliente no puede enviar
token o Authorization en la cabecera (caso del navegador).Precondiciones
- Existe una configuración en
websocket_configspara la instancia (creada víaPOST /api/events/websocket/:instance) conenabled=true. - Token válido,
TokenAccountoTokenInstancede la instancia (misma matriz que REST). - Si proviene de un navegador, el
Origindel request está enALLOWED_WS_ORIGINSo es same-origin.
Autenticación
ValidateTokenFlexible() acepta el token desde tres fuentes:
| Fuente | Ejemplo |
|---|---|
Header token | token: a1b2c3d4-... |
Header Authorization: Bearer | Authorization: Bearer a1b2c3d4-... |
Query param ?token= | wss://api.example.com/ws/myinst?token=a1b2c3d4-... |
Validación de Origin (ALLOWED_WS_ORIGINS)
Independiente del CORS (que solo afecta al REST), el WebSocket tiene su propia allowlist controlada por la variable de entorno ALLOWED_WS_ORIGINS.
ALLOWED_WS_ORIGINS | Comportamiento |
|---|---|
| Vacío / indefinido | Solo se acepta same-origin (Origin igual a Host). |
"https://app.example.com,https://dashboard.example.com" | Allowlist explícita, separada por comas. |
Los clientes sin la cabecera
Origin (curl, Postman, libs de Node/Python/Go) siempre son aceptados, Origin es un mecanismo del navegador, no universal. La seguridad para esos clientes proviene del token.Los bloqueos se registran como WebSocket upgrade blocked from origin <origin> (host <host>). El cliente recibe 403 Forbidden (sin body) y se cierra el TCP.Heartbeat
| Lado | Mensaje | Intervalo |
|---|---|---|
| Servidor → cliente | PING | cada ~54s (pingPeriod) |
| Cliente → servidor | PONG | en 60s (pongWait) |
gorilla/websocket, Node ws, Python websockets, navegador nativo) responden PONG automáticamente, el cliente casi nunca necesita implementar esto manualmente.
Buffers y backpressure
| Límite | Valor | Efecto al excederse |
|---|---|---|
| Read buffer (mensaje máximo del cliente) | 4096 bytes | El servidor cierra la conexión. |
| Send buffer por cliente | 256 mensajes | Cliente lento es descartado por el hub. |
Catálogo de eventos
Los 6 tipos posibles (message.exchange, message.status, call.update, group.flow, instance.state, label.update) comparten este envoltorio. Esquemas y ejemplos completos en /es/api/events/catalog.
Reconexión y resiliencia
El servidor no replays los eventos perdidos durante outages, el cliente WS es fire-and-forget. Para garantía de entrega, usa webhook en paralelo.Siempre ten un handler de
close con reconexión automática, idealmente con backoff exponencial y jitter, limitado a 30s entre intentos.Maneja los close codes:
1006 (network drop), 1011 (server error), 1008 (policy violation), 4xxx (custom, raros).Catch up vía REST después de reconectar, usa
GET /api/chat/history/:instance para traer mensajes recientes que pudieron perderse.Buffer local en el cliente, nunca bloquees el handler de
message con operaciones lentas; encola y procesa en otro thread/worker.Efectos colaterales
- Hub en memoria: el handler registra al cliente en
WebSocketHub(map[instanceName]map[*WebSocketClient]bool). La conexión no se persiste. Un restart del proceso los descarta a todos. - Goroutines: cada conexión genera 2 goroutines (
WritePumpyReadPump) que viven hasta el close. - Sin escritura en BD: el upgrade en sí no escribe nada. Los broadcasts subsiguientes pasan por el dispatcher de webhook (que toca la BD) en paralelo, el WS es solo un fanout adicional.
- Métricas Prometheus: contadores de conexiones activas por instancia (consulta /es/api/observability/overview).
Notas
- Sin retry/persistencia: un cliente offline por 5 min pierde 5 min de eventos. Para garantías, usa webhook.
- Multi-cliente: varios clientes pueden conectarse a la misma instancia. Todos reciben todos los eventos (broadcast). No hay atomicidad para “quién procesó primero”.
- Filtros son globales por instancia: el filtro
events[]configurado enPOST /api/events/websocket/:instancese aplica a todos los clientes, no es configurable por conexión. - Tamaño de frame del servidor: el límite de 4096 bytes solo se aplica a mensajes enviados por el cliente. El servidor envía frames potencialmente mucho más grandes (media en base64 supera fácilmente 100KB). Lee frames sin límites en el cliente.
Cuándo usar webhook vs WebSocket
| Criterio | WebSocket | Webhook |
|---|---|---|
| Latencia | ms (push directo) | 100-500ms (HTTP + cola) |
| Persistencia | Eventos perdidos si offline | Cola + 5x retry + DLQ |
| Multi-consumidor | Varios clientes en broadcast | Hasta 3 webhooks habilitados (labels) |
| Auth | Token en el upgrade | Header Authorization opcional |
| Setup | Cliente WS + config habilitada | Endpoint HTTP público + URL |
| Dev-friendliness | Excelente (wscat, DevTools) | Requiere tunneling en dev |
Errores antes del upgrade
Todos ocurren antes de101 Switching Protocols, el cliente recibe una respuesta HTTP normal.
| HTTP | error.message | Cuándo |
|---|---|---|
| 400 | WebSocket is not enabled for this instance. Configure it first using POST /api/events/websocket/<instance> | Sin config o enabled=false. |
| 401 | Missing token in header, Authorization header, or query parameter | Ninguna de las 3 fuentes proporcionó un token. |
| 401 | Invalid token | Token inválido. |
| 403 | , (body vacío, desde el upgrader) | Origin fuera de la allowlist. |
| 404 | Instance not found | :instance no existe. |
| 429 | Rate limit exceeded. Try again later. | Rate-limit global. |
| 500 | Failed to get instance / Failed to get websocket configuration | Error de base de datos. |
Errores después del upgrade
Después de101, cualquier falla de protocolo cierra la conexión con un close code estándar (1001 going away, 1006 abnormal closure, 1011 server error). El servidor no escribe un body, el cliente lo maneja vía el close code.
Causas comunes:
- Frame del cliente mayor a 4096 bytes.
pongWait(60s) expiró sin respuesta al PING.- Send buffer del cliente lleno (cliente lento), el hub lo desregistra.
- La instancia fue eliminada mientras el cliente estaba conectado.
Referencias
Configurar WebSocket
POST /api/events/websocket/:instanceCatálogo de eventos
Esquemas de los 6 tipos.
Resumen de eventos
Webhook vs WebSocket, envoltorio, mejores prácticas.
Autenticación
Matriz de tokens, header
token, ?token=.