Widget do site

O widget é o chat do seu bot direto no seu site: um botão redondo no canto da página que abre uma conversa. Para o visitante é mais um jeito de falar com você; para o bot é um canal comum, com as mesmas reações e os mesmos diálogos de operador dos mensageiros. Visão geral da multicanalidade em Canais.

Instalar é colar um trecho de código no template do site. Todo o resto se configura no painel e é aplicado sem mexer no site de novo.

Onde pegar o código

No painel abra Widget → bloco Instalação no site → campo Código para inserir. O botão Copiar coloca o trecho inteiro na área de transferência.

O código é assim, com a sua chave no lugar das reticências:

<script>
  window.mybot = { key: "eu-1a2b3c4d-..." };
</script>
<script async src="https://getmybot.dev/loader.js"></script>

Se, em vez do código, aparecer o aviso de que ainda não foi emitida uma chave de instalação para este bot, o canal do widget não está conectado. Fale com o suporte: a chave é emitida ao conectar o canal.

Abaixo do código há uma indicação de qual host o widget vai chamar. Se o seu site usa CSP (Content Security Policy), libere esse host para carregar scripts e receber requisições; caso contrário o navegador bloqueia o widget em silêncio.

Onde o código entra

O trecho vai no HTML de cada página em que o widget deve aparecer. Na prática: uma única vez no template comum — o rodapé, o campo «código antes de </body>» do seu CMS ou um contêiner do gerenciador de tags.

As regras são curtas:

  • O melhor lugar é logo antes do </body> de fechamento. No <head> também funciona, mas aí o navegador gasta tempo com o widget antes do seu conteúdo.
  • A ordem das duas tags importa: a primeira define a chave, a segunda carrega o widget. Não inverta nem separe pela página.
  • A segunda tag tem async e não bloqueia a renderização. Não remova esse atributo.
  • Um trecho por página. Duas cópias são duas tentativas de iniciar o widget.

Daí em diante o carregador descobre de qual data center o seu bot é atendido e busca lá o código do widget. Nada é exigido de você para isso.

A chave de instalação é pública

A chave da primeira tag é um identificador público do seu bot, não uma senha. Ela fica no código-fonte da página: qualquer visitante pode abrir «Exibir código-fonte» e lê-la. Todo widget de chat funciona assim, e tudo bem.

O que importa:

  • Com a chave não dá para entrar no painel, ler diálogos alheios, exportar sua base de assinantes nem alterar nada do bot. Ela habilita exatamente uma coisa: iniciar uma conversa nova com este bot.
  • Não trate a chave como segredo: esconder ou ofuscar não adianta nada.
  • Se for preciso trocá-la (após romper com um fornecedor, por exemplo), o suporte faz isso. Depois da troca o código antigo no site para de funcionar e precisa ser atualizado.

Lista de domínios permitidos

O widget tem uma lista de domínios permitidos (origin allowlist): os endereços de site a partir dos quais ele pode funcionar. Ela é definida ao conectar o canal e pode ser alterada depois sem reemitir a chave, então o seu código no site continua o mesmo.

As entradas são origens: esquema, host e, se não for o padrão, porta.

https://example.com
https://www.example.com
https://shop.example.com
http://localhost:3000

A comparação é exata, caractere a caractere:

  • Não há curingas. https://*.example.com não funciona: liste os subdomínios um a um.
  • example.com e www.example.com são entradas diferentes. Se o site responde nos dois, adicione os dois.
  • http:// e https:// também são entradas diferentes. Normalmente basta https://, mas um ambiente de teste em http:// precisa ser adicionado explicitamente.
  • Sem caminho: https://example.com/shop é recusado; uma origem é só o endereço do site.

Se o seu domínio real não estiver na lista, o widget não inicia. O navegador do visitante recebe a recusa «origin not allowed» e o chat simplesmente não abre. Isso costuma aparecer depois de uma mudança de domínio, um subdomínio novo ou o lançamento de uma segunda versão de idioma em endereço próprio: cada uma exige a sua entrada.

Se a lista estiver vazia não há restrição e o widget funciona de qualquer lugar. Encare isso como «ainda não configurado», não como abertura proposital: assim que souber seus domínios, cadastre-os.

O que essa restrição protege e o que não protege

Protege: impede que outro site incorpore o seu widget. Sem isso alguém poderia copiar o trecho para a própria página e os navegadores de visitantes reais entregariam a esse site o conteúdo das conversas com o seu bot. É exatamente o que a lista fecha.

Não protege: não impede quem copiou a chave e chama a plataforma diretamente — não pelo navegador, mas por um script, por exemplo. O cabeçalho com o endereço do site é colocado pelo navegador; um programa fora do navegador pode omiti-lo ou mandar outro. Trate a lista como restrição de incorporação, não como fronteira de segurança. Contra abuso protegem os limites de frequência e a moderação de diálogos da plataforma, não essa lista.

Aparência

