Widget de Chat JavaScript
Incorpore o chat ao vivo do Deskwoot em qualquer site com uma única tag <script>. Sem etapa de build, sem login no dashboard exigido dos visitantes.
Nesta página
Instalação
Cole este snippet no template da sua página, idealmente logo antes da tag de fechamento </body>. Ele carrega o widget de forma assíncrona, então não bloqueia a renderização.
<script>
window.deskwootSettings = {
widgetToken: "YOUR_WIDGET_TOKEN"
};
(function (d, t) {
var g = d.createElement(t);
var s = d.getElementsByTagName(t)[0];
g.src = "https://deskwoot.com/widget/deskwoot-loader.js";
g.async = true;
g.defer = true;
s.parentNode.insertBefore(g, s);
})(document, "script");
</script>Pronto. A bolha do chat aparece na sua página na hora. Sem pacote npm, sem bundler, sem etapa de build: funciona em Next.js, React, Vue, temas Shopify, WordPress, HTML puro e em qualquer lugar onde JavaScript rode.
Encontre o token do widget
- Entre no dashboard do Deskwoot com uma conta de administrador. Seus visitantes nunca precisam de login.
- Vá para Configurações → Caixas de entrada.
- Crie uma nova caixa de entrada do tipo Chat ao vivo ou abra a que você já tem.
- Na aba Widget, o cartão Instalação do widget mostra o snippet com o seu próprio
widgetToken. Copie como está.
widgetToken = uma caixa de entrada. Conversas desse widget sempre caem nessa caixa de entrada, onde seus agentes as pegam. Para rotear para uma equipe ou conjunto de agentes diferente, crie uma segunda caixa de entrada e use seu token em uma página diferente.Configuração
Todas as opções ficam em window.deskwootSettings. Só widgetToken é obrigatório.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
| widgetToken | string | nenhum | Obrigatório. Identificador da caixa de entrada do dashboard. |
| user | object | nenhum | Identidade assinada de um visitante logado. Veja Verificação de identidade. |
| embedded | boolean | false | Preenche um container ou a tela inteira em vez de flutuar em um canto. Veja Modo embedded. |
| container | string | Element | nenhum | Só com embedded: o seletor CSS ou elemento a preencher. Sem ele, o widget preenche toda a viewport. |
| openAnchor | string | Element | { x, y } | nenhum | Elemento de onde o painel surge no desktop: um seletor CSS, um elemento ou um ponto { x, y }. |
| openAlign | string | "auto" | Qual canto do painel fica no âncora: auto, bottom-left, bottom-right, top-left ou top-right. |
window.deskwootSettings = {
widgetToken: "YOUR_WIDGET_TOKEN",
// Optional: a verified, logged in visitor (see Identity verification)
user: {
identifier: "user_4711",
identifierHash: "<HMAC-SHA256 of identifier, from your server>",
email: "jane@example.com",
name: "Jane Doe"
},
// Optional: open the panel out of your own button on desktop
openAnchor: "#support-button",
openAlign: "auto"
};Verificação de identidade
Quando os visitantes estão logados no seu site, diga ao widget quem eles são. As conversas chegam então com nome, e-mail e histórico, em vez de como um visitante anônimo.
O widget só aceita uma identidade que o seu servidor assinou. Você assina o id do usuário com um segredo que pertence à caixa de entrada. Visitantes verificados conversam com o nome e o e-mail reais e pulam os formulários de pré-chat e de e-mail. O segredo fica no seu servidor, então o Deskwoot confirma o visitante pela assinatura sem nenhum acesso aos seus sistemas.
Encontre seu segredo de assinatura nas configurações da caixa de entrada do site, na aba Formulário de pré-chat, no cartão Verificação de identidade.
import crypto from "crypto";
const identifierHash = crypto
.createHmac("sha256", process.env.DESKWOOT_IDENTITY_SECRET)
.update(userId) // your stable user id, as a string
.digest("hex");window.deskwootSettings = {
widgetToken: "YOUR_WIDGET_TOKEN",
user: {
identifier: "user_4711", // your stable user id
identifierHash: "<HMAC-SHA256 of identifier, computed on your server>",
email: "jane@example.com", // optional
name: "Jane Doe", // optional
attributes: { plan: "Pro" }, // optional, shown on the contact
attributesIssuedAt: 1760781600, // optional: unix seconds you signed them at
attributesHash: "<HMAC-SHA256 of the signed attributes, from your server>" // optional
}
};identifier é o id estável do usuário e o único campo assinado. email, name e attributes (um mapa simples de textos, como plano ou id da empresa, exibido no contato) são opcionais. Um visitante verificado sempre cai no mesmo contato, com conversas, notas e atributos anteriores à vista. O bot de IA responde a esse visitante no chat ao vivo apenas com base em atributos assinados (veja abaixo), e só quando o Data Access dele inclui contatos.
Para apps de página única que fazem login depois que a página carrega, chame window.deskwoot.identify(user) assim que tiver o usuário assinado. Isso atualiza o visitante atual sem recarregar. Se o usuário já estiver logado quando a página carrega, defina deskwootSettings.user antes do script do widget: assim o chat continua aberto a cada recarregamento. Com identify() depois do carregamento, cada recarregamento começa com o chat vazio, que volta assim que o usuário é verificado.
Atributos assinados
O identifierHash assina só o id do usuário, então um visitante pode alterar attributes no navegador. Para que o bot de IA responda com base neles, assine-os no seu servidor e envie attributesHash e attributesIssuedAt junto. Atributos sem assinatura, e atributos gravados pela API ou pela sua equipe, continuam aparecendo no contato, mas o bot nunca os lê. O bot pode dizer ao visitante o que um atributo assinado diz, então assine apenas o que o visitante pode ver.
attributesHash é o HMAC-SHA256 em hex minúsculo, com o mesmo segredo da caixa de entrada, de quatro linhas unidas por uma quebra de linha (\n): o texto deskwoot-attributes-v1, o id do usuário, attributesIssuedAt e o JSON canônico dos atributos. JSON canônico: chaves ordenadas por code point Unicode, sem espaços, caracteres fora do ASCII escritos como são (sem escape) e, como valores, apenas textos, números, true, false e null (sem objetos ou listas aninhados). É exatamente o que json.dumps(attributes, sort_keys=True, separators=(",", ":"), ensure_ascii=False) do Python escreve.
Envie números decimais e inteiros muito grandes (acima de 9007199254740991) como texto. O Python escreve 1.0 onde o JavaScript escreve 1, então esses números não dariam o mesmo hash dos dois lados.
attributesIssuedAt é o horário unix em segundos inteiros em que você assinou o conjunto. O Deskwoot usa um conjunto com até 24 horas e até 5 minutos à frente do relógio dele, e nunca um mais antigo que o último usado para esse usuário, então assine de novo a cada carregamento da página. Fora disso, o visitante continua verificado, mas os atributos dessa requisição são ignorados (nada muda no contato) e a resposta da verificação traz attributesIgnored com out_of_range ou stale. Um hash errado é recusado como um identifierHash errado, e o visitante conversa então de forma anônima. O mesmo vale para um conjunto assinado acima dos limites: no máximo 30 chaves, chaves de até 64 caracteres, valores de até 1024 caracteres e nenhuma chave que o Deskwoot reserva para si, como source.
Cada conjunto assinado substitui o que o bot pode ler para esse usuário: uma chave que você deixa de fora continua no contato, mas o bot deixa de lê-la, e uma chave assinada como null é removida do contato.
import crypto from "crypto";
// Keys sorted by Unicode code point, no spaces, non ASCII characters raw.
// Values: strings, integers, true, false or null.
function canonicalJson(attributes) {
const keys = Object.keys(attributes).sort((a, b) => {
const x = Array.from(a), y = Array.from(b);
for (let i = 0; i < Math.min(x.length, y.length); i++) {
const d = x[i].codePointAt(0) - y[i].codePointAt(0);
if (d !== 0) return d;
}
return x.length - y.length;
});
return "{" + keys.map((k) => JSON.stringify(k) + ":" + JSON.stringify(attributes[k])).join(",") + "}";
}
const attributesIssuedAt = Math.floor(Date.now() / 1000);
const message = "deskwoot-attributes-v1\n" + userId + "\n" + attributesIssuedAt + "\n" + canonicalJson(attributes);
const attributesHash = crypto
.createHmac("sha256", process.env.DESKWOOT_IDENTITY_SECRET)
.update(message)
.digest("hex");import hashlib, hmac, json, os, time
attributes_issued_at = int(time.time())
canonical = json.dumps(attributes, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
message = "deskwoot-attributes-v1\n" + user_id + "\n" + str(attributes_issued_at) + "\n" + canonical
attributes_hash = hmac.new(
os.environ["DESKWOOT_IDENTITY_SECRET"].encode(),
message.encode("utf-8"),
hashlib.sha256,
).hexdigest()secret test_identity_secret
identifier user_4711
attributesIssuedAt 1760781600
attributes { "plan": "Pro", "status": "verified", "trades": 12, "vip": true,
"referrer": null, "city": "Zürich", "Zone": "EU" }
canonical JSON {"Zone":"EU","city":"Zürich","plan":"Pro","referrer":null,"status":"verified","trades":12,"vip":true}
signed message (4 lines, joined by \n, no line break at the end):
deskwoot-attributes-v1
user_4711
1760781600
{"Zone":"EU","city":"Zürich","plan":"Pro","referrer":null,"status":"verified","trades":12,"vip":true}
attributesHash bd363335977152a3b73cfb6c042c9afc823f18eab73faff36acddfffd0b1e1f5API JavaScript
Assim que o loader rodar, window.deskwoot (também disponível como window.deskwootWidget) permite controlar o widget pela sua própria interface, por exemplo um link Fale conosco na navegação que abre o chat em vez de uma página de suporte.
| Método | O que faz |
|---|---|
| open(options?) | Abre o painel do chat. Os valores opcionais anchor, align e offset fazem ele surgir do seu próprio elemento no desktop. |
| close() | Fecha o painel. |
| toggle(options?) | Abre o painel, ou fecha se ele já estiver aberto. Aceita as mesmas opções de open(). |
| identify(user) | Identifica um usuário verificado depois que a página carregou. Veja Verificação de identidade. |
| window.__deskwootUnread | Número de mensagens de agentes não lidas por este visitante. Atualiza enquanto o painel está fechado. |
Chamadas feitas enquanto o widget ainda está iniciando ficam em fila e rodam quando ele estiver pronto, então não é preciso esperar nem consultar.
// window.deskwoot (alias window.deskwootWidget) exists as soon as the
// loader script has run. Calls made while the widget is still starting
// are queued, so there is no need to wait or poll. Only code that runs
// before the loader itself needs the guard below.
function openDeskwoot() {
if (window.deskwoot) {
window.deskwoot.open();
}
}
openDeskwoot(); // open the chat panel
// window.deskwoot.close(); // close it again
// window.deskwoot.toggle(); // open or close
// Unread agent messages for this visitor:
console.log(window.__deskwootUnread);Modo embedded
Defina embedded: true quando o widget estiver dentro de um container que ocupa a tela inteira, normalmente uma WebView de app mobile, uma tela de contato em página inteira ou um modal com moldura própria.
No modo embedded, a bolha do launcher, o popup de gatilho e o botão de fechar ficam ocultos, e o painel preenche position:fixed; inset:0 sem sombra, sem cantos arredondados e sem o recuo de 20px. Aponte container para um elemento para preencher só esse elemento. O app Deskwoot para celular usa esse modo na tela de suporte.
<div id="support-pane" style="height: 600px"></div>
<script>
window.deskwootSettings = {
widgetToken: "YOUR_WIDGET_TOKEN",
embedded: true,
container: "#support-pane" // leave out to fill the whole viewport
};
</script>Abre automaticamente e preenche o container.
No modo embedded o painel abre ao carregar e preenche o container. A bolha do launcher e o botão de fechar ficam ocultos, então seu app ou página cuida da navegação. A API JavaScript continua disponível para abrir, fechar ou alternar:
<button onclick="window.deskwoot.open()">Contact Support</button>Posicionamentos recomendados
Três posições que funcionam bem para suporte dentro de um app:
Item na barra lateral ou na navegação inferior (como o Deskwoot faz)
Coloque um item Contatar Suporte na navegação que você já tem, ao lado de Configurações ou Ajuda. Ele parece um link comum, mas abre o chat em vez de uma nova página.
<a href="#" onclick="window.deskwoot.open(); return false;" class="nav-item">
<svg class="icon"><!-- chat bubble icon --></svg>
Contact Support
</a>Item no menu de ajuda ou dropdown
Se você já tem um menu de Ajuda ou Conta, adicione uma linha Conversar com suporte no topo. Ela combina bem com itens como Docs, Atalhos de teclado ou Feedback.
<div role="menuitem" onclick="window.deskwoot.open()">
Chat with support
<span class="kbd">?</span>
</div>Botão flutuante no canto
Um pequeno botão flutuante que substitui a bolha oculta. Útil quando o seu app não tem uma barra de navegação sempre visível.
<button
id="support-fab"
onclick="window.deskwoot.open({ anchor: this })"
style="position:fixed;bottom:20px;right:20px;border:0;border-radius:9999px;padding:12px 18px;cursor:pointer;font-weight:600"
>
Help
</button>
<!--
open() and toggle() take an optional anchor, so the panel grows out of
your own element instead of the default corner:
open({ anchor: this }) // grow from this button
open({ anchor: "#support-fab" }) // or from a CSS selector
open({ anchor: { x: 120, y: 640 } }) // or from a viewport point
anchor CSS selector, DOM element or { x, y } (null = default corner)
align "auto" (default), "bottom-left", "bottom-right",
"top-left" or "top-right": the corner it grows from
offset { x, y } in px, to fine tune the position
window.deskwootSettings.openAnchor and .openAlign make this the
default for every open(); open({ anchor: null }) opens one time in
the default corner. Desktop only; phones always get full screen.
-->Dicas
- Chame
deskwoot.toggle()em vez deopen()se quiser que o mesmo gatilho feche o painel quando ele já estiver aberto. - Mostre um contador de não lidas no seu gatilho com
window.__deskwootUnread, um número que atualiza enquanto o painel está fechado. - Coloque o gatilho no mesmo lugar em todas as páginas que exigem login, para os usuários sempre saberem onde está o suporte.
Na instalação padrão em sites você não precisa de um gatilho próprio: deskwoot-loader.js desenha a bolha e cuida do clique.
Content Security Policy
Se o seu site envia um header de Content Security Policy, libere a origem do Deskwoot. Uma política mínima típica:
script-src 'unsafe-inline' https://deskwoot.com;
connect-src https://deskwoot.com;
frame-src https://deskwoot.com;
style-src 'unsafe-inline';O snippet de instalação é um script inline, então script-src precisa de 'unsafe-inline', um nonce ou um hash. style-src 'unsafe-inline' é necessário porque o widget adiciona uma pequena folha de estilo à sua página, e frame-src porque o chat roda em frames servidos por deskwoot.com. Dúvidas sobre a sua política? Escreva para hello@deskwoot.com.
Perguntas frequentes
As respostas curtas estão aqui. Para qualquer outra coisa, pergunte ao Fynn abaixo.
Meus visitantes precisam de uma conta Deskwoot para conversar?
Não. Só você e sua equipe entram no dashboard para responder. Os visitantes apenas digitam no widget.
O widget funciona em SPAs que trocam de rota no navegador?
Sim. Carregue o script uma vez quando o app inicia (por exemplo em _app.tsx ou root.tsx) e ele continua ativo durante a navegação. Nada precisa rodar de novo quando a rota muda.