Integraciones
Inserción del widget
Inserte el widget de chat web de HelpStack en cualquier sitio web con una única etiqueta de script.
Datos rápidos#
| Loader | https://helpstack.eu/widget.js |
| Autenticación | Ninguna — los endpoints del widget son públicos |
| Transporte | Socket.IO (principal) + fallback REST |
| Tipo de canal | WEBSITE_CHAT |
| Límite de tasa | ~30 req/min/IP en config/FAQ/status; ~20 msg/min/visitante en el fallback HTTP de mensajes |
| Control de origen | Lista de dominios permitidos opcional por canal (403 si no está permitido) |
El loader obtiene su baseUrl de su propio origen <script src>, por lo que el widget.js servido desde helpstack.eu se comunica automáticamente con HelpStack — no hay nada más que configurar.
Fragmento de inserción#
<script src="https://helpstack.eu/widget.js?id=CHANNEL_ID" async></script>
CHANNEL_ID es el id de su canal WEBSITE_CHAT. El loader se inicializa automáticamente desde el parámetro de consulta ?id= — no se requiere JavaScript adicional.
Cómo obtener el CHANNEL_ID#
En el panel de control, vaya a Settings → Channels, abra (o cree) un canal Website Chat y copie su id usando el botón de copia que aparece junto al canal. Ese id es el que se pasa como ?id=CHANNEL_ID.
Cómo funciona el loader#
- El script lee su propio
<script src>para determinar:baseUrl— el origen del script (p. ej.https://helpstack.eu)channelId— el parámetro de consulta?id=
- Inyecta una burbuja de chat flotante y un
<iframe>que apunta a:{baseUrl}/widget/{channelId}?visitorId={visitorId} - El iframe abre una conexión Socket.IO para mensajería en tiempo real, con un fallback HTTP cuando WebSocket no está disponible.
Identidad del visitante#
El loader genera un id de visitante persistente almacenado en localStorage:
| Clave | Formato del valor |
|---|---|
chat_widget_visitor_id | visitor_<uuid> |
El mismo visitorId se reutiliza en cargas de página y sesiones en ese navegador, de modo que un visitante que regresa se reconecta a su conversación existente. Limpiar localStorage (o usar un navegador/dispositivo diferente) genera un nuevo visitante y un nuevo hilo de conversación.
API programática#
El loader expone un objeto global window.ChatWidget:
window.ChatWidget.init('CHANNEL_ID'); // inicializar manualmente (si no se usa ?id=)
window.ChatWidget.open(); // abrir el panel de chat
window.ChatWidget.close(); // cerrar el panel de chat
window.ChatWidget.toggle(); // alternar entre abierto/cerrado
También está disponible un asistente de cola de comandos para la forma diferida usada en el comentario del fragmento:
window.chat('init', 'CHANNEL_ID');
Identifique en cualquier momento con quién está hablando, para que la IA no tenga que preguntarlo:
window.ChatWidget.identify({ name: 'Ana Novak', email: 'ana@example.com' },
{ plan: 'pro', account_id: '1234' });
Registre una agent tool del lado del cliente para que la IA pueda llamar a código que se ejecuta en su página:
window.HelpStack('registerTool', 'highlight', async ({ section }) => {
document.querySelector(`#${section}`)?.scrollIntoView({ behavior: 'smooth' });
return { highlighted: section };
});
Lo que devuelva el handler es lo que lee la IA. Por debajo, el iframe del widget envía TOOL_EXECUTE por postMessage a la ventana padre y espera un TOOL_RESULT o TOOL_ERROR con el mismo callId; registerTool responde eso por usted. El widget guarda silencio ante cualquier nombre de herramienta para el que no tenga handler, así que un message listener propio que ya tuviera sigue funcionando. Consulte Herramientas de agente personalizadas.
Configuración de apariencia#
La apariencia se configura en el panel de control o por MCP (get_widget_config / update_widget_config, véase MCP), no mediante el fragmento de inserción. El widget la obtiene del servidor (GET /api/widget/[channelId]/config) y la aplica mediante variables CSS. Campos configurables verificados:
| Campo | Notas |
|---|---|
primaryColor | Color hexadecimal. Por defecto #247f7c (el turquesa de la marca) |
position | bottom-right | bottom-left | top-right | top-left |
mobilePosition | Mismo enum que position, aplicado en viewports móviles |
bubbleIconType | custom o un preset |
bubbleIconName | Uno de: message, help, smile, headphone, zap, heart |
bubbleOffsetX | Desplazamiento horizontal de la burbuja |
bubbleOffsetY | Desplazamiento vertical de la burbuja |
greetingMessage | Texto de saludo que se muestra cuando se abre el panel |
avatarUrl | Avatar del agente/marca |
showOnlineStatus | Si se muestra el estado en línea/fuera de línea |
offlineMessage | Mensaje que se muestra cuando el canal está fuera de línea |
productCards | Mostrar los enlaces a productos de las respuestas como tarjetas. Por defecto true. Ver Tarjetas de producto |
Endpoints públicos del widget#
Todos los endpoints son públicos (sin autenticación) y están limitados por IP/visitante.
| Método | Ruta | Propósito |
|---|---|---|
GET | /api/widget/[channelId]/config | Configuración del widget (colores, posición, saludo, avatar, estado). ~30 req/min/IP |
GET | /api/widget/[channelId]/faqs | Hasta 5 chips de FAQ para el saludo. ~30 req/min/IP |
GET | /api/widget/[channelId]/status | Si el canal está actualmente en línea |
POST | /api/widget/[channelId]/upload | Subida de archivos multipart/form-data |
POST | /api/widget/message | Fallback HTTP para enviar un mensaje cuando WebSocket no está disponible. ~20 msg/min/visitante |
Respuesta de subida (POST /api/widget/[channelId]/upload):
{
"storagePath": "...",
"publicUrl": "...",
"fileName": "...",
"fileSize": 12345,
"contentType": "image/png",
"width": 800,
"height": 600
}
width y height solo están presentes para imágenes.
Fallback de mensajes (POST /api/widget/message) — cuerpo:
{
"channelId": "CHANNEL_ID",
"visitorId": "visitor_<uuid>",
"content": "Hello, I need help",
"visitorInfo": {
"userAgent": "...",
"currentUrl": "https://example.com/pricing",
"referrer": "https://google.com"
}
}
visitorInfo es opcional, igual que metadata (el mismo envoltorio opcional que acepta el evento join del socket). El canal debe ser de tipo WEBSITE_CHAT o la solicitud se rechaza. En caso de éxito, el campo data del envoltorio de respuesta contiene { messageId, conversationId, createdAt }. La conversación de un visitante se crea con su primer mensaje, así que la primera llamada correcta es la que hace que exista conversationId; las llamadas posteriores del mismo visitante la reutilizan.
Eventos en tiempo real (Socket.IO)#
El iframe se comunica con el servidor a través de Socket.IO.
Widget → servidor
| Evento | Payload |
|---|---|
join | channelId, visitorId, visitorInfo { userAgent, currentUrl, referrer } |
message | Mensaje saliente del visitante |
typing | Indicador de escritura del visitante |
heartbeat | Keep-alive de presencia |
tool:result | Resultado de la ejecución de una herramienta del lado del cliente |
tool:error | Error de la ejecución de una herramienta del lado del cliente |
Servidor → widget
| Evento | Payload |
|---|---|
joined | conversationId, messages iniciales, config del widget, faqs. La conversación solo se crea cuando el visitante envía su primer mensaje, por lo que para un visitante nuevo conversationId es null y messages está vacío; un visitante que vuelve con una conversación abierta recibe su id y su historial. No dependa del id antes del primer mensaje. |
message | Nuevo mensaje del agente |
agent_typing | Indicador de escritura del agente |
tool:execute | Solicitud para ejecutar una herramienta del lado del cliente en el navegador |
Tarjetas de producto#
Cuando una respuesta enlaza a la página de un producto de tu tienda, el widget muestra el enlace como una tarjeta: imagen, nombre, precio, stock y un botón Añadir al carrito. Funciona como una vista previa de enlace y no requiere configuración. Las respuestas de la IA, los borradores aprobados y las respuestas que escribe tu equipo reciben tarjetas.

De dónde salen los datos. Cuando se entrega la respuesta, HelpStack abre la
página enlazada y lee los datos del producto que tu tienda ya publica para los
buscadores: datos estructurados schema.org Product (JSON-LD o microdatos) y, como alternativa, las
etiquetas Open Graph (og:title, og:image, product:price:amount).
WooCommerce los publica por defecto, igual que la mayoría de los temas de
Shopify. La tarjeta nunca sale del texto de la respuesta, así que la IA no puede
inventarse un precio: un enlace que no carga, o una página que no es un
producto (la página de envíos, una entrada del blog), sigue siendo un enlace
normal.
Las tiendas Shopify conectadas se leen mediante la Shopify Admin API en lugar de la página: el precio, el stock y las variantes vienen directamente de Shopify, y también funciona en una tienda de desarrollo protegida con contraseña.
Qué enlaces se abren. Solo los de tus propios sitios: la lista de dominios permitidos del widget, la página en la que está el visitante y una tienda Shopify o WooCommerce conectada. Cada petición pasa por la misma protección contra direcciones privadas que las herramientas personalizadas, también en las redirecciones. Las páginas se guardan en caché una hora, así que un producto popular cuesta una petición por hora.
Límites. Como máximo 3 tarjetas por respuesta. Si tu tienda no responde en unos segundos, la respuesta se envía sin tarjetas en lugar de tarde.
Los botones
Una tarjeta tiene hasta dos botones. El primero es:
| Situación | Botón principal |
|---|---|
| Agotado | Agotado (desactivado) |
| El visitante tiene que elegir talla o color primero | Elegir opciones: abre la página del producto |
| Tu página puede añadirlo al carrito | Añadir al carrito |
| En otro caso | Ver producto |
El segundo botón es uno tuyo (ver abajo) o Ver producto. Cuando Añadir al carrito funciona, el botón pasa a Añadido · Ver carrito. Los enlaces a tu tienda se abren en la misma pestaña, como cualquier enlace de tu tienda; el chat sigue en la conversación tras el cambio de página.
Shopify y WooCommerce funcionan sin configurar nada. En una tienda Shopify
el widget añade la variante mediante el /cart/add.js de la propia tienda; en
WooCommerce añade mediante la Store API de WooCommerce, que tienen tanto los
temas de bloques como los clásicos (con el endpoint clásico de añadir al carrito
como alternativa), y avisa al minicarrito para que se actualice. Un
producto de Shopify con varias variantes muestra Elegir opciones, salvo que
el enlace apunte a una variante (?variant=123). El carrito integrado solo
añade productos de la tienda en la que está el visitante.
Tus propios botones
Registra un manejador con forProducts: true y aparecerá en cada tarjeta.
Recibe el producto:
<script>
window.HelpStack = window.HelpStack || function () {
(window.HelpStack.q = window.HelpStack.q || []).push(arguments);
};
window.HelpStack('registerAction', 'wishlist', async (product) => {
// product: { url, title, productId, variantId, price, currency, platform, quantity }
await myWishlist.add(product.productId);
return { ok: true };
}, { forProducts: true, label: { en: 'Save for later', es: 'Guardar para después' } });
</script>
| Opción | |
|---|---|
forProducts | true lo convierte en un botón de tarjeta en lugar de un botón «Muéstramelo» |
label | Texto del botón: una cadena o uno por idioma ({ en, sl, de, ... }), elegido según el idioma del navegador del visitante. Como máximo 40 caracteres |
primary | true pone tu botón primero, antes de Añadir al carrito |
El nombre add_to_cart es especial: se convierte en el botón Añadir al
carrito de la tarjeta y sustituye al integrado de Shopify/WooCommerce. Úsalo
con cualquier otro sistema de tienda. Devuelve
{ ok: true, cartUrl: '/cart' } para que Ver carrito lleve a tu página de
carrito, o { ok: false, message } para mostrar un fallo bajo la tarjeta.
Los manejadores siguen las mismas reglas que los botones de acción: 10 segundos por toque, y un error lanzado cuenta como fallo.
Qué sabe la IA
En el chat web se pide a la IA que incluya el enlace de cada producto que
recomienda, solo enlaces obtenidos de una herramienta o de la base de
conocimiento, y como máximo 3 por respuesta. Cuando el visitante toca un botón
de una tarjeta, la IA lo lee en su siguiente turno («tocó Añadir al carrito en
la tarjeta de Bici de trail 29" y funcionó») y puede seguir a partir de ahí.
Cada toque se registra como una fila CLIENT_ACTION en los registros de
herramientas, con el nombre del botón (add_to_cart, view_product,
choose_options, view_cart o el tuyo).
Desactivar las tarjetas
Canales → Apariencia → Tarjetas de producto, o update_widget_config con
productCards: false. Los enlaces siguen siendo enlaces normales.
Lista de dominios permitidos#
Un canal puede restringir qué orígenes pueden cargar el widget. Cuando se configura una lista y el origen solicitante no está en ella, el servidor responde con 403. Configure la lista en la configuración del canal en el panel de control. Déjela vacía para permitir cualquier origen.
Página de ejemplo mínima#
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>HelpStack Widget Demo</title>
</head>
<body>
<h1>My website</h1>
<button onclick="window.ChatWidget.open()">Need help?</button>
<!-- HelpStack widget -->
<script
src="https://helpstack.eu/widget.js?id=CHANNEL_ID"
async
></script>
</body>
</html>