SDK, API REST y MCP de Dashboard Apps

Conversa Labs

Conversa Labs

Última actualización el Aug 26, 2026

Descripción general

Los Dashboard Apps nativos tienen tres contratos separados:

  • El SDK V2 del navegador entrega contexto limitado por capacidades, eventos de solo lectura, comandos seguros e identidad firmada opcional para el backend propio de la app.
  • La API REST permite a administradores o roles personalizados con integration_manage crear, consultar, actualizar, reordenar y eliminar apps e instalaciones.
  • El servidor MCP expone el mismo contrato con cuatro herramientas opcionales en Canales e integraciones → Dashboard Apps.

El SDK autentica solo una identidad breve de la app incrustada; no ofrece un proxy de API genérico. Las escrituras deben pasar por tu backend, autenticado en REST con un token de privilegio mínimo.

Requisitos previos

  • dashboard_apps_native_surfaces habilitada en la cuenta.
  • Dashboard App HTTPS en un origen diferente del dashboard de Conversa Labs para V2.
  • Un administrador o rol personalizado con integration_manage y un api_access_token para gestión REST.
  • Para MCP, perfil cuyo usuario actuante tenga ese permiso y que incluya Dashboard Apps.
  • CSP frame-ancestors que permita el origen exacto de Conversa Labs, sin X-Frame-Options incompatible.

Paso a paso

1. Conecta el SDK V2 en el navegador

Importa el SDK desde la ruta estable y versionada de tu despliegue de Conversa Labs. Redirige al build actual con fingerprint y expone exportaciones con nombre del módulo JavaScript.

import { connect } from '/dashboard-app-sdk/v2.js';

try {
  // El host inyecta parámetros cl_*; origen e instalación se descubren automáticamente.
  // Las opciones explícitas siguen disponibles para pruebas controladas.
  const client = await connect();
  const stop = await client.subscribe(
    ['context.initialized', 'conversation.changed', 'theme.changed'],
    event => console.log(event.context_revision, event.data)
  );

  await client.setHeight(520);
  document.querySelector('#documentacion').addEventListener('click', () => {
    client.openLink('https://app.ejemplo.com/docs');
  });

  // Después: await stop(); client.disconnect();
} catch (error) {
  console.error(error.code);
}

Usa las importaciones con nombre del módulo mostradas arriba. Son el contrato estable para aplicaciones nuevas. Si tu aplicación no puede consumir exportaciones con nombre, el mismo archivo también expone window.ConversaLabsDashboardAppSDK, con las mismas funciones.

Llama a connect() apenas cargue la página, no detrás de autenticación ni de una ida a tu backend. El host inicia el handshake cuando el marco termina de cargar y reintenta hasta 10 segundos; una aplicación que empieza a escuchar después de esa ventana recibe handshake_timeout. Un import estático al inicio de un <script type="module"> es la forma soportada. Un import() dinámico funciona si se espera de inmediato — no dejes la carga del SDK para después de renderizar la pantalla.

connect lee cl_dashboard_origin y cl_installation_id de la URL, valida el origen exacto, negocia protocolo 2.0 y resuelve tras el handshake. El cliente expone installationId, capabilities, connected, subscribe, unsubscribe, setHeight, openLink, getIdentityAssertion y disconnect.

Los eventos incluyen contexto, conversación, estado/asignación/etiquetas, mensajes, contacto/usuario, idioma/tema/permisos e instalación. La conversación es omnicanal y funciona para correo, WhatsApp, SMS y otros inboxes; la barra lateral omite conversación y contacto. Programa según capabilities, campos opcionales y context_revision creciente; resincroniza datos antiguos.

Identidad segura para n8n y otros backends

No envíes identidad ni credenciales en la query string del iframe. En context.initialized, usa:

  • account.id y account.name para identificar la cuenta;
  • current_user.id, current_user.name, current_user.role y current_user.avatar_url para identificar a la persona que abrió la app;
  • installation.id, surface, sidebar_category y sidebar_icon en barra lateral para identificar instalación, lugar, categoría nativa e icono elegido;
  • en la superficie de conversación, conversation, contact, permissions y eventos de mensajes para el contexto operativo.

