Widget Marco · Documentación técnica

Guía de implementación

El widget de Marco se instala de tres formas distintas, según dónde viva su aplicación y dónde deban permanecer los datos de la sesión. Esta guía cubre las tres. Elija su flujo y siga solo esa columna: cada una es autocontenida.

Versión de mensajes 1 Producción production.deep-talk.ai Pruebas staging.deep-talk.ai

Paso cero

Elegir el flujo

La pregunta que decide es dónde se crea la sesión y qué recibe el navegador. Todo lo demás se deriva de eso.

Flujo A

Página pública

Sitios sin autenticación. Dos etiquetas HTML y nada más: la sesión arranca sola. La confianza se apoya en el origen del dominio y en el appId.

Flujo B

Página privada

Su frontend ya gestiona usuarios autenticados. Inicia la sesión con init() y una identidad firmada con HMAC por su backend. Sin endpoint propio.

Flujo C

Servidor a servidor

El contexto clínico nunca toca el navegador. Su backend crea la sesión por API y el frontend solo recibe un token opaco de un solo uso.

Comparación

Página pública Página privada Servidor a servidor
Quién crea la sesión El script, automáticamente Su frontend, vía init() Su backend, por API
Qué recibe el navegador Solo el appId user_id, firma y metadata Solo un token opaco
Autenticación Origen + appId Firma HMAC-SHA256 Bearer client_secret
Incrustación Script + elemento <marco> Script + API JavaScript <iframe> con token en la URL
Backend requerido No Solo para firmar
Contexto clínico No aplica Pasa por el navegador, firmado Nunca sale del servidor
Renderizado inline No, siempre flotante Sí, vía component_id Sí, usted controla el iframe
Sesiones reanudables Sí, vía localStorage Sí, por user_id/external_id No, por diseño

Los estados se escriben igual en los tres flujos

Los tres publican los nombres de estado en mayúscula (READY, SESSION_CREATED, STARTED), así que un manejador escrito para un flujo se puede reutilizar en otro sin normalizar. Lo que cambia entre flujos es qué estados existen, no cómo se escriben.

Guía por flujo

Instalación

La sesión se identifica por origen (dominio) y appId, ambos configurados de antemano con Marco. El widget se carga con una sola etiqueta de script y arranca por sí mismo, sin llamar a ninguna API de JavaScript.

Cuándo usarlo
Landings, sitios informativos, portales sin login.
Configuración
Vive en el backend de Marco, asociada a su appId.
Renderizado
Siempre burbuja flotante anclada al viewport.
Backend propio
No se necesita ninguno.
Secuencia del flujo entre Navegador, Marco NAVEGADOR MARCO 01 Carga widget.js con el app-id en la etiqueta <marco> 02 Verifica el origen compara el dominio contra la lista del appId 03 SESSION_CREATED o SESSION_RELOADED si ya había una en localStorage 04 Conversación STARTED · RESPONSE_CREATED 05 COMPLETED created_at y finished_at
El navegador es el único actor del lado del cliente. No hay backend suyo en la secuencia: el origen del dominio y el appId son toda la verificación.

Integración básica

Agregue estas dos etiquetas a su página, una en el head y otra en el body. Eso es todo: no se requiere ninguna otra llamada.

html
<html>
  <head>
    <script
      src="https://production.deep-talk.ai/widget/widget.js"
      defer>
    </script>
  </head>
  <body>
    <marco
      id="my-marco-widget"
      api-base-url="https://production.deep-talk.ai/widget"
      app-id="YOUR_APP_ID">
    </marco>
  </body>
</html>

Sustituya YOUR_APP_ID por el identificador que Marco le entrega. Cargar el script es suficiente: el widget aparece automáticamente como burbuja flotante.

La configuración vive en el backend de Marco

Colores de marca, comportamiento y demás ajustes se configuran una vez con su contacto en Marco y quedan asociados a su appId. Un cambio se actualiza del lado de Marco y se refleja automáticamente en su página, sin volver a desplegar. La URL del script también es estática: las correcciones del producto llegan solas.

Ciclo de vida y persistencia

La sesión se crea en cuanto el widget termina de cargar. Cada navegador mantiene una única conversación continua, identificada por su propio session_id.

Identificador anónimo

