Widget d'appel web

Permettre à un visiteur de parler à votre agent depuis votre site : une balise script, une clé publiable limitée à un agent et à vos domaines, rien à héberger.

Le widget ajoute un bouton à votre page. Quand un visiteur clique, le navigateur demande le microphone, lance un appel web avec votre agent et affiche l'état de l'appel. Le script est un seul fichier sans dépendance : il est servi par Infynia, tout comme le client LiveKit qu'il charge au premier clic. Le seul tiers est Google reCAPTCHA v3, qui protège la clé publiable.

Ajouter le widget à une page

  1. Dans le dashboard, ouvrez Développeurs, puis Clés, et créez une clé publiable : choisissez l'agent, listez les sites autorisés à l'utiliser (adresses HTTPS, sans joker) et fixez un plafond de minutes par mois. La clé n'est affichée qu'une fois.
  2. Collez le snippet ci-dessous à l'endroit où le bouton doit apparaître, ou n'importe où dans la page pour un bouton flottant en bas à droite. L'onglet Clés affiche le même snippet, avec votre clé déjà remplie juste après la création.
  3. Ouvrez la page depuis l'un des sites autorisés et cliquez sur le bouton.
<script src="https://infynia.quantylcore.com/widget/v1.js"
        data-key="inf_pub_YOUR_KEY"
        data-recaptcha-site-key="YOUR_RECAPTCHA_SITE_KEY"
        defer></script>

data-recaptcha-site-key est la clé de site reCAPTCHA v3 publique de votre compte Infynia. Elle n'est pas secrète.

AttributRôle
data-keyLa clé publiable (inf_pub_...). Obligatoire, sauf si vous utilisez data-session-token.
data-recaptcha-site-keyClé de site reCAPTCHA v3. Obligatoire avec data-key.
data-session-tokenJeton de session à usage unique émis par votre serveur (voir plus bas). Remplace data-key et data-recaptcha-site-key.
data-targetSélecteur CSS de l'élément qui reçoit le bouton, par exemple #call. Sans lui, le bouton flotte en bas à droite.
data-langfr ou en. Par défaut : la langue de la page (attribut lang).
data-labelTexte du bouton au repos. Par défaut : « Parler à l'agent ».
data-api-baseRacine de l'API. Par défaut : le domaine qui sert le script, suivi de /api/v1.

Ce que voit le visiteur

ÉtatBoutonAnnoncé aux lecteurs d'écran
Au repos« Parler à l'agent »Le libellé du bouton.
Connexion« Connexion… », inactif« Connexion à l'agent. Autorisez le microphone si votre navigateur le demande. »
En appel« Raccrocher », rouge« Appel en cours. Votre microphone est activé. »
Erreur« Réessayer »La raison, sous forme d'alerte : microphone bloqué, panne réseau, site non autorisé, trop de tentatives, agent indisponible, etc.
  • Le bouton est un vrai élément button : Tab l'atteint, Entrée et Espace l'activent, Échap met fin à un appel en cours, et l'anneau de focus est toujours visible.
  • Les textes sont en français ou en anglais, sans code d'erreur technique montré au visiteur.
  • La pulsation de l'indicateur d'appel s'arrête quand le visiteur préfère les animations réduites.
  • Le widget vit dans un shadow DOM : les styles de votre page ne le modifient pas, et il ne modifie pas votre page.

API JavaScript

Pour placer le bouton vous-même ou suivre l'appel, chargez le script sans attributs et appelez InfyniaWidget.init. Elle renvoie start(), stop(), getState() et destroy().

<script src="https://infynia.quantylcore.com/widget/v1.js" defer></script>
<div id="call-button"></div>
<script>
  window.addEventListener("DOMContentLoaded", function () {
    var widget = InfyniaWidget.init({
      key: "inf_pub_YOUR_KEY",
      recaptchaSiteKey: "YOUR_RECAPTCHA_SITE_KEY",
      target: "#call-button",
      lang: "en",
      onStateChange: function (e) { console.log(e.state, e.error); },
    });
  });
</script>
  • Options : key, recaptchaSiteKey, target, lang, label, apiBase, onStateChange, plus sessionToken et getSessionToken (voir plus bas).
  • onStateChange reçoit { state } avec idle, connecting, in-call ou error, et { state: "error", error } avec un code court. Les mêmes données sont envoyées comme événement infynia:statechange sur l'élément du widget.

Ce que fait l'appel

Le widget envoie POST /api/v1/web-calls avec la clé et un jeton reCAPTCHA, puis rejoint la salle reçue. L'appel est coupé par le serveur après la durée maximale, 10 minutes par défaut ou ce qu'il reste du plafond mensuel, la plus courte des deux. Les minutes commencées comptent sur le plafond de la clé ; la part inutilisée d'une réservation est rendue à la fin de l'appel.

