SDK, API REST e MCP de Dashboard Apps

Conversa Labs

Conversa Labs

Última atualização em Aug 26, 2026

Visão geral

Dashboard Apps nativos têm três contratos de integração separados:

  • O SDK V2 do navegador entrega contexto limitado por capacidades, eventos somente leitura, comandos seguros de quadro e identidade assinada opcional para um backend próprio.
  • A API REST da conta permite a administradores ou funções personalizadas com integration_manage criar, consultar, atualizar, reordenar e excluir apps e instalações.
  • O servidor MCP da conta expõe o mesmo contrato de instalações em quatro ferramentas opcionais de Canais e integrações → Dashboard Apps.

O SDK autentica somente a identidade curta do app incorporado; não oferece um proxy genérico de API. Escritas de negócio devem passar pelo seu backend, autenticado na API REST com um token de privilégio mínimo.

Pré-requisitos

  • dashboard_apps_native_surfaces habilitada na conta.
  • Dashboard App HTTPS em origem diferente do dashboard da Conversa Labs para o modo V2.
  • Uma pessoa administradora ou função personalizada com integration_manage e um api_access_token para gerenciamento REST.
  • Para MCP, perfil MCP cujo usuário atuante tenha essa permissão e que inclua Dashboard Apps.
  • CSP frame-ancestors permitindo a origem exata da Conversa Labs, sem X-Frame-Options conflitante.

Passo a passo

1. Conecte o SDK V2 no navegador

Importe o SDK pelo caminho estável e versionado da sua implantação da Conversa Labs. Ele redireciona para o build atual com fingerprint e expõe exportações nomeadas do módulo JavaScript.

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

try {
  // A origem e o installationId são descobertos dos parâmetros cl_* injetados pelo host.
  // Também podem ser fixados explicitamente para testes controlados.
  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('#documentacao').addEventListener('click', () => {
    client.openLink('https://app.exemplo.com/docs');
  });

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

Use as importações nomeadas do módulo mostradas acima. Elas são o contrato estável para novos aplicativos. Se o seu app não consegue consumir exportações nomeadas, o mesmo arquivo também expõe window.ConversaLabsDashboardAppSDK, com as mesmas funções.

Chame connect() assim que a página carregar, não atrás de autenticação ou de uma ida ao seu backend. O host inicia o handshake quando o quadro termina de carregar e insiste por até 10 segundos; um aplicativo que só começa a escutar depois desse prazo recebe handshake_timeout. Um import estático no topo de um <script type="module"> é a forma suportada. import() dinâmico funciona, desde que aguardado imediatamente — não deixe para carregar o SDK depois de renderizar a tela.

connectcl_dashboard_origin e cl_installation_id da URL de lançamento, valida a origem exata, negocia o protocolo 2.0 e resolve depois do handshake. O cliente expõe installationId, capabilities, connected, subscribe, unsubscribe, setHeight, openLink, getIdentityAssertion e disconnect.

Os eventos incluem inicialização de contexto, conversa, status/atribuição/labels, criação/atualização de mensagem, contato/usuário atual, idioma/tema/permissões e instalação. A superfície de conversa é omnichannel e funciona para e-mail, WhatsApp, SMS e outros inboxes; a barra lateral omite conversa e contato. Programe para as capabilities, campos opcionais e context_revision crescente; ressincronize em vez de aplicar dados antigos.

Identificação segura para n8n e outros backends

Não envie identidade nem credenciais pela query string do iframe. No evento context.initialized, use:

  • account.id e account.name para identificar a conta;
  • current_user.id, current_user.name, current_user.role e current_user.avatar_url para identificar a pessoa que abriu o app;
  • installation.id, surface e, na barra lateral, sidebar_category e sidebar_icon para identificar a instalação, o local, sua categoria nativa e o ícone escolhido;
  • na superfície de conversa, conversation, contact, permissions e eventos de mensagem para o contexto operacional.

O bridge nunca expõe tokens de usuário, sessão, CSRF, REST, MCP ou credenciais do provedor do canal. Quando concedidos por instalação, current_user:email adiciona o e-mail do usuário atual; contact:email e contact:phone adicionam os dados do contato na conversa. Sem a capacidade, o campo é omitido em contexto e eventos.

Para um backend próprio ou webhook n8n confirmar a identidade sem confiar em IDs enviados pelo navegador, peça uma asserção curta:

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

O backend do app envia assertion por POST para a introspection_url recebida, opcionalmente com expected_origin: "https://app.exemplo.com". Uma resposta ativa contém claims de conta, usuário, instalação, superfície e capacidades. A asserção expira em dois minutos e é revalidada contra feature, instalação, usuário ativo, associação, público e origem a cada introspecção. O verificador público rejeita asserções maiores que 16 KiB antes de verificar a assinatura. Ela não é token REST/MCP e não deve ser armazenada nem usada como bearer. Para dados/escritas adicionais, o backend usa sua própria credencial REST de privilégio mínimo ou perfil MCP restrito.

2. Gerencie instalações por REST

Todos os caminhos pertencem à conta. Com a feature desligada, a resposta é 404 resource_not_found; um membro autenticado sem permissão de gerenciamento recebe 403 forbidden nessas operações.

# Lista instalações visíveis; administradores também recebem audience.
curl -sS "https://suporte.exemplo.com/api/v1/accounts/1/dashboard_app_installations?surface=conversation" \
  -H "api_access_token: API_TOKEN"

# Cria uma instalação. dashboard_app_id não pode ser alterado depois.
curl -sS -X POST "https://suporte.exemplo.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":[]}}}'

