Widget de chat JavaScript

Intégrez le chat en direct de Deskwoot sur n'importe quel site web avec une seule balise <script>. Pas d'étape de build, et aucune connexion au tableau de bord n'est demandée aux visiteurs.

Sur cette page

Installation

Collez cet extrait de code dans le modèle de votre page, idéalement juste avant la balise fermante </body>. Il charge le widget de façon asynchrone, il ne bloque donc pas l'affichage de la page.

<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>

C'est tout. Le bouton du chat apparaît tout de suite sur votre page. Pas de paquet npm, pas de bundler, pas d'étape de build : il fonctionne avec Next.js, React, Vue, les thèmes Shopify, WordPress, le HTML simple et partout où JavaScript s'exécute.

Trouver le jeton de votre widget

  1. Connectez-vous au tableau de bord Deskwoot avec un compte administrateur. Vos visiteurs n'ont jamais besoin de se connecter.
  2. Allez dans Paramètres → Boîtes de réception.
  3. Créez une nouvelle boîte de réception de type Chat en direct, ou ouvrez celle que vous avez déjà.
  4. Dans l'onglet Widget, la carte Installation du widget affiche l'extrait de code avec votre propre widgetToken. Copiez-le tel quel.
Un widgetToken = une boîte de réception. Les conversations de ce widget arrivent toujours dans cette boîte de réception, où vos agents les prennent en charge. Pour les diriger vers une autre équipe ou un autre groupe d'agents, créez une deuxième boîte de réception et utilisez son jeton sur une autre page.

Configuration

Toutes les options se trouvent dans window.deskwootSettings. Seul widgetToken est obligatoire.

OptionTypePar défautDescription
widgetTokenstringaucunObligatoire. Identifiant de la boîte de réception, disponible dans le tableau de bord.
userobjectaucunIdentité signée d'un visiteur connecté. Voir Vérification d'identité.
embeddedbooleanfalseRemplit un conteneur ou tout l'écran au lieu de flotter dans un coin. Voir Mode intégré.
containerstring | ElementaucunUniquement avec embedded : le sélecteur CSS ou l'élément à remplir. Sans cette option, le widget occupe toute la fenêtre d'affichage.
openAnchorstring | Element | { x, y }aucunÉlément d'où s'ouvre le panneau sur ordinateur : un sélecteur CSS, un élément ou un point { x, y }.
openAlignstring"auto"Le coin du panneau placé sur l'ancre : auto, bottom-left, bottom-right, top-left ou top-right.
La couleur, la position du bouton du chat, le thème clair ou sombre et les textes d'accueil ne se définissent pas dans le code. Vous les modifiez pour chaque boîte de réception dans Paramètres → Boîtes de réception → votre boîte de réception → Widget → Apparence du widget, et le widget les charge à chaque démarrage.
Exemple complet
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"
};

Vérification d'identité

Quand les visiteurs sont connectés sur votre site, indiquez au widget qui ils sont. Leurs conversations arrivent alors avec leur nom, leur e-mail et leur historique, et non comme celles d'un visiteur anonyme.

Le widget n'accepte qu'une identité signée par votre serveur. Vous signez l'id de l'utilisateur avec un secret propre à la boîte de réception. Les visiteurs vérifiés discutent sous leur vrai nom et leur vrai e-mail, et passent le formulaire avant le chat ainsi que l'étape de l'e-mail. Le secret reste sur votre serveur : Deskwoot confirme le visiteur grâce à la signature, sans aucun accès à vos systèmes.

Vous trouverez votre secret de signature dans les paramètres de la boîte de réception de votre site web, dans l'onglet Formulaire avant le chat, sur la carte Vérification d'identité.

Calculez le hash sur votre serveur (Node.js)
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");
Visiteur vérifié
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 est l'identifiant stable de votre utilisateur et le seul champ signé. email, name et attributes (une liste simple de textes, comme un forfait ou un identifiant d'entreprise, affichée sur le contact) sont des informations facultatives. Un visiteur vérifié arrive toujours sur le même contact, avec ses conversations passées, ses notes et ses attributs sous les yeux. Le bot IA ne répond à ce visiteur dans le chat en direct qu'à partir des attributs signés (voir ci-dessous), et seulement s'il a le droit de consulter les contacts dans « Ce que Fynn peut faire ».

Si votre application connecte l'utilisateur après le chargement de la page, appelez window.deskwoot.identify(user) dès que vous avez l'utilisateur signé. Cela met à jour le visiteur actuel sans rechargement. Si l'utilisateur est déjà connecté au chargement de la page, définissez plutôt deskwootSettings.user avant le script du widget : le chat reste alors ouvert d'un rechargement à l'autre. Avec identify() après le chargement, chaque rechargement commence par un chat vide, qui revient dès que l'utilisateur est vérifié.

Le hash doit venir de votre serveur. Ne le calculez jamais dans le navigateur et n'envoyez jamais le secret au client. Toute personne qui a le secret peut se faire passer pour vos utilisateurs.

Attributs signés

identifierHash ne signe que l'id de l'utilisateur, un visiteur peut donc modifier attributes dans le navigateur. Pour que le bot IA réponde à partir de ces attributs, signez-les sur votre serveur et envoyez attributesHash et attributesIssuedAt avec eux. Les attributs non signés, et ceux écrits via l'API ou par votre équipe, restent visibles sur le contact, mais le bot ne les lit jamais. Le bot peut dire au visiteur ce que contient un attribut signé, alors ne signez que ce que le visiteur a le droit de voir.

attributesHash est le HMAC-SHA256 en hexadécimal minuscule, avec le même secret de boîte de réception, de quatre lignes reliées par un saut de ligne (\n) : le texte deskwoot-attributes-v1, l'id de l'utilisateur, attributesIssuedAt et le JSON canonique des attributs. JSON canonique : clés triées par point de code Unicode, sans espaces, caractères hors ASCII écrits tels quels (non échappés), et uniquement des chaînes, des nombres, true, false et null comme valeurs (pas d'objets ni de listes imbriqués). C'est exactement ce qu'écrit json.dumps(attributes, sort_keys=True, separators=(",", ":"), ensure_ascii=False) en Python.

Envoyez les nombres décimaux et les très grands entiers (au-delà de 9007199254740991) sous forme de chaînes. Python écrit 1.0 là où JavaScript écrit 1, ces nombres ne donneraient donc pas le même hash des deux côtés.

attributesIssuedAt est l'heure unix, en secondes entières, à laquelle vous avez signé l'ensemble. Deskwoot accepte un ensemble vieux de 24 heures au plus et en avance de 5 minutes au plus sur son horloge, et jamais un ensemble plus ancien que le dernier utilisé pour cet utilisateur : signez donc à nouveau à chaque chargement de page. En dehors de ces limites, le visiteur reste vérifié, mais les attributs de cette requête sont ignorés (rien ne change sur le contact) et la réponse de vérification indique attributesIgnored avec out_of_range ou stale. Un hash incorrect est refusé comme un identifierHash incorrect, et le visiteur discute alors de façon anonyme. Il en va de même pour un ensemble signé qui dépasse les limites : 30 clés au maximum, des clés de 64 caractères au plus, des valeurs de 1024 caractères au plus, et aucune clé que Deskwoot se réserve, comme source.

Chaque ensemble signé remplace ce que le bot peut lire pour cet utilisateur : une clé que vous omettez reste sur le contact, mais le bot ne la lit plus, et une clé que vous signez avec la valeur null est supprimée du contact.

Signez les attributs sur votre serveur (Node.js)
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");
La même chose en Python
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()
Vecteur de test : votre code doit produire exactement ce JSON canonique et ce hash
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      bd363335977152a3b73cfb6c042c9afc823f18eab73faff36acddfffd0b1e1f5

API JavaScript

Dès que le loader s'est exécuté, window.deskwoot (aussi disponible sous le nom window.deskwootWidget) vous permet de piloter le widget depuis votre propre interface, par exemple avec un lien Nous contacter dans votre navigation qui ouvre le chat au lieu d'une page de support.

MéthodeCe qu'elle fait
open(options?)Ouvre le panneau du chat. Les options facultatives anchor, align et offset le font s'ouvrir depuis votre propre élément sur ordinateur.
close()Ferme le panneau.
toggle(options?)Ouvre le panneau, ou le ferme s'il est déjà ouvert. Accepte les mêmes options que open().
identify(user)Connecte un utilisateur vérifié après le chargement de la page. Voir Vérification d'identité.
window.__deskwootUnreadNombre de messages d'agents non lus pour ce visiteur. Il se met à jour pendant que le panneau est fermé.

Les appels faits pendant que le widget démarre encore attendent dans une file et s'exécutent dès qu'il est prêt. Inutile donc d'attendre ou de vérifier en boucle.

Ouvrir le chat depuis votre propre code
// 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);

Mode intégré

Définissez embedded: true quand le widget se trouve dans un conteneur qui doit occuper tout l'écran, en général une WebView d'application mobile, un écran Contact en pleine page ou une fenêtre modale qui a son propre cadre.

En mode intégré, le bouton du chat, la fenêtre pop-up du déclencheur et le bouton de fermeture sont masqués, et le panneau remplit position:fixed; inset:0 sans ombre, sans coins arrondis et sans le décalage de 20px. Faites pointer container vers un élément pour ne remplir que cet élément. L'application mobile Deskwoot utilise ce mode sur son écran Contacter le support.

<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>

S'ouvre automatiquement et remplit son conteneur.

En mode intégré, le panneau s'ouvre au chargement et remplit son conteneur. Le bouton du chat et le bouton de fermeture sont masqués, c'est donc votre application ou votre page qui gère la navigation. L'API JavaScript reste disponible pour l'ouvrir, le fermer ou basculer son affichage :

Déclencheur minimal
<button onclick="window.deskwoot.open()">Contact Support</button>

Emplacements recommandés

Trois emplacements qui fonctionnent bien pour le support dans une application :

Élément dans la barre latérale ou la navigation du bas (comme le fait Deskwoot)

Placez un élément Contacter le support dans votre navigation existante, à côté de Paramètres ou Aide. Il ressemble à n'importe quel autre lien, mais ouvre le chat au lieu d'une nouvelle page.

<a href="#" onclick="window.deskwoot.open(); return false;" class="nav-item">
  <svg class="icon"><!-- chat bubble icon --></svg>
  Contact Support
</a>

Élément dans le menu d'aide ou un menu déroulant

Si vous avez déjà un menu Aide ou Compte, ajoutez une ligne Discuter avec le support en haut. Elle s'intègre bien à côté d'éléments comme Documentation, Raccourcis clavier ou Commentaires.

<div role="menuitem" onclick="window.deskwoot.open()">
  Chat with support
  <span class="kbd">?</span>
</div>

Bouton flottant dans le coin

Un petit bouton flottant qui remplace le bouton du chat masqué. Pratique quand votre application n'a pas de barre de navigation toujours visible.

<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.
-->

Conseils

  • Appelez deskwoot.toggle() au lieu de open() si vous voulez que le même déclencheur ferme le panneau quand il est déjà ouvert.
  • Affichez un badge de messages non lus sur votre déclencheur avec window.__deskwootUnread, un nombre qui se met à jour pendant que le panneau est fermé.
  • Placez le déclencheur au même endroit sur toutes les pages qui demandent une connexion, pour que les utilisateurs sachent toujours où trouver le support.

Pour l'installation standard sur un site web, vous n'avez pas besoin de votre propre déclencheur : deskwoot-loader.js affiche le bouton du chat et gère le clic.

Content Security Policy

Si votre site envoie un en-tête Content Security Policy, autorisez l'origine de Deskwoot. Une politique minimale typique :

script-src  'unsafe-inline' https://deskwoot.com;
connect-src https://deskwoot.com;
frame-src   https://deskwoot.com;
style-src   'unsafe-inline';

Le code d'installation est un script en ligne, donc script-src a besoin de 'unsafe-inline', d'un nonce ou d'un hash. style-src 'unsafe-inline' est nécessaire parce que le widget ajoute une petite feuille de style à votre page, et frame-src parce que le chat s'exécute dans des frames servis par deskwoot.com. Des questions sur votre politique ? Écrivez à hello@deskwoot.com.

Questions fréquentes

Voici les réponses courtes. Pour tout le reste, demandez à Fynn ci-dessous.

Mes visiteurs ont-ils besoin d'un compte Deskwoot pour discuter ?

Non. Seuls vous et votre équipe vous connectez au tableau de bord pour répondre. Les visiteurs écrivent simplement dans le widget.

Le widget fonctionne-t-il dans les SPA qui changent de route sans rechargement ?

Oui. Chargez le script une seule fois au démarrage de votre application (par exemple dans _app.tsx ou root.tsx), et il reste actif pendant la navigation. Rien ne doit être relancé quand la route change.

Démo interactive de Deskwoot