El bridge nunca expone tokens de usuario/sesión/CSRF, REST, MCP ni credenciales del proveedor del canal. Cuando se conceden por instalación, current_user:email añade el correo del usuario actual; contact:email y contact:phone añaden datos del contacto en conversación. Sin la capacidad, el campo se omite del contexto y los eventos.

Para que un backend propio o webhook n8n verifique identidad sin confiar en IDs del navegador, pide una aserción breve:

const identity = await client.getIdentityAssertion();
await fetch('https://app.ejemplo.com/api/dashboard-session', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(identity),
});

El backend envía assertion por POST a la introspection_url recibida, opcionalmente con expected_origin: "https://app.ejemplo.com". Una respuesta activa contiene claims de cuenta, usuario, instalación, superficie y capacidades. La aserción expira en dos minutos y cada introspección revalida feature, instalación, usuario activo, membresía, audiencia y origen. El verificador público rechaza aserciones mayores de 16 KiB antes de verificar la firma. No es un token REST/MCP y no debe almacenarse ni usarse como bearer. Datos y escrituras adicionales usan la credencial REST de privilegio mínimo o el perfil MCP restringido del backend.

2. Gestiona instalaciones por REST

Todas las rutas pertenecen a la cuenta. Con la feature desactivada responde 404 resource_not_found; un miembro autenticado sin permiso de gestión recibe 403 forbidden en esas operaciones.

# Lista instalaciones visibles; administradores también reciben audience.
curl -sS "https://soporte.ejemplo.com/api/v1/accounts/1/dashboard_app_installations?surface=conversation" \
  -H "api_access_token: API_TOKEN"

# Crea una instalación. dashboard_app_id queda inmutable.
curl -sS -X POST "https://soporte.ejemplo.com/api/v1/accounts/1/dashboard_app_installations" \
  -H "api_access_token: API_TOKEN" -H "Content-Type: application/json" \
  --data '{"dashboard_app_installation":{"dashboard_app_id":42,"surface":"conversation","compatibility_mode":"v2","enabled":true,"position":0,"capabilities":["account:read","current_user:read","current_user:email","permissions:read","appearance:read","installation:read","conversation:read","contact:read","contact:email","contact:phone","messages:read","identity:assertion"],"audience":{"type":"any","roles":["administrator"],"team_ids":[7],"user_ids":[]}}}'

# Actualiza o reordena con el lock_version de la respuesta más reciente.
curl -sS -X PATCH "https://soporte.ejemplo.com/api/v1/accounts/1/dashboard_app_installations/9" \
  -H "api_access_token: API_TOKEN" -H "Content-Type: application/json" \
  --data '{"dashboard_app_installation":{"sidebar_category":"applications","sidebar_icon":"rocket","position":1,"lock_version":3}}'

# Elimina.
curl -sS -X DELETE "https://soporte.ejemplo.com/api/v1/accounts/1/dashboard_app_installations/9" \
  -H "api_access_token: API_TOKEN"

La lista devuelve { "payload": [...], "meta": { "revision": N } }; crear, ver y actualizar devuelven { "payload": { ... } }; eliminar devuelve 204. La instalación incluye app id/título/URL, surface, compatibility_mode, enabled, visible operativo, position, sidebar_category y sidebar_icon solo en barra lateral, capabilities, audience solo para administradores, transport_security, warnings, lock_version y updated_at.

POST /api/v1/accounts/:account_id/dashboard_app_installations/:id/identity_assertion es la llamada autenticada usada por getIdentityAssertion; aplica feature, instalación V2/dual activa, audiencia y capacidades. Las apps deben preferir el SDK. La validación server-to-server usa el endpoint público POST /dashboard-apps/v2/assertions/introspect descrito arriba.

También puedes crear app e instalaciones iniciales atómicamente con installations en POST /dashboard_apps. Con la feature habilitada, omitir la propiedad crea la instalación por defecto conversation + legacy + all; un array vacío crea solo la app. Con la feature deshabilitada, enviar installations se rechaza y omitirla conserva la creación heredada. Cada app puede tener una instalación por superficie.

