01 —Introduction
Le Lead Intent Agent (LIA) est un bundle JavaScript mono-fichier (lia.js) conçu pour être intégré dans n'importe quelle propriété web. Il observe en continu le comportement des visiteurs, enrichit chaque événement avec le contexte de l'appareil, de la langue et de l'acquisition, et transmet des charges utiles structurées à une API backend.
LIA a été construit autour de trois principes fondamentaux :
- Privacy-first — respecte les entêtes DoNotTrack, ne lit jamais les valeurs des champs de formulaire, et assainit les paramètres URL pour supprimer les données personnelles avant la transmission.
- Livraison résiliente — les événements sont mis en lot en mémoire et transmis via
fetch + keepaliveavec réessais à backoff exponentiel. Les événements de sortie de page utilisent la sémantiquesendBeaconpour résister à la fermeture d'onglet. - Zéro dépendance — livré sous forme d'IIFE unique sans prérequis d'exécution externe, compatible avec tous les navigateurs modernes et les frameworks SPA.
L'arborescence source est divisée en six modules ES (identity.js, context.js, behaviour.js, suspicion.js, transport.js, tracker.js) orchestrés par agent.js. Le script de build (build.js) supprime la syntaxe ES module et enveloppe le résultat dans un IIFE, produisant lia.js et lia.min.js.
02 —Vue d'ensemble de l'architecture
L'agent suit une architecture en pipeline : les signaux bruts du navigateur traversent des étapes d'enrichissement avant d'être mis en file d'attente dans un buffer d'événements en mémoire. Une boucle de vidage basée sur un timer transmet les lots à l'API ; un callback de mise à jour de session permet au serveur de renvoyer des données de scoring dans l'état de l'agent.
03 —Carte des modules
LIA est composé de six modules spécialisés. Chaque module a une responsabilité unique et communique via l'orchestrateur agent.js.
identity.js
Génère et persiste le visitor_id (longue durée) et le session_id (TTL). Repli gracieux : localStorage → sessionStorage → mémoire vive.
context.js
Capture l'empreinte de l'appareil, la famille navigateur/OS, la locale, le fuseau horaire, les infos réseau et les paramètres UTM. Assainit les query strings pour supprimer les données personnelles.
behaviour.js
Suit les jalons de profondeur de défilement (25/50/75/90/100%), le temps actif, la détection d'inactivité (30s), et déclenche engaged_session quand les deux seuils sont atteints.
suspicion.js
Calcule un score de suspicion de bot côté client (0–100) en utilisant les drapeaux WebDriver, les indices d'UA headless, les dimensions d'écran nulles, le nombre de plugins et la variance de timing.
transport.js
Gère le batching des événements (max 10/lot, capacité de file 100), le timer de vidage (5s), la logique de réessai (3 tentatives, backoff exponentiel) et la livraison fiable au déchargement.
tracker.js
Détection des clics CTA avec reconnaissance des plateformes de réservation. Suivi du cycle de vie des formulaires (ouvert → démarré → champ touché → soumis) via IntersectionObserver et délégation d'événements.
04 —Installation
Installez LIA en ajoutant un objet de configuration et la balise script dans le <head> de votre HTML. L'agent s'initialise automatiquement lorsque le DOM est prêt.
<!-- 1. Déclarer la config AVANT la balise script --> <script> window.LeadIntentConfig = { siteId: 'votre-site-id', apiBaseUrl: 'https://api.votrebackend.com', apiKey: 'sk-...', sessionTtlMinutes: 30, captureForms: true, respectDoNotTrack: true, debug: false, }; </script> <!-- 2. Charger l'agent (async est possible) --> <script async src="/lia.js"></script>
Si window.LeadIntentConfig n'est pas présent à l'exécution de lia.js, l'agent utilise un MutationObserver sur <head> pour attendre l'objet de configuration. Déclarez la config de manière synchrone avant le script pour une initialisation optimale.
05 —Référence de configuration
| Clé | Type | Défaut | Description |
|---|---|---|---|
| siteId | string | — | requis Identifiant unique du site suivi. Envoyé avec chaque événement. |
| apiBaseUrl | string | — | requis URL de base de l'API backend. L'agent ajoute /v1/events/batch, /v1/identify, etc. |
| apiKey | string | — | requis Envoyé en tant qu'entête X-Api-Key sur toutes les requêtes. |
| sessionTtlMinutes | number | 30 | Délai d'expiration de session par inactivité en minutes. |
| captureForms | boolean | true | Activer le suivi du cycle de vie des formulaires. |
| captureScroll | boolean | true | Activer le suivi de profondeur de défilement et la mesure du temps actif. |
| captureClicks | boolean | true | Activer la détection des clics CTA via les sélecteurs configurés. |
| respectDoNotTrack | boolean | true | Si true, définit consent_state à essential_only lorsque l'entête DNT du navigateur vaut 1. |
| ctaSelectors | string[] | [] | Sélecteurs CSS supplémentaires à traiter comme éléments CTA, fusionnés avec la liste intégrée. |
| pageCategoryRules | Rule[] | [] | Tableau de règles { match: RegExp, category: string } évaluées avant les heuristiques intégrées. |
| debug | boolean | false | Active la sortie console.log détaillée et force une identité fraîche à chaque chargement (développement uniquement). |
06 —Consentement & Confidentialité
LIA est livré avec des hooks de gestion du consentement en propre. Le suivi est entièrement bloqué lorsque le consentement est 'denied'. Une méthode setConsent() permet l'intégration avec n'importe quelle CMP externe.
// États : 'unknown' | 'granted' | 'denied' | 'essential_only' maCMP.onDecision((decision) => { window.LeadIntentAgent.setConsent( decision.analytics ? 'granted' : 'denied' ); }); const session = window.LeadIntentAgent.getSession(); // → { sessionId, visitorId, leadId, score, segment }
Gardes-fous de confidentialité intégrés
- Les query strings sont assainies — seuls les paramètres UTM et une liste d'autorisation connue sont transmis.
- Les valeurs des champs de formulaire ne sont jamais lues — seule la classification du type de champ (email, téléphone, entreprise) est suivie.
- L'entête DoNotTrack force le mode
essential_onlylorsquerespectDoNotTrack: true. - La méthode
reset()efface toute identité persistée de toutes les couches de stockage.
07 —Module d'identité
LIA maintient deux niveaux de persistance d'identité avec des replis de stockage automatiques :
Visitor ID
- lia_visitor_id longue durée
Généré une fois par navigateur via crypto.randomUUID(). Persiste jusqu'à suppression. Utilisé pour la reconnaissance de visiteur inter-sessions.
Session ID
- lia_session_id TTL session
- lia_session_start horodatage
Rafraîchi à chaque interaction. Expire après sessionTtlMinutes d'inactivité. Une nouvelle session émet session_started.
First Touch
- lia_first_touch préservé
Capture les paramètres UTM et le referrer dès la toute première visite. Jamais écrasé — préservé pour l'attribution sur toutes les sessions suivantes.
08 —Suivi comportemental
Le module de comportement surveille la qualité d'engagement en utilisant des listeners non-invasifs et throttlés. Il ne se déclenche jamais sur chaque événement DOM brut — tous les signaux sont anti-rebondis ou limités par jalons.
Les jalons suivis sont : profondeur de défilement à 25%, 50%, 75%, 90% et 100% — temps actif à 30s, 60s, 120s — session engagée (temps actif ≥ 30s ET défilement ≥ 50%) — résumé de sortie de page au déchargement.
Le temps d'inactivité (30s sans interaction) met en pause le compteur de temps actif. Les changements de visibilité (onglet caché) sont également tracés séparément.
09 —Couche transport
Le module transport gère toutes les communications réseau. Il regroupe les événements par lots, réessaie les livraisons échouées, et s'assure que les événements critiques (soumissions de formulaires, réservations) sont vidés immédiatement.
| Paramètre | Valeur | Notes |
|---|---|---|
| MAX_BATCH_SIZE | 10 | Événements par lot vidé |
| FLUSH_INTERVAL_MS | 5 000 | Intervalle de vidage basé sur timer |
| MAX_RETRY_ATTEMPTS | 3 | Réessais avec backoff exponentiel (500ms, 1s, 2s) |
| MAX_QUEUE_SIZE | 100 | Suppression FIFO en cas de dépassement (le plus ancien supprimé) |
Événements critiques — vidage immédiat
Les événements suivants contournent le timer de lot et déclenchent un flush() immédiat : form_submitted · booking_started · identify · session_ended · booking_opened
10 —Moteur de suspicion
Le module de suspicion calcule un score de qualité côté client pour détecter le trafic automatisé. Ce sont des signaux faibles uniquement ; l'enrichissement côté serveur fait autorité. Un score ≥ 30 déclenche un événement headless_suspected.
| Signal | Score | Méthode de détection |
|---|---|---|
webdriver_true | +30 | navigator.webdriver === true |
headless_chrome_ua | +20 | /HeadlessChrome/.test(userAgent) ET pas de window.chrome |
zero_screen | +20 | screen.width === 0 && screen.height === 0 |
no_plugins | +5 | navigator.plugins.length === 0 (non-Firefox) |
no_languages | +5 | navigator.languages.length === 0 |
permissions_anomaly | +5 | typeof navigator.permissions === 'undefined' |
11 —API publique
Doit être appelé une seule fois avant toute autre méthode. Typiquement appelé automatiquement via window.LeadIntentConfig. Émet session_started ou session_resumed.
- eventNamestringNom de l'événement personnalisé. Envoyé avec l'enrichissement complet de page et de contexte.
- propertiesobject?Propriétés arbitraires clé-valeur fusionnées dans l'enveloppe
properties.
- payload.emailstring?Adresse email du lead. Envoyée à
POST /v1/identify. - payload.namestring?Nom complet du lead.
- payload.companystring?Nom de l'entreprise.
Retourne la réponse backend incluant lead_id. Les événements suivants incluront ce lead_id pour l'attribution.
Retourne { sessionId, visitorId, leadId, score, segment } de manière synchrone. Le score et le segment sont renseignés après le premier aller-retour API.
États acceptés : 'granted' | 'denied' | 'essential_only' | 'unknown'. Définir 'denied' arrête immédiatement toute émission d'événements.
Supprime toutes les clés du localStorage, sessionStorage et du stockage en mémoire. Réinitialise les IDs de session, visiteur et lead. Arrête le timer de vidage.
12 —Catalogue d'événements
| Nom de l'événement | Type | Propriétés clés | Description |
|---|---|---|---|
session_started | cycle de vie | visitor_id | Déclenché lors de la création d'une nouvelle session |
session_resumed | cycle de vie | visitor_id | Déclenché lors de la reprise d'une session dans son TTL |
page_view | cycle de vie | page_category, is_entry_page | Déclenché à chaque navigation, y compris les changements de route SPA |
page_exit | cycle de vie | active_time_seconds, max_scroll_depth | Déclenché au déchargement de page ; livré via keepalive fetch |
scroll_depth_reached | comportemental | depth_percent (25/50/75/90/100) | Déclenché une fois par jalon par page, throttlé à 200ms |
active_time_30s | comportemental | active_seconds | Temps actif cumulé atteint 30 secondes |
engaged_session | intention | active_seconds | Actif ≥ 30s ET défilement ≥ 50% — signal d'engagement de haute qualité |
cta_click | intention | cta_text, cta_id | Clic sur un élément CTA correspondant aux sélecteurs configurés |
booking_opened | intention | source, href | Clic sur un lien de réservation Calendly / Cal.com / Acuity |
form_opened | critique | form_id | Formulaire entré dans le viewport (seuil 30% via IntersectionObserver) |
form_started | critique | form_id | Premier champ focalisé dans un formulaire |
form_submitted | critique | form_id, elapsed_seconds | Événement de soumission de formulaire. Déclenche un vidage immédiat du transport. |
pricing_view | intention | page_category | Auto-émis quand le chemin de page correspond à la catégorie tarification |
headless_suspected | comportemental | signals[], score | Score de suspicion ≥ 30 — trafic automatisé suspecté |