Code d'erreurSignification
origin_not_allowed (403)Le site qui héberge le widget n'est pas dans la liste autorisée de la clé.
invalid_key (401)La clé est fausse ou révoquée.
invalid_recaptcha (400)La vérification reCAPTCHA a échoué. Le visiteur peut recharger la page et réessayer.
rate_limited (429)Trop d'appels depuis la même adresse : 6 par tranche de 10 minutes, pour le chemin par clé comme pour le chemin par jeton de session. L'adresse est celle vue par le serveur, pas un en-tête envoyé par le visiteur.
minute_cap_reached (429)Le plafond de minutes mensuel de la clé est atteint.
agent_unavailable (503)L'agent de la clé n'est pas actif.
plan_required (403)L'organisation n'a pas de forfait payant actif.

Une clé publiable est faite pour figurer dans une page. Elle ne peut que lancer un appel vers son agent, depuis les sites que vous listez, dans la limite de son plafond mensuel : elle ne peut rien lire. Si elle fuite, révoquez-la depuis l'onglet Clés et créez-en une autre. Pour les intégrations qui ne doivent mettre aucune clé dans la page, utilisez les jetons de session.

Jetons de session : aucune clé dans la page

Si vous ne voulez aucune clé dans votre page, votre serveur peut émettre un jeton pour chaque visiteur avec POST /v1/web-call-sessions et une clé secrète qui a le scope webcalls:write. Le jeton est à usage unique et valable 5 minutes ; le navigateur le donne au widget à la place de la clé publiable, et il n'y a ni reCAPTCHA, ni liste de sites, ni limite par adresse, car le jeton prouve que votre serveur voulait cet appel.

curl -X POST https://infynia.quantylcore.com/api/v1/web-call-sessions \
  -H "Authorization: Bearer $INFYNIA_API_KEY" \
  -H "Idempotency-Key: visit-8841" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "AGENT_ID"}'
{
  "token": "inf_wcs_3f9a0c5d7e1b4a68c2d09e7f1a3b5c4d6e8f0a1b2c3d4e5f",
  "expiresAt": "2026-10-16T09:05:00.000Z",
  "livemode": true
}
  • Corps : agentId (obligatoire) et variables (facultatif, valeurs simples, conservées avec l'appel et jamais envoyées au navigateur). La clé peut être limitée à certains agents ; une clé live exige un forfait payant actif.
  • Envoyez une Idempotency-Key pour réessayer sans risque : la même clé renvoie le même jeton, qui peut avoir expiré ou été utilisé entre-temps, donc en cas de doute émettez-en un nouveau avec une nouvelle clé.
  • Le jeton n'est affiché qu'une fois, et seul son hachage est conservé. Ne le journalisez pas et ne le mettez pas dans une URL.
  • La durée de l'appel est limitée comme pour une clé publiable (10 minutes par défaut). Les minutes du forfait de votre organisation sont vérifiées à l'utilisation du jeton. La même limite de 6 appels par tranche de 10 minutes et par adresse de visiteur s'applique que pour une clé publiable, avec rate_limited (429) au-delà.
  • Un jeton faux, expiré ou déjà utilisé reçoit 401 invalid_session_token. Un refus dû au forfait, au quota ou à un agent indisponible ne consomme pas le jeton ; un échec au démarrage de l'appel le consomme, donc émettez-en un autre.

Donnez le jeton au widget avec data-session-token (un seul appel : l'attribut est retiré de la page dès qu'il est lu), ou avec les options JavaScript. getSessionToken est appelée avant chaque appel, le visiteur peut donc rappeler.

<script src="https://infynia.quantylcore.com/widget/v1.js"
        data-session-token="SESSION_TOKEN_FROM_YOUR_SERVER"
        defer></script>
<script src="https://infynia.quantylcore.com/widget/v1.js" defer></script>
<script>
  window.addEventListener("DOMContentLoaded", function () {
    InfyniaWidget.init({
      getSessionToken: function () {
        return fetch("/my-server/web-call-session", { method: "POST" })
          .then(function (r) { return r.json(); })
          .then(function (s) { return s.token; });
      },
    });
  });
</script>

Vous pouvez aussi envoyer vous-même POST /api/v1/web-calls avec { "sessionToken": "inf_wcs_..." } et rejoindre la salle renvoyée avec n'importe quel client LiveKit.

Avec une clé de test (inf_test_), le jeton lance un appel simulé : le widget l'affiche en cours pendant environ une minute, rien n'est connecté, aucune minute n'est décomptée et un appel avec livemode: false est enregistré. Voir le guide du mode test.

Garder la clé à sa place

  • Ne mettez jamais une clé secrète (inf_live_, inf_test_) dans une page : elle peut faire bien plus que lancer un appel web.
  • Le widget n'envoie la clé que dans le corps de la requête d'appel, au domaine qui sert le script. Il ne l'écrit jamais dans la page, la console ni les événements.
  • Gardez la liste des sites autorisés la plus courte possible, et le plafond mensuel à ce que vous pouvez payer.