3. Usa las herramientas MCP de la cuenta

Habilita Dashboard Apps en el perfil MCP. El módulo permanece en channels_integrations, conserva las herramientas existentes de definición de app y añade:

Herramienta Efecto Perfil de solo lectura
list_dashboard_app_installations Lista instalaciones visibles al usuario actuante Disponible
create_dashboard_app_installation Crea una instalación Oculta
update_dashboard_app_installation Actualiza categoría, icono, posición, audiencia o bridge Oculta
delete_dashboard_app_installation Elimina una instalación Oculta

MCP usa el mismo ámbito, política, audiencia, feature y errores que REST. El detalle REST y el alias PUT no son herramientas separadas: list es la lectura canónica y PATCH la actualización/reordenación canónica. La emisión de identidad no es una herramienta MCP: pertenece a la sesión humana incrustada, mientras MCP ya posee su propio principal autenticado.

Configuración y opciones

  • Superficies: conversation o sidebar.
  • Modos: legacy, v2 o dual.
  • Capacidades: núcleo obligatorio; conversación/contacto/mensajes solo para conversation; datos personales opcionales current_user:email, contact:email, contact:phone; identidad opcional identity:assertion. Omitirlas aplica el valor completo y útil de la superficie; un array vacío explícito se rechaza con invalid_capabilities. Elimina solo concesiones opcionales y conserva todas las capacidades obligatorias para la superficie seleccionada.
  • Audiencia: { "type": "all" } o { "type": "any", "roles": [...], "team_ids": [...], "user_ids": [...] }. Roles válidos: agent y administrator; any exige un selector no vacío y concede acceso si cualquiera coincide.
  • Categoría lateral: support, contacts_crm, applications, commercial, productivity, automation, growth o analytics_config; solo se acepta en sidebar y el valor predeterminado es productivity. El grupo applications queda justo debajo de Contactos y CRM y se omite vacío.
  • Icono lateral: panels_top_left, layout_dashboard, app_window, boxes, briefcase, bot, calendar_days, chart_no_axes_combined, circle_dollar_sign, clipboard_list, database, folder, globe_2, headphones, life_buoy, messages_square, package, rocket, shopping_bag, sparkles, workflow o wrench; solo se acepta en sidebar, con panels_top_left como valor predeterminado.
  • Posición: índice desde cero normalizado por superficie y, en barra lateral, por categoría.
  • Concurrencia: envía lock_version; 409 stale_installation exige recargar y reintentar.
  • Errores estables: invalid_audience, invalid_capabilities, invalid_sidebar_category, invalid_sidebar_icon, installation_already_exists, invalid_dashboard_app_url, dashboard_app_migration_required, unsafe_same_origin_v2 y resource_not_found. El error de migración exige un marco HTTP(S); query string y fragmento son válidos.
  • Límites SDK: 64 KiB por mensaje, 32 suscripciones, 30 comandos por 10 segundos y altura 160–2.000 px.
  • Los plazos del handshake son dos, independientes. El panel abre la ventana cuando el marco carga y reenvía la invitación hasta 10 segundos. El connect() del SDK tiene su propio límite de 10 segundos desde la llamada, ajustable con connect({ timeoutMs }). Subir el del SDK no ayuda: el panel deja de reintentar antes.
  • Ciclo de vida: el SDK emite eventos de ventana conversalabs.dashboard-apps:bridge.connected, :bridge.paused, :bridge.resumed y :context.resynced. Escúchalos para pausar trabajo cuando la pestaña pierde el foco y para resincronizar. El código disconnected significa que todo comando posterior será rechazado; reconecta en lugar de reintentar el comando.

Códigos de error y lo primero que revisar

