Controla el widget desde tu página y mantiene cada conversación resiliente — reconexión automática, reintento de mensajes, detección de offline, timeouts y manejo de errores amigable.
Una vez que se carga el script del widget, Companin registra un puente de host en window.CompaninWidgetHost. Le da a tu página una pequeña API sin dependencias para abrir y cerrar el widget, enviar mensajes, leer estado y suscribirse a eventos de ciclo de vida — sin acoplar tu código a los internos del widget.
El puente también añade una capa de fiabilidad sobre el widget: los mensajes se encolan mientras están offline, se reintentan automáticamente cuando la conexión vuelve, se agotan si no llega respuesta, y el widget se reconecta automáticamente después de errores transitorios. Cada cambio de estado se presenta como un evento DOM para que puedas reaccionar en tu propia interfaz de usuario.
Llama a estos métodos en window.CompaninWidgetHost después de que el widget se haya cargado. Son no-op seguros cuando el widget aún no está listo, así que nunca necesitas proteger cada llamada.
open() / close() / toggle() — Mostrar, ocultar o voltear el panel del widget.sendText(text) — Enviar un mensaje de texto plano como visitante.sendPayload(payload) — Enviar un mensaje estructurado o un objeto de comando.sendSafe(payload) — Enviar a través de la capa de fiabilidad — encolado cuando está offline y pasado a través de cualquier interceptor.sendWithTimeout(payload, ms) — Enviar y emitir un evento de timeout si no llega respuesta dentro de ms (por defecto 10000).intercept(fn) — Registrar una función que puede reescribir o cancelar mensajes salientes; devuelve una función de cancelación.getState() — Leer el estado actual del host: estado abierto, último mensaje enviado y recibido, bandera online y historial de comandos.getIsOnline() — Si el host considera actualmente que la conexión está online.drainRetryQueue() — Vaciado manual de cualquier mensaje encolado mientras está offline.cleanup() — Desuscribirse de cada oyente — llama antes de eliminar el widget.<script> window.addEventListener('load', function () { var host = window.CompaninWidgetHost; if (!host) return; host.open(); host.sendText('Hi! I have a question about pricing.'); console.log(host.getState()); });</script>Deja que el widget reconozca a tus usuarios registrados para que las conversaciones sean personalizadas y se restauren en sus dispositivos. Tu servidor firma un token de corta duración que el widget entrega a Companin; Companin lo verifica y vincula la sesión a ese usuario.
1. Obtén tu secreto de firma. En el panel, abre Instalar → Usuarios registrados → Generar secreto, luego cópialo en el entorno de tu servidor. Nunca lo expongas en el código del navegador.
2. Firma un token en tu servidor — un JWT HS256 de corta duración que lleva el id del usuario (sub), correo electrónico y nombre:
const jwt = require('jsonwebtoken'); // npm i jsonwebtokenfunction signUserToken(user) { return jwt.sign( { sub: String(user.id), email: user.email, name: user.name }, process.env.COMPANIN_EMBED_SECRET, { algorithm: 'HS256', expiresIn: '5m' } );}3. Entrega el token al widget. En una página renderizada por el servidor, agrégalo a la etiqueta de script como data-user-token; en una aplicación de una sola página, llama a identify() después de que el usuario inicie sesión:
<!-- Option A: server-rendered page — put the signed token on the script tag --><script src="https://YOUR_WIDGET_HOST/widget.js" data-widget-key="wgt_your_key" data-user-token="SERVER_SIGNED_JWT"></script><!-- Option B: after login — fetch a fresh token and call identify() --><script> fetch('/api/widget-user-token') .then(function (r) { return r.json(); }) .then(function (data) { if (data.token) window.CompaninWidget.identify({ token: data.token }); });</script>Un token malo o caducado es ignorado — el widget simplemente permanece anónimo, así que es seguro intentar siempre la identificación.
Usa sendSafe en lugar de sendText cuando la entrega importa. El host observa los eventos online y offline del navegador; mientras está offline, los mensajes se añaden a una cola de reintentos y se dispara un evento companin:widget:offline. Tan pronto como la conexión vuelve, la cola se drena automáticamente en orden y se dispara companin:widget:online.
window.CompaninWidgetHost.sendSafe('Track my order #1234');window.addEventListener('companin:widget:offline', function () { showBanner('You are offline — your message will send automatically.');});window.addEventListener('companin:widget:online', function () { showBanner('Back online.');});window.addEventListener('companin:widget:retryDrained', function (e) { showBanner(e.detail.count + ' queued message(s) sent.');});Envuelve un envío en sendWithTimeout para protegerte contra una respuesta que nunca llega. Si no se recibe respuesta dentro del timeout (10 segundos por defecto), se dispara un evento companin:widget:timeout para que puedas mostrar un aviso de reintento amigable en lugar de dejar al usuario esperando.
window.CompaninWidgetHost.sendWithTimeout('Are you there?', 8000);window.addEventListener('companin:widget:timeout', function () { showRetryPrompt('That took longer than expected. Try again?');});Cuando el widget informa un error, el host intenta reconectar hasta tres veces con un retroceso creciente (1.5s, 3s, luego 4.5s), emitiendo companin:widget:reconnecting en cada intento. Una respuesta exitosa restablece el contador; si todos los intentos fallan, se dispara companin:widget:reconnectFailed para que puedas retroceder de manera elegante.
window.addEventListener('companin:widget:reconnecting', function (e) { console.log('Reconnecting — attempt', e.detail.attempt, 'in', e.detail.delay, 'ms');});window.addEventListener('companin:widget:reconnectFailed', function () { showBanner('We could not reconnect. Please refresh the page.');});Registra un interceptor con intercept(fn) para inspeccionar, reescribir o cancelar cada mensaje saliente. Devuelve una carga útil modificada para cambiarlo, devuelve false para cancelar el envío, o no devuelvas nada para dejarlo pasar sin cambios. intercept devuelve una función de cancelación.
const stop = window.CompaninWidgetHost.intercept(function (payload) { if (typeof payload === 'string') { if (!payload.trim()) return false; // cancel the send return payload.replace(/[\w.+-]+@[\w-]+\.[\w.-]+/g, '[email]'); } return payload;});// Later, to remove the interceptor:stop();El host vuelve a emitir cada cambio de ciclo de vida del widget como un DOM CustomEvent en window, para que puedas reaccionar sin mantener una referencia al widget. Los datos relevantes están en event.detail.
companin:widget:open / close — El panel del widget se abrió o cerró.companin:widget:message / response — El visitante envió un mensaje, o el agente respondió.companin:widget:authFailure — La autenticación con el backend falló.companin:widget:error — El widget informó un error.companin:widget:offline / online — El navegador perdió o recuperó su conexión.companin:widget:queued / retryDrained — Un mensaje fue encolado mientras estaba offline, o la cola fue vaciada.companin:widget:reconnecting / reconnectFailed — Un intento de reconexión automática comenzó, o se agotaron todos los intentos.companin:widget:timeout — Un mensaje no recibió respuesta dentro de su timeout.window.addEventListener('companin:widget:message', function (e) { console.log('Visitor sent:', e.detail.message);});window.addEventListener('companin:widget:response', function (e) { console.log('Agent replied:', e.detail.response);});window.addEventListener('companin:widget:authFailure', function (e) { console.warn('Widget auth failed:', e.detail.error);});El código desacoplado puede controlar el widget despachando un evento companin:widget:command en lugar de llamar a la API directamente. Envía una cadena para entregar un mensaje, o un objeto de detalle como { action: 'open' } o { text: 'Hola' }. Esto es útil para etiquetas de análisis, GTM u otros scripts que no deberían importar el widget.
// Open the widget from anywhere — no widget reference needed.window.dispatchEvent(new CustomEvent('companin:widget:command', { detail: { action: 'open' }}));// Or send a message.window.dispatchEvent(new CustomEvent('companin:widget:command', { detail: { text: 'I need help with billing' }}));Establece window.__COMPANIN_WIDGET_WEBHOOK_URL a un endpoint de colector y el host reenvía eventos de apertura, cierre, mensaje y respuesta a él como JSON a través de navigator.sendBeacon (retrocediendo a un fetch de keepalive). Este es un complemento ligero del lado del cliente a los webhooks del lado del servidor — útil para análisis de primera parte.
// Point the host at your collector before the widget loads.window.__COMPANIN_WIDGET_WEBHOOK_URL = 'https://example.com/collect/widget';