# Atualiza ou reordena com a lock_version da resposta mais recente.
curl -sS -X PATCH "https://suporte.exemplo.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}}'

# Exclui.
curl -sS -X DELETE "https://suporte.exemplo.com/api/v1/accounts/1/dashboard_app_installations/9" \
  -H "api_access_token: API_TOKEN"

A lista retorna { "payload": [...], "meta": { "revision": N } }; criação, detalhe e atualização retornam { "payload": { ... } }; exclusão retorna 204. A instalação contém app id/título/URL, surface, compatibility_mode, enabled, visible operacional, position, sidebar_category e sidebar_icon na barra lateral, capabilities, audience apenas para administradores, transport_security, warnings, lock_version e updated_at.

POST /api/v1/accounts/:account_id/dashboard_app_installations/:id/identity_assertion é a chamada autenticada usada internamente por getIdentityAssertion; respeita feature, instalação ativa, V2/dual, público e capacidades. Aplicativos devem preferir o SDK. A validação server-to-server usa o endpoint público POST /dashboard-apps/v2/assertions/introspect descrito acima.

Também é possível criar app e instalações iniciais de forma atômica enviando installations no POST /dashboard_apps. Com a feature ligada, omitir a propriedade cria a compatibilidade padrão conversation + legacy + all; um array vazio cria somente o app. Com a feature desligada, enviar installations é rejeitado e omitir a propriedade preserva a criação legada. Cada app pode ter uma instalação por superfície.

3. Use as ferramentas MCP da conta

Habilite Dashboard Apps no perfil MCP. O módulo permanece no grupo channels_integrations, preserva as ferramentas existentes de definição de app e adiciona:

Ferramenta Efeito Perfil somente leitura
list_dashboard_app_installations Lista instalações visíveis ao usuário atuante Disponível
create_dashboard_app_installation Cria uma instalação Oculta
update_dashboard_app_installation Atualiza categoria, ícone, posição, público ou bridge Oculta
delete_dashboard_app_installation Exclui uma instalação Oculta

MCP usa o mesmo escopo da conta, política, público, feature e erros de validação da API REST. O detalhe REST e o alias PUT não viram ferramentas separadas: a lista é a leitura canônica e PATCH é a atualização e reordenação canônica. A mintagem de identidade não vira ferramenta MCP: ela pertence à sessão humana incorporada, enquanto o cliente MCP já possui seu próprio principal autenticado.

Configurações e opções

  • Superfícies: conversation ou sidebar.
  • Modos: legacy, v2 ou dual.
  • Capacidades: núcleo obrigatório; conversa/contato/mensagens somente em conversation; dados pessoais opcionais current_user:email, contact:email, contact:phone; identidade opcional identity:assertion. Omitir aplica o padrão completo e útil da superfície; um array vazio explícito é rejeitado com invalid_capabilities. Remova apenas concessões opcionais e preserve todas as capacidades obrigatórias da superfície selecionada.
  • Público: { "type": "all" } ou { "type": "any", "roles": [...], "team_ids": [...], "user_ids": [...] }. Funções válidas: agent e administrator; any exige ao menos um seletor não vazio e concede acesso se qualquer seletor corresponder.
  • Categoria lateral: support, contacts_crm, applications, commercial, productivity, automation, growth ou analytics_config; é aceita somente em sidebar e o padrão é productivity. O grupo applications aparece logo abaixo de Contatos & CRM e é omitido quando vazio.
  • Ícone 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 ou wrench; aceito somente em sidebar, com panels_top_left como padrão.
  • Posição: índice a partir de zero, normalizado por superfície e, na barra lateral, por categoria.
  • Concorrência: envie lock_version; 409 stale_installation pede recarga e nova tentativa.
  • Erros estáveis: 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 e resource_not_found. O erro de migração exige um único quadro HTTP(S); query string e fragmento são aceitos.
  • Limites do SDK: mensagens de 64 KiB, 32 assinaturas, 30 comandos por 10 segundos e altura entre 160–2.000 px.
  • Prazos do handshake: são dois, independentes. O painel abre a janela quando o quadro carrega e reenvia o convite por até 10 segundos. O connect() do SDK tem o próprio limite de 10 segundos, contado a partir da chamada, ajustável por connect({ timeoutMs }). Aumentar o do SDK não ajuda: o painel para de insistir antes.
  • Ciclo de vida: o SDK dispara eventos de janela conversalabs.dashboard-apps:bridge.connected, :bridge.paused, :bridge.resumed e :context.resynced. Ouça-os para pausar trabalho quando a aba sai de foco e para ressincronizar. O código disconnected significa que todo comando seguinte será rejeitado; refaça a conexão em vez de repetir o comando.