Código Significa Empieza por
handshake_timeout El panel reintentó 10 s y la aplicación no respondió ¿Llama a connect() al cargar? ¿El bundle cargado expone connect?
invalid_origin Origen configurado o del bridge inválido URL HTTP(S) sin credenciales; en V2 el origen debe diferir del panel
frame_blocked El navegador rechazó la incrustación frame-ancestors y X-Frame-Options en el servidor de la aplicación
insecure_transport_blocked Aplicación HTTP dentro de un panel HTTPS Publica la aplicación por HTTPS
unsupported_version Aplicación y panel no comparten versión del bridge Usa el SDK del mismo despliegue
unsupported_command Comando fuera de la allowlist Usa solo los comandos anunciados
invalid_payload Mensaje fuera del contrato Versión, secuencia, campos y tamaño
invalid_sequence Mensaje fuera de orden No reimplementes el protocolo a mano; usa el SDK
rate_limited Más de 30 comandos en 10 s Baja la frecuencia y agrupa suscripciones
disconnected El bridge se cerró Reconecta; los comandos pendientes no vuelven
user_denied La persona rechazó la confirmación Esperado en openLink; no insistas
capability_denied Capacidad no concedida Actívala en la instalación; los datos de contacto solo existen en conversación
identity_unavailable No se pudo emitir la aserción Feature, modo V2/dual, instalación activa y audiencia

Casos de uso

  • Mostrar contexto de conversación manteniendo cambios del CRM en un backend auditable.
  • Provisionar instalaciones desde un servicio administrativo interno.
  • Permitir que un perfil IA de solo lectura inventaríe apps sin escritura.
  • Usar MCP de escritura en un perfil administrador restringido para automatización controlada.

Consejos, límites y buenas prácticas

  • Fija dashboardOrigin al origen HTTPS exacto. No uses *, no aceptes orígenes postMessage no confiables ni reimplementes el protocolo si existe el SDK.
  • Query string y fragmento estáticos se conservan. En V2/dual el host añade y sobrescribe solo cl_dashboard_origin, cl_installation_id, cl_account_id, cl_surface, cl_protocol y cl_locale; trata todo nombre cl_* como reservado. El host elimina los valores cl_* estáticos antes de escribir esos seis parámetros. Nunca pongas tokens, aserciones, secretos ni datos personales en la URL o bundle.
  • Mantén credenciales REST/MCP en el servidor, rótalas y separa perfiles de inventario y administración.
  • Autoriza audiencia en el servidor. Ocultar una pestaña no es autorización.
  • openLink siempre muestra una confirmación nativa del host. El enlace solo se abre después de que la persona pulse Abrir enlace; nunca se confía en un timestamp del iframe como prueba de gesto.
  • Maneja códigos de DashboardAppSDKError, desconexión, límites y resincronización.
  • insecure_http es una señal de migración, no aprobación para producción.

Solución de problemas

  • handshake_timeout: el panel reenvió la invitación durante 10 segundos y la aplicación no respondió. Empieza por la aplicación, no por la red: ¿llama a connect() apenas carga la página? ¿El bundle que cargó expone connect? Solo después revisa el origen y el modo V2/dual. Un marco en blanco es otro problema — consulta frame_blocked.
  • El modo Dual oculta el fallo de V2: con Dual, la atención sigue por el lane heredado y la superficie aparece lista aunque el bridge V2 esté muerto. El diálogo Probar muestra el resultado de cada bridge por separado; para ver el error crudo, cambia la instalación a V2 temporalmente.
  • invalid_origin: usa una URL HTTP(S) sin credenciales incrustadas y mantén V2 en un origen distinto al del panel.
  • unsupported_version o unsupported_command: usa el SDK del mismo despliegue y capacidades anunciadas.
  • capability_denied: habilita la capacidad solicitada; correo/teléfono del contacto existe solo en la superficie de conversación.
  • identity_unavailable o introspección inactiva: pide una aserción nueva y verifica feature, modo V2/dual, instalación activa, audiencia, membresía y origen exacto.
  • rate_limited o invalid_payload: reduce frecuencia, tamaño y suscripciones.
  • REST/MCP devuelve 404: revisa cuenta, instalación y feature; la audiencia también puede ocultarla.
  • REST/MCP devuelve 403: el usuario autenticado o actuante no es administrador ni posee un rol personalizado con integration_manage.
  • 409 al actualizar: obtiene el estado actual y repite con su lock_version.
  • 422 al crear/actualizar: revisa error, URL, par app/superficie, categoría lateral, audiencia y origen V2.

Ver también