Widget de site

Le widget, c'est le chat de votre bot directement sur votre site : un bouton rond dans un coin de la page, qui ouvre une conversation. Pour le visiteur, c'est un moyen de plus de vous joindre ; pour le bot, c'est un canal ordinaire, avec les mêmes réactions et les mêmes dialogues opérateur que dans les messageries. Vue d'ensemble de la multicanalité : Canaux.

L'installation tient en un extrait de code collé dans le gabarit du site. Tout le reste se configure dans l'espace client et s'applique sans retoucher le site.

Où trouver le code d'intégration

Dans l'espace client, ouvrez Widget → bloc Installation sur le site → champ Code à insérer. Le bouton Copier place l'extrait complet dans le presse-papiers.

Le code ressemble à ceci, avec votre propre clé à la place des points de suspension :

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

Si, à la place du code, vous voyez qu'aucune clé d'installation n'a encore été émise pour ce bot, c'est que le canal widget n'est pas connecté. Écrivez au support : la clé est délivrée à la connexion du canal.

Sous le code figure une indication de l'hôte que le widget contactera. Si votre site applique une CSP (Content Security Policy), autorisez cet hôte à charger des scripts et à recevoir des requêtes, sinon le navigateur bloquera le widget silencieusement.

Où placer le code

L'extrait va dans le HTML de chaque page où le widget doit apparaître. En pratique : une seule fois dans le gabarit commun — le pied de page, le champ « code avant </body> » de votre CMS, ou un conteneur de votre gestionnaire de balises.

Les règles sont courtes :

  • Le meilleur emplacement est juste avant le </body> fermant. Le <head> fonctionne aussi, mais le navigateur consacre alors du temps au widget avant votre contenu.
  • L'ordre des deux balises compte : la première fixe la clé, la seconde charge le widget. Ne les intervertissez pas et ne les séparez pas.
  • La seconde balise porte async et ne bloque pas l'affichage. Conservez cet attribut.
  • Un extrait par page. Deux copies, ce sont deux tentatives de démarrage.

Ensuite, le chargeur détermine depuis quel centre de données votre bot est servi et récupère le code du widget là-bas. Rien n'est requis de votre part pour cela.

La clé d'installation est publique

La clé de la première balise est un identifiant public de votre bot, pas un mot de passe. Elle figure dans le code source de la page : n'importe quel visiteur peut ouvrir « Afficher le code source » et la lire. Tous les widgets de chat fonctionnent ainsi, et c'est normal.

Ce qu'il faut retenir :

  • La clé ne permet pas de se connecter à l'espace client, de lire les dialogues d'autrui, d'exporter votre base d'abonnés ni de modifier quoi que ce soit au bot. Elle ouvre exactement une possibilité : démarrer une conversation avec ce bot.
  • Ne traitez pas la clé comme un secret : la cacher ou l'obscurcir n'apporte rien.
  • S'il faut la remplacer (après une séparation avec un prestataire, par exemple), le support s'en charge. Après le remplacement, l'ancien code du site cesse de fonctionner et doit être mis à jour.

Liste des domaines autorisés

Le widget dispose d'une liste de domaines autorisés (origin allowlist) : les adresses de sites depuis lesquelles il a le droit de fonctionner. Elle est définie à la connexion du canal et se modifie ensuite sans réémettre la clé : votre code sur le site reste inchangé.

Les entrées sont des origines : schéma, hôte et, s'il n'est pas standard, port.

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

La comparaison est exacte, caractère par caractère :

  • Pas de jokers. https://*.example.com ne fonctionnera pas : listez les sous-domaines un par un.
  • example.com et www.example.com sont des entrées différentes. Si votre site répond aux deux, ajoutez les deux.
  • http:// et https:// sont également des entrées différentes. En général seul https:// est nécessaire, mais une préproduction en http:// doit être ajoutée explicitement.
  • Pas de chemin : https://example.com/shop est refusé, une origine, c'est l'adresse du site et rien d'autre.