Códigos de erro e a primeira coisa a conferir

Código Significa Comece por
handshake_timeout O painel insistiu por 10 s e o app não respondeu O app chama connect() no carregamento? O bundle carregado expõe connect?
invalid_origin Origem configurada ou de destino inválida URL HTTP(S) sem credenciais; em V2 a origem precisa ser diferente da do painel
frame_blocked O navegador recusou a incorporação frame-ancestors e X-Frame-Options no servidor do app
insecure_transport_blocked App HTTP dentro de painel HTTPS Publique o app em HTTPS
unsupported_version App e painel não compartilham a versão da bridge Use o SDK da mesma implantação
unsupported_command Comando fora da allowlist Use apenas os comandos anunciados
invalid_payload Mensagem fora do contrato Versão, sequência, campos e tamanho
invalid_sequence Mensagem fora de ordem Não reimplemente o protocolo à mão; use o SDK
rate_limited Mais de 30 comandos em 10 s Reduza frequência e agrupe assinaturas
disconnected A ponte fechou Reconecte; comandos pendentes não voltam
user_denied A pessoa recusou a confirmação Esperado em openLink; não insista
capability_denied Capacidade não concedida Habilite na instalação; dados de contato só existem em conversa
identity_unavailable Não foi possível emitir a asserção Feature, modo V2/dual, instalação ativa e público

Casos de uso

  • Renderizar contexto da conversa e manter alterações de CRM em backend auditável.
  • Provisionar instalações por um serviço administrativo interno.
  • Permitir que um perfil de IA somente leitura inventarie apps sem direitos de escrita.
  • Usar MCP de escrita em perfil administrador restrito para automação controlada.

Dicas, limites e boas práticas

  • Fixe dashboardOrigin na origem HTTPS exata. Não use *, não aceite origens postMessage não confiáveis e não reimplemente o protocolo quando houver SDK.
  • Query string e fragmento estáticos são preservados. Em V2/dual, o host acrescenta e sobrescreve apenas cl_dashboard_origin, cl_installation_id, cl_account_id, cl_surface, cl_protocol e cl_locale; trate todo nome cl_* como reservado. O host remove valores cl_* estáticos antes de escrever esses seis parâmetros. Nunca coloque tokens, asserções, segredos ou dados pessoais na URL ou no bundle do app.
  • Mantenha credenciais REST/MCP no servidor, rotacione-as e separe perfis de inventário dos administrativos.
  • Autorize o público no servidor. Esconder uma aba no iframe não é autorização.
  • openLink sempre mostra uma confirmação nativa do host. O link só abre depois que a pessoa clica em Abrir link; um timestamp enviado pelo iframe nunca é aceito como prova de gesto.
  • Trate códigos de DashboardAppSDKError, desconexões, limites e ressincronização explicitamente.
  • insecure_http é sinal de migração, não aprovação para produção.

Solução de problemas

  • handshake_timeout: o painel reenviou o convite por 10 segundos e o aplicativo não respondeu. Comece pelo app, não pela rede: ele chama connect() assim que a página carrega? O bundle que ele carregou expõe connect? Só depois confira a origem e o modo V2/dual. Um quadro que fica em branco é outro problema — veja frame_blocked.
  • O modo Dual esconde a falha da V2: com Dual, o atendimento segue pelo lane legado e a superfície aparece pronta mesmo com a bridge V2 morta. O diálogo Testar mostra o resultado de cada bridge separadamente; para ver o erro cru, mude a instalação para V2 temporariamente.
  • invalid_origin: use URL HTTP(S) sem credenciais embutidas e mantenha a V2 numa origem diferente da do painel.
  • unsupported_version ou unsupported_command: use o SDK da mesma implantação e apenas capacidades anunciadas.
  • capability_denied: habilite a capacidade pedida na instalação; contato/e-mail do contato existem somente em conversation.
  • identity_unavailable ou introspecção inativa: obtenha uma nova asserção e confira feature, modo V2/dual, instalação ativa, público, associação e origem exata.
  • rate_limited ou invalid_payload: reduza frequência, tamanho e assinaturas.
  • REST/MCP retorna 404: confira conta, instalação e feature. O público também pode ocultar o recurso.
  • REST/MCP retorna 403: usuário autenticado ou atuante não é administrador nem possui uma função personalizada com integration_manage.
  • 409 ao atualizar: busque o estado atual e repita com sua lock_version.
  • 422 ao criar/atualizar: confira o error, URL, par app/superfície único, categoria lateral, público e origem do V2.

Veja também