Em WidgetAparência se ajustam:

  • Posição: o botão no canto inferior esquerdo ou direito.
  • Cor de destaque: cor do botão e dos elementos do chat. Use a cor da sua marca para o widget não parecer estranho ao site.
  • Nome do chat: rótulo do botão e título do painel. Em geral o nome da empresa ou o nome com que o bot se apresenta.

Ao lado há uma pré-visualização: um widget vivo que mostra o resultado antes de salvar.

O widget ainda não tem mensagem de boas-vindas: são apenas esses três ajustes. A primeira mensagem o bot envia em resposta ao visitante, seguindo as suas reações de sempre.

As mudanças chegam ao site sozinhas: o widget lê as configurações ao carregar a página, e o código inserido nunca precisa ser editado.

Os visitantes são anônimos

O visitante não preenche nada para escrever. Na primeira execução o widget recebe da plataforma um identificador anônimo de visitante e o guarda no navegador, para que ao voltar a pessoa veja a conversa anterior e não um chat vazio.

Disso decorre que:

  • Nos diálogos esse visitante aparece como anônimo: sem nome, telefone ou e-mail enquanto ele mesmo não escrever.
  • O identificador vive em um navegador específico. Outro navegador, outro aparelho ou dados do site apagados significam um visitante novo com histórico limpo.
  • Um visitante anônimo pode ser vinculado a um cliente que você já conhece, por exemplo um usuário autenticado da sua área logada. Como fazer isso está na seção seguinte.

Vincular um visitante ao seu próprio usuário

Se o visitante já está autenticado no seu site, você pode dizer à plataforma quem ele é. Depois do vínculo a conversa deixa de ser anônima: ela é unida ao perfil dessa pessoa, e as mensagens anônimas anteriores não se perdem.

Basta uma chamada, com uma função que produz a prova:

mybot.identify(async (visitorId) => {
  const res = await fetch("/mybot-sign", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ userId: currentUser.id, visitorId }),
  });
  return res.json(); // { userId, signature, expiresAt }
});

O widget chama sua função com visitorId — o identificador anônimo de visitante atual para esse navegador, o único valor que só o widget conhece — e espera que ela retorne, ou resolva para, { userId, signature, expiresAt }. A única tarefa da sua função é entregar visitorId ao seu próprio servidor, junto com o identificador do usuário autenticado (currentUser.id acima é o que isso for no seu site), e repassar exatamente o que seu servidor responder. A assinatura de verdade acontece no seu servidor, não nessa função — veja abaixo.

Um callback, não um valor já pronto nem uma forma de ler visitorId diretamente, por dois motivos:

  • Momento em que existe. O widget cria o visitorId de forma assíncrona enquanto inicializa — ele não existe no instante em que o script de carregamento roda, e o próprio mybot.identify só é instalado depois que isso termina (mais detalhes abaixo). Então, no momento em que sua página consegue chamar mybot.identify, o identificador que sua função recebe é garantidamente real. Um getter simples não teria essa garantia: nada impediria uma página de lê-lo uma linha cedo demais e não obter nada, produzindo em silêncio uma assinatura que nunca bate, sem nenhuma pista do motivo.
  • Escopo. O código da sua própria página nunca precisa guardar, armazenar ou passar visitorId manualmente — ele existe só dentro dessa função, para a única chamada que ela precisa fazer.

A partir do momento em que sai do widget, visitorId é uma credencial ao portador (bearer capability): quem conseguir uma assinatura para ele pode ser unido à sessão desse visitante. Envie-o só para o seu próprio servidor, na sua própria requisição autenticada, e para nenhum outro lugar — não registre em log, não repasse a terceiros e não coloque numa chamada de analytics do lado do cliente.

A assinatura é calculada só no seu próprio servidor

Dentro da sua função acima, o seu próprio servidor — nunca o navegador — calcula a assinatura: um HMAC-SHA256, com o segredo do widget como chave, em hexadecimal, os primeiros 32 caracteres, de três valores unidos numa só mensagem. A mesma fórmula aparece no painel ao lado do segredo, sob «Signing algorithm»:

signature = hex(HMAC-SHA256(key: secret, message: userId + "\n" + visitorId + "\n" + expiresAt)).slice(0, 32)

Cada + "\n" + acima é uma quebra de linha de verdade entre as partes, não os dois caracteres barra invertida e n. Se a sua função HMAC recebe a mensagem como uma única string, junte as três partes com uma quebra de linha real: três chamadas .update() separadas sem quebra de linha entre elas geram o hash de uma mensagem diferente, e errada.

As três partes:

  • userId — o identificador do usuário autenticado, o mesmo valor que seu servidor recebe da função acima e devolve na resposta.
  • visitorId — o identificador anônimo de visitante que sua função recebeu como argumento e repassou sem alterar ao seu servidor.
  • expiresAt — um timestamp unix em segundos (não em milissegundos) até quando essa assinatura específica continua válida, gerado pelo seu servidor no momento de assinar. A plataforma recusa uma chamada cujo expiresAt já esteja no passado, e também uma que esteja mais de 24 horas no futuro — assine bem antes de devolver o valor da sua função, não uma vez para reaproveitar em requisições futuras.