Si votre domaine réel n'est pas dans la liste, le widget ne démarrera pas. Le navigateur du visiteur reçoit un refus « origin not allowed » et le chat ne s'ouvre tout simplement pas. Cela survient typiquement après un déménagement de domaine, l'ajout d'un sous-domaine ou le lancement d'une seconde version linguistique sur sa propre adresse : chacune demande sa propre entrée.

Si la liste est vide, il n'y a pas de restriction et le widget fonctionne depuis n'importe où. Considérez cet état comme « pas encore configuré », et non comme une ouverture délibérée : dès que vous connaissez vos domaines, inscrivez-les.

Ce que cette restriction protège, et ce qu'elle ne protège pas

Elle protège : un site tiers ne peut pas intégrer votre widget. Sinon, quelqu'un pourrait copier votre extrait sur sa page et les navigateurs de vrais visiteurs livreraient à ce site le contenu des conversations avec votre bot. C'est exactement ce que la liste ferme.

Elle ne protège pas : elle n'arrête pas celui qui a copié la clé et interroge la plateforme directement — non pas depuis un navigateur, mais depuis un script. L'en-tête portant l'adresse du site est posé par le navigateur ; un programme hors navigateur peut l'omettre ou en envoyer un autre. Voyez donc cette liste comme une restriction d'intégration, pas comme une frontière de sécurité. Contre les abus, ce sont les limites de fréquence et la modération des dialogues de la plateforme qui protègent, pas cette liste.

Apparence

Dans WidgetApparence se règlent :

  • Position : le bouton en bas à gauche ou en bas à droite.
  • Couleur d'accent : couleur du bouton et des éléments du chat. Utilisez votre couleur de marque pour que le widget ne paraisse pas rapporté.
  • Nom du chat : libellé du bouton et titre du panneau. En général le nom de l'entreprise ou celui sous lequel le bot se présente.

À côté se trouve un aperçu : un widget vivant qui montre le résultat avant l'enregistrement.

Le widget n'a pas encore de message d'accueil : ces trois réglages sont les seuls. Le premier message est envoyé par le bot en réponse au visiteur, selon vos réactions habituelles.

Les modifications parviennent seules à votre site : le widget lit ses réglages au chargement de la page, le code inséré n'est jamais à modifier.

Les visiteurs sont anonymes

Le visiteur ne remplit rien pour écrire. Au premier démarrage, le widget reçoit de la plateforme un identifiant anonyme de visiteur et le range dans le navigateur, afin qu'en revenant la personne retrouve sa conversation plutôt qu'un chat vide.

Il en découle que :

  • Dans les dialogues, ce visiteur apparaît comme anonyme : ni nom, ni téléphone, ni e-mail tant qu'il ne les écrit pas lui-même.
  • L'identifiant vit dans un navigateur précis. Un autre navigateur, un autre appareil ou des données de site effacées, c'est un nouveau visiteur avec un historique vierge.
  • Un visiteur anonyme peut être relié à un client que vous connaissez déjà, par exemple un utilisateur authentifié de votre espace client. La section suivante explique comment.

Relier un visiteur à votre propre utilisateur

Si un visiteur est déjà connecté sur votre site, vous pouvez indiquer à la plateforme de qui il s'agit. Après ce rattachement, la conversation cesse d'être anonyme : elle est fusionnée avec le profil de cette personne, et ses messages anonymes antérieurs ne sont pas perdus.

Un seul appel suffit, avec une fonction qui produit la preuve :

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 }
});

Le widget appelle votre fonction avec visitorId — l'identifiant anonyme actuel du visiteur pour ce navigateur, la seule valeur que seul le widget connaît — et attend qu'elle retourne, ou résolve, { userId, signature, expiresAt }. La seule tâche de votre fonction est de transmettre visitorId à votre propre serveur, avec l'identifiant de l'utilisateur connecté (currentUser.id ci-dessus, quel que soit son nom chez vous), et de renvoyer exactement ce que répond votre serveur. La signature elle-même se fait sur votre serveur, pas dans cette fonction — voir plus bas.

