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_managecriar, 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_surfaceshabilitada 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_managee umapi_access_tokenpara gerenciamento REST. - Para MCP, perfil MCP cujo usuário atuante tenha essa permissão e que inclua Dashboard Apps.
- CSP
frame-ancestorspermitindo a origem exata da Conversa Labs, semX-Frame-Optionsconflitante.
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.
connect lê cl_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.ideaccount.namepara identificar a conta;current_user.id,current_user.name,current_user.roleecurrent_user.avatar_urlpara identificar a pessoa que abriu o app;installation.id,surfacee, na barra lateral,sidebar_categoryesidebar_iconpara identificar a instalação, o local, sua categoria nativa e o ícone escolhido;- na superfície de conversa,
conversation,contact,permissionse 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:
conversationousidebar. - Modos:
legacy,v2oudual. - Capacidades: núcleo obrigatório; conversa/contato/mensagens somente em
conversation; dados pessoais opcionaiscurrent_user:email,contact:email,contact:phone; identidade opcionalidentity:assertion. Omitir aplica o padrão completo e útil da superfície; um array vazio explícito é rejeitado cominvalid_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:agenteadministrator;anyexige ao menos um seletor não vazio e concede acesso se qualquer seletor corresponder. - Categoria lateral:
support,contacts_crm,applications,commercial,productivity,automation,growthouanalytics_config; é aceita somente emsidebare o padrão éproductivity. O grupoapplicationsaparece 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,workflowouwrench; aceito somente emsidebar, companels_top_leftcomo 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_installationpede 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_v2eresource_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 porconnect({ 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.resumede:context.resynced. Ouça-os para pausar trabalho quando a aba sai de foco e para ressincronizar. O códigodisconnectedsignifica 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
dashboardOriginna origem HTTPS exata. Não use*, não aceite origenspostMessagenã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_protocolecl_locale; trate todo nomecl_*como reservado. O host remove valorescl_*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.
openLinksempre 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 chamaconnect()assim que a página carrega? O bundle que ele carregou expõeconnect? Só depois confira a origem e o modo V2/dual. Um quadro que fica em branco é outro problema — vejaframe_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_versionouunsupported_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 emconversation.identity_unavailableou 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_limitedouinvalid_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.