Essa expiração é o motivo real da mudança, não um detalhe secundário. A fórmula anterior cobria só o identificador do usuário, então uma assinatura capturada uma vez — registrada em algum log, interceptada no tráfego, tanto faz — continuava válida para sempre e servia para qualquer visitante, não só aquele para quem foi emitida. Quem conseguisse uma poderia reproduzi-la num navegador completamente diferente, e a plataforma acabaria unindo a navegação anônima de um estranho ao perfil de um cliente de verdade. Amarrar a assinatura a um visitorId específico e dar a ela uma vida curta fecha as duas pontas dessa brecha: ela só é válida para a sessão para a qual foi emitida, em nenhuma outra, e para de valer assim que expiresAt passa — então até uma assinatura vazada continua sendo um risco breve, de uma única sessão, não permanente.

Calcule os três valores no seu servidor e devolva-os da sua função já prontos. Isso não é formalidade: para calcular a assinatura no navegador seria preciso enviar o segredo até lá, ou seja, entregá-lo a cada visitante da página. A partir daí qualquer um poderia se passar por qualquer cliente seu e ler a conversa dele. A assinatura (e o visitorId/expiresAt para os quais foi calculada) é segura para devolver ao navegador; o segredo, não.

A chamada em si não mostra nada na tela: o vínculo acontece no servidor e em silêncio. Se a sua função lançar um erro ou sua promise for rejeitada, ou se a plataforma recusar a assinatura resultante — não bate, ou expiresAt está ausente, já passou ou está a mais de 24 horas no futuro —, nada mais acontece: o visitante não percebe nada e segue escrevendo como anônimo.

mybot.identify não existe de imediato: ele aparece depois que o pacote principal do widget carrega, junto com um visitorId real (veja «Momento em que existe» acima). Chame-o de um handler de carregamento da página, não como primeira linha do <head>.

Onde fica o segredo e como trocá-lo

O segredo do widget é a chave com que você assina. Ele fica nas configurações do widget, na seção «identify() secret». Se essa seção disser que o widget ainda não está conectado, conecte primeiro o canal web: o segredo aparece junto com ele.

Exibir. O botão «Reveal secret» busca o valor. Ele não aparece em texto claro: primeiro você vê pontos, e um alternador à parte, «Show», mostra os caracteres («Mask» volta a escondê-los). Ao lado há «Clear from screen», que tira o valor da tela.

Você pode exibi-lo quantas vezes quiser, e exibir não invalida nada: o segredo continua válido depois de fechar a tela. É assim porque ele é guardado criptografado e não em hash — a plataforma precisa do valor real para verificar cada assinatura. Se daqui a meio ano você subir um backend novo, é só voltar e consultá-lo de novo.

Depois de copiar, recolha você mesmo. A plataforma não consegue limpar sua área de transferência: do navegador isso não é confiável, então não prometemos. Tire o valor da tela e limpe a área de transferência à mão quando terminar, sobretudo num computador compartilhado.

Trocar. O botão «Rotate secret», com etapa de confirmação. É uma ação que quebra na hora: assim que o segredo novo existe, o antigo deixa de verificar. Não há janela de sobreposição, nem um instante em que as duas chaves valham.

O que quebra é exatamente o vínculo de visitantes com os seus usuários. As conversas não são interrompidas: os visitantes seguem escrevendo e recebendo resposta, só que como anônimos, até o seu backend assinar com o segredo novo. O código de instalação e a lista de domínios permitidos não são afetados; no seu site não há nada a mudar.

Portanto troque o segredo com consciência: prepare primeiro o deploy do backend com o valor novo e só então clique em «Rotate secret». Clicar para ver o que acontece é má ideia. O valor novo aparece na tela logo após a troca, então dá para copiá-lo na hora.

Como o widget se comporta na página

O widget foi feito de propósito para não atrapalhar o seu site:

  • Ele vive em um contêiner isolado (Shadow DOM fechado). Seus estilos não entram nele e os dele não vazam para a página. Efeito colateral: você não consegue reestilizá-lo com o seu CSS — use os ajustes de aparência.
  • O contêiner cobre a tela, mas não intercepta cliques: eles atravessam para a página, e só o botão e o painel do chat respondem.
  • O widget fica sempre acima do seu conteúdo; suas camadas não conseguem cobri-lo.
  • Qualquer erro dentro do widget fica dentro: na pior hipótese o botão do chat não funciona, e o site continua rodando. Se o widget não aparecer, olhe o console do navegador — quase sempre é a CSP ou um domínio faltando na lista.

Se o widget não aparecer

Percorra na ordem:

  1. Abra o código-fonte e confirme que as duas tags estão lá e que a chave não está vazia.
  2. Confirme que o endereço do site é exatamente o da lista, incluindo https:// e www.
  3. Veja o console do navegador: uma mensagem de Content Security Policy significa liberar o host da plataforma na CSP do site.
  4. Verifique se um bloqueador de anúncios não está removendo o widget: teste em janela anônima sem extensões.

O que vem depois