El widget genera un identificador de sesión aleatorio la primera vez que se carga en un navegador y lo guarda en localStorage. No está vinculado a ninguna identidad real: solo permite reanudar la conversación en ese mismo navegador y sirve como identificador de correlación para eventos y auditoría.

Reanudación

Recargar la página, navegar y volver, o cerrar y reabrir la pestaña o el navegador (mismo perfil): en todos esos casos el widget encuentra el identificador guardado y, si la sesión sigue dentro de su tiempo de espera por inactividad, reanuda la conversación y publica SESSION_RELOADED en lugar de SESSION_CREATED.

Tiempo de espera por inactividad

Una sesión sin actividad más allá de su límite configurado se cierra automáticamente. La siguiente carga del widget en ese navegador ya no encuentra sesión que reanudar y publica SESSION_CREATED.

Cierre explícito

Por defecto la sesión solo termina por inactividad o por acción del usuario dentro del widget. Si necesita cerrarla de forma programática, use MarcoWidget.finish().

API opcional: MarcoWidget

Para la mayoría de las integraciones, la sección anterior es todo lo necesario. Si su aplicación necesita reaccionar a eventos o cerrar la sesión explícitamente, el script expone un namespace global opcional, MarcoWidget, en window.

javascript
MarcoWidget.on('READY', function () {
  console.log('Widget Marco listo');
});

MarcoWidget.on('SESSION_CREATED', function (msg) {
  console.log('Nueva sesión:', msg.session_id);
});

// Cierra la sesión y limpia el identificador de localStorage.
// La próxima carga inicia una sesión nueva.
MarcoWidget.finish();

MarcoWidget.on() suscribe un callback a los eventos de estado y gestiona la verificación de origen por usted. En este flujo la sesión ya está activa al cargar el script, así que MarcoWidget sirve únicamente para escuchar eventos y, opcionalmente, cerrar la sesión.

Eventos

El widget publica el estado de la sesión con postMessage, emitido desde su iframe hacia la ventana que lo contiene. MarcoWidget.on() es un wrapper sobre ese mismo flujo.

Estado Se publica cuando
READY El script terminó de cargar. La sesión aún se está creando.
SESSION_CREATED Se creó una sesión nueva para este navegador.
SESSION_RELOADED Se encontró y reanudó una sesión activa existente en este navegador.
STARTED El visitante envió una consulta y la interacción está en curso.
RESPONSE_CREATED El agente produjo una nueva respuesta dentro de la sesión activa.
COMPLETED La sesión finalizó normalmente. Incluye created_at y finished_at.
CANCELLED Finalizó antes de tiempo: acción del visitante, finish(), o inactividad.
ERROR La sesión o la llamada en curso finalizó por una falla.

Estados terminales

COMPLETED, CANCELLED y ERROR son terminales. Ninguno puede ir seguido de más actividad en la misma sesión, y la siguiente carga del widget publicará SESSION_CREATED.

Estructura del mensaje

READY, STARTED, CANCELLED y ERROR comparten esta estructura base. session_id está ausente en READY y presente desde SESSION_CREATED o SESSION_RELOADED en adelante.

json
{
  "source": "marco-widget",
  "version": 1,
  "type": "STATE_CHANGE",
  "state": "READY",
  "session_id": "sess_9f2a1c",
  "timestamp": "2026-08-20T14:32:04Z"
}

COMPLETED agrega dos campos sobre la base: created_at y finished_at.

Errores

Los errores llegan como un evento de estado ERROR, en la misma estructura anterior, con los campos code y message.

Código Causa habitual
unauthorized_origin La solicitud provino de un origen fuera de la lista permitida para este appId.
invalid_app_id El appId no corresponde a ninguna cuenta configurada, o fue omitido.
rate_limited Se superó el límite de solicitudes para este appId o esta dirección IP.
network_error La solicitud al backend de Marco falló: conectividad o timeout.

Seguridad

El origen es la señal de confianza primaria de este flujo. Cada carga del widget se compara contra los dominios autorizados para el appId indicado, y toda solicitud desde un origen fuera de esa lista se rechaza con unauthorized_origin. Marco aplica además límites de tasa por appId y por dirección IP.

El script y sus llamadas se sirven únicamente por HTTPS, y su aplicación debe servirse por HTTPS en todos los entornos. El widget se ejecuta dentro de un iframe en sandbox con permisos mínimos, gestionado automáticamente por el script cargador.

Si su aplicación aplica una Content-Security-Policy, autorice el dominio del widget en las tres directivas que gobiernan la carga del script, la incrustación del iframe y las llamadas de red del widget.

http
Content-Security-Policy:
  script-src  https://production.deep-talk.ai;
  frame-src   https://production.deep-talk.ai;
  connect-src 'self' https://production.deep-talk.ai;

Común a los tres flujos

Manejo de datos

Marco no utiliza el contenido de las conversaciones ni los metadata de las sesiones para entrenar, ajustar ni mejorar ningún modelo.

El contenido de la sesión y los registros de auditoría se conservan según plazos distintos, porque responden a propósitos diferentes.

Datos Propósito Retención
Contenido de la sesión metadata, transcripción y resumen. Sirve a la consulta misma. Eliminado dentro de las 48 horas posteriores al estado terminal de la sesión.
Registros de auditoría Monitoreo de seguridad y evidencia de cumplimiento. session_id, external_id cuando aplica, marcas de tiempo y estado final, sin contenido clínico. 6 años desde su creación, alineado con el estándar de la industria para registros de acceso en salud.

session_id es el identificador de correlación en los tres flujos, y vincula los eventos de una sesión, las entradas de auditoría y la carga útil de COMPLETED. En el flujo de página privada, external_id lo acompaña cuando se usa. No existe un ID de correlación independiente.

Estabilidad de los códigos de error

En los tres flujos, code es un identificador estable y seguro para usar en lógica de negocio; pueden agregarse nuevos códigos con el tiempo, así que incluya siempre un caso por defecto. message es para registros y depuración: su texto exacto puede cambiar y no debe compararse por patrón.

Común a los tres flujos

Entornos

Marco ofrece dos entornos, con las mismas rutas en ambos hosts.

Entorno Host
Pruebas (staging) staging.deep-talk.ai
Producción production.deep-talk.ai
text
// Página pública y página privada
Script:             https://<host>/widget/widget.js
API base URL:       https://<host>/widget

// Servidor a servidor
Creación de sesión: POST https://<host>/launch_token
Iniciar chat:       GET  https://<host>/chat?launch_token=...

Aislamiento entre entornos

Cada entorno tiene sus propias credenciales y su propia lista de orígenes autorizados, y no existen rutas entre entornos para ninguna de ellas.

Flujo Credencial por entorno
Página pública Un appId propio. Un appId de staging no es válido contra el host de producción.
Página privada Un secreto compartido propio. Una firma calculada con el secreto de staging es rechazada en producción.
Servidor a servidor Un client_secret propio. Un launch_token solo es válido en el entorno que lo emitió.

Guarde y rote las credenciales de cada entorno por separado.

Rendimiento

Las métricas de rendimiento y los umbrales fijos, incluido el tiempo hasta el primer token y cualquier otra garantía de latencia acordada, se definen en el acuerdo SLA, un documento independiente de esta guía.

Común a los tres flujos

Antes de la puesta en producción

Antes de activar cualquiera de los tres flujos, comparta lo siguiente con su contacto en Marco.

Qué necesitamos Para qué Aplica a
Origen(es) donde se embebe el widget Verificación de origen. Es la lista contra la que se compara cada carga o llamada. Los tres flujos
Colores de marca primario y secundario Personalizar el tema de la interfaz para adaptarlo a su sitio. Los tres flujos
Duración de sesión Tiempo de espera por inactividad y duración máxima, si los valores por defecto no le sirven. Los tres flujos
Forma del objeto metadata en COMPLETED Acordar la salida estructurada que su integración necesita recibir al cerrar la sesión. Servidor a servidor

Marco le entrega a cambio las credenciales de cada entorno: el appId en el flujo de página pública, el secreto compartido en página privada, y el client_secret junto con el app_id en servidor a servidor.

¿Dudas sobre cuál flujo le corresponde?

Escríbanos a sales@marco.care con una descripción de dónde vive su aplicación y qué datos de contexto necesita pasar. Le confirmamos el flujo y le entregamos las credenciales de staging para que pueda probar antes de comprometerse.