Un callback, pas une valeur déjà calculée ni un moyen de lire visitorId directement, pour deux raisons :

  • Le moment où il existe. Le widget génère visitorId de façon asynchrone au démarrage — il n'existe pas à l'instant où le script de chargement s'exécute, et mybot.identify lui-même n'est installé qu'une fois cela terminé (détails plus bas). Ainsi, au moment où votre page peut appeler mybot.identify, l'identifiant reçu par votre fonction est garanti réel. Un simple getter n'aurait pas cette garantie : rien n'empêcherait une page de le lire une ligne trop tôt et de ne rien obtenir, produisant en silence une signature qui ne correspond jamais, sans aucun indice sur la raison.
  • La portée. Le code de votre page n'a jamais à conserver, stocker ou faire circuler visitorId à la main — il n'existe qu'à l'intérieur de cette fonction, pour le seul appel qu'elle a besoin de faire.

Dès qu'il quitte le widget, visitorId est une capacité au porteur (bearer capability) : quiconque parvient à obtenir une signature pour lui peut être rattaché à la session de ce visiteur. Envoyez-le uniquement à votre propre serveur, via votre propre requête authentifiée, et nulle part ailleurs — ne le journalisez pas, ne le transmettez pas à un tiers, ne le placez pas dans un appel d'analytique côté client.

La signature se calcule uniquement sur votre propre serveur

Dans votre fonction ci-dessus, votre propre serveur — jamais le navigateur — calcule la signature : un HMAC-SHA256, avec le secret du widget comme clé, en hexadécimal, les 32 premiers caractères, de trois valeurs réunies en un seul message. La même formule figure dans l'espace client à côté du secret, sous « Signing algorithm » :

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

Chaque + "\n" + ci-dessus représente un véritable saut de ligne entre les parties, pas les deux caractères antislash et n. Si votre fonction HMAC prend le message sous forme d'une seule chaîne, assemblez les trois parties avec un vrai saut de ligne : trois appels .update() séparés sans saut de ligne entre eux hachent un message différent, et incorrect.

Les trois parties :

  • userId — l'identifiant de l'utilisateur connecté, la même valeur que votre serveur reçoit de la fonction ci-dessus et renvoie dans sa réponse.
  • visitorId — l'identifiant anonyme de visiteur que votre fonction a reçu en argument et transmis tel quel à votre serveur.
  • expiresAt — un horodatage unix en secondes (pas en millisecondes) au-delà duquel cette signature précise cesse d'être valide, produit par votre serveur au moment de signer. La plateforme rejette un appel dont le expiresAt est déjà dans le passé, ainsi qu'un appel de plus de 24 heures dans le futur — signez juste avant de la renvoyer depuis votre fonction, pas une fois pour toutes en la mettant en cache pour de futures requêtes.

Cette expiration est le véritable objet de ce changement, pas un détail secondaire. L'ancienne formule ne couvrait que l'identifiant utilisateur, si bien qu'une signature capturée une fois — journalisée quelque part, interceptée sur le réseau, peu importe — restait valide indéfiniment et correspondait à n'importe quel visiteur, pas seulement à celui pour lequel elle avait été émise. Quiconque s'en emparait pouvait la rejouer dans un navigateur totalement différent, et la plateforme aurait fusionné la navigation anonyme d'un inconnu avec le profil d'un vrai client. Lier la signature à un visitorId précis et lui donner une durée de vie courte referme les deux bouts de cette faille : elle ne se valide que pour la session à laquelle elle a été délivrée, nulle part ailleurs, et cesse totalement de se valider une fois expiresAt dépassé — même une signature qui fuite ne reste donc qu'un risque bref, limité à une seule session, jamais permanent.

Calculez les trois valeurs sur votre serveur et renvoyez-les depuis votre fonction toutes faites. Ce n'est pas une formalité : pour calculer la signature dans le navigateur, il faudrait y livrer le secret, c'est-à-dire le remettre à chaque visiteur de la page. Dès lors, n'importe qui pourrait se faire passer pour n'importe lequel de vos clients et lire sa conversation. La signature (et visitorId/expiresAt pour lesquels elle a été calculée) peut revenir vers le navigateur ; le secret, non.

L'appel lui-même n'affiche rien : le rattachement se fait côté serveur, silencieusement. Si votre fonction lève une erreur ou que sa promesse est rejetée, ou si la plateforme refuse la signature obtenue — incohérence, ou expiresAt manquant, déjà passé, ou à plus de 24 heures dans le futur —, rien de plus ne se produit : le visiteur ne remarque rien et continue d'écrire en anonyme.

mybot.identify n'existe pas immédiatement : il apparaît une fois le bundle principal du widget chargé, avec un visitorId réel (voir « Le moment où il existe » ci-dessus). Appelez-le depuis un gestionnaire de chargement de page, pas en première ligne du <head>.

Où trouver le secret et comment le changer

Le secret du widget est la clé avec laquelle vous signez. Il se trouve dans les réglages du widget, section « identify() secret ». Si cette section indique que le widget n'est pas encore connecté, connectez d'abord le canal web : le secret apparaît avec lui.

Afficher. Le bouton « Reveal secret » récupère la valeur. Elle n'apparaît pas en clair : vous voyez d'abord des points, et un interrupteur distinct « Show » dévoile les caractères (« Mask » les masque de nouveau). À côté se trouve « Clear from screen », qui retire la valeur de l'écran.

Vous pouvez l'afficher autant de fois que nécessaire, et l'afficher n'invalide rien : le secret reste valide après la fermeture de l'écran. C'est ainsi parce qu'il est stocké chiffré et non haché — la plateforme a besoin de sa valeur réelle pour vérifier chaque signature. Si vous déployez un nouveau backend dans six mois, revenez simplement le consulter.

Une fois copié, rangez derrière vous. La plateforme ne peut pas vider votre presse-papiers : depuis un navigateur, c'est impossible de façon fiable, nous ne le promettons donc pas. Retirez la valeur de l'écran et videz le presse-papiers à la main quand vous avez terminé, surtout sur un ordinateur partagé.

Changer. Le bouton « Rotate secret », avec une étape de confirmation. L'action est immédiatement cassante : dès que le nouveau secret existe, l'ancien cesse de valider. Il n'y a pas de fenêtre de recouvrement, aucun instant où les deux clés fonctionnent.

Ce qui casse, c'est précisément le rattachement des visiteurs à vos utilisateurs. Les conversations ne sont pas interrompues : les visiteurs continuent d'écrire et de recevoir des réponses, simplement en anonymes, jusqu'à ce que votre backend signe avec le nouveau secret. Le code d'intégration et la liste des domaines autorisés ne sont pas touchés, rien à changer sur votre site.

Changez donc le secret en connaissance de cause : préparez d'abord le déploiement du backend avec la nouvelle valeur, et seulement ensuite appuyez sur « Rotate secret ». Appuyer pour voir ce que ça donne est une mauvaise idée. La nouvelle valeur s'affiche à l'écran juste après le changement, vous pouvez donc la copier immédiatement.

Comment le widget se comporte sur la page

Le widget est conçu pour ne pas interférer avec votre site :

  • Il vit dans un conteneur isolé (Shadow DOM fermé). Vos styles n'y entrent pas et les siens ne sortent pas sur la page. Effet de bord : vous ne pouvez pas le restyler avec votre CSS — passez par les réglages d'apparence.
  • Le conteneur couvre l'écran mais n'intercepte pas les clics : ils traversent vers la page, seuls le bouton et le panneau réagissent.
  • Le widget est toujours au-dessus de votre contenu ; vos propres couches ne peuvent pas le masquer.
  • Toute erreur interne reste interne : au pire le bouton de chat ne marche pas, votre site continue de fonctionner. Si le widget n'apparaît pas, regardez la console du navigateur — c'est presque toujours la CSP ou un domaine absent de la liste.

Si le widget n'apparaît pas

Vérifiez dans l'ordre :

  1. Ouvrez le code source de la page et assurez-vous que les deux balises sont présentes et que la clé n'est pas vide.
  2. Vérifiez que l'adresse du site est exactement celle de la liste, https:// et www compris.
  3. Regardez la console du navigateur : un message Content Security Policy signifie qu'il faut autoriser l'hôte de la plateforme dans la CSP du site.
  4. Assurez-vous qu'un bloqueur de publicités ne supprime pas le widget : testez en fenêtre privée sans extensions.

Et ensuite