Installer Probant sur votre site.
Le script navigateur, le consentement, les conversions, l’affectation côté serveur et l’API des événements.
Deux façons d’affecter les visiteurs
Probant affecte chaque visiteur à une variante, toujours la même, puis mesure ses expositions et ses conversions. Deux outils s’en chargent et se complètent.
- Le script navigateur (
/sdk/v1.js, moins de 6,5 Ko compressé) affecte, applique les modifications créées dans l’éditeur et mesure. - L’aide serveur (
sdk/edge.ts) affecte avant le rendu, sur votre serveur ou en périphérie de réseau, pour un affichage sans aucun clignotement. Le script reste nécessaire pour mesurer.
Les deux lisent le même cookie, probant_id, et donnent donc la même variante au même visiteur.
Installer le script navigateur
Collez ce code dans le <head> de chaque page, avec la clé publique de votre espace (pk_…). La clé secrète (sk_…) n’a jamais sa place dans une page web : l’API la refuse.
<script>
window.probant = window.probant || [];
["consent", "track"].forEach(function (m) {
probant[m] = probant[m] || function () { probant.push([m].concat([].slice.call(arguments))); };
});
</script>
<script async src="https://app.probant.app/sdk/v1.js" data-key="pk_…"></script>data-key: clé publique de l’espace, obligatoire.data-endpoint: adresse de l’API, si elle diffère de celle du script. Par défaut, l’origine desrc.
Le premier bloc permet d’appeler probant.consent(…) et probant.track(…) avant que le script soit chargé : les appels attendent dans une file et sont rejoués dans l’ordre. Il est facultatif si vous n’appelez rien avant le chargement. Inclus deux fois, le script ne démarre qu’une seule instance.
Le script ne masque jamais la page. Chaque modification s’applique dès que son élément existe, puis de nouveau à chaque redessin (React, navigation sans rechargement), avant l’affichage suivant. Dans vos sélecteurs, visez des repères stables ([data-add-to-cart], identifiants) plutôt que des classes de thème. Un sélecteur invalide compte comme introuvable et n’empêche pas les modifications suivantes.
La vue d’ensemble de l’application montre votre clé une seule fois, avec ce code prêt à copier.
Transmettre le consentement
Si l’espace exige le consentement (réglage par défaut), le script ne pose aucun cookie, n’affecte rien, n’applique rien et n’envoie rien tant que le visiteur n’a pas accepté. Transmettez son choix à chaque page, dès que votre outil de consentement le connaît :
probant.consent(true); // accepté : affectation et mesure, sans bascule de la page affichée
probant.consent(false); // refusé ou retiré : cookie et traces locales effacésLes appels faits avant l’accord, track compris, attendent et sont rejoués après. probant.consent(false) efface le cookie et les traces locales, vide la file d’envoi et arrête l’observation de la page, même si l’espace n’exige pas le consentement.
Rien ne bascule sous les yeux du visiteur. S’il accepte une fois la page affichée, elle reste l’originale, sans exposition : sa variante commence à la page suivante, rechargée ou non, et seules partent les conversions d’un test déjà vu. S’il a déjà accepté lors d’une visite passée (le cookie existe), sa variante s’applique dès le chargement et rien n’est envoyé avant que votre outil confirme l’accord ; un refus efface tout. Un test qui vise les pages où l’on arrive perd ainsi la première vue des nouveaux visiteurs qui acceptent en cours de page, sans biais entre variantes : le plan d’un test le signale.
Un visiteur peut aussi s’opposer à la mesure depuis votre politique de confidentialité, quel que soit le mode : placez-y le lien d’opposition. Il pose un cookie d’opposition que le script, le module de parcours et l’aide serveur respectent, et demande l’effacement des données rattachées à l’identifiant du visiteur.
<div data-probant-opposition></div>
<script async src="https://app.probant.app/sdk/opposition.js" data-key="pk_…"></script>Le cookie probant_id est interne à votre site, SameSite=Lax, et dure au plus 390 jours après la première visite, soit moins de 13 mois. Le script le pose une seule fois ; l’aide serveur le repose avec la même échéance (voir Identité sur Safari) : il n’est jamais prolongé au-delà.
Mesurer les conversions
probant.track("commande", 89.9); // avec un montant
probant.track("ajout-panier"); // sans montant : compte pour 1Le nom (commande, ajout-panier…) est le « nom d’événement » que référencent les métriques de l’espace. Une conversion est rattachée aux tests que le visiteur a vus, avec leur variante. L’exposition à un test est envoyée une fois par visiteur, test et session.
Les envois sont groupés et partent par navigator.sendBeacon, qui termine la requête même si la page se ferme ; à défaut, par fetch avec keepalive.
Leads et conversions du CRM
Pour un site de leads, le formulaire transporte l’identifiant visiteur Probant dans un champ caché, que votre CRM garde avec le lead. Activé dans Probant (page Leads), le script le remplit dans les formulaires marqués, après l’accord du visiteur, jamais pour un visiteur opposé :
<form action="/devis" method="post" data-probant-lead>…</form>
// Formulaire en JavaScript, au moment de l’envoi :
const id = typeof probant.leadId === "function" ? probant.leadId() : "";Votre CRM renvoie ensuite « qualifié », « rendez-vous tenu » ou « signé » pour cet identifiant, de serveur à serveur, avec une clé de gestion qui porte la permission « Envoyer des conversions différées » :
POST https://app.probant.app/api/v1/conversions
Authorization: Bearer sk_…
{ "visitorId": "0190f2c4-…", "metric": "lead-qualifie",
"occurredAt": "2026-11-08T10:30:00Z", "externalId": "deal-1042",
"value": 4800, "currency": "EUR" }- Aucune donnée personnelle. Un champ qui ressemble à un e-mail, un nom ou un téléphone fait refuser l’appel entier ; l’identifiant du CRM n’est gardé qu’en empreinte à clé ; la première conversion reçue pour un identifiant fait foi ; celle d’un visiteur inconnu ou opposé est ignorée.
- Une fenêtre. Une métrique « Conversion du CRM » compte la conversion jusqu’à J+N après la première exposition. Un visiteur dont la fenêtre est ouverte attend, hors du calcul, et le plan ajoute N jours à la durée.
- Par outil, d’après leurs pages officielles consultées le 8 novembre 2026 : HubSpot, action « Send a webhook » des workflows (offres Data Hub Professional ou Enterprise) ; Pipedrive, webhook d’automatisation (offres « Advanced and higher ») ; Zapier, Webhooks by Zapier (offres Professional, Team, Enterprise) ; Salesforce, action HTTP Callout d’un Flow, et Make, module HTTP (offres requises non relevées) ; Stripe, branché directement (ci-dessous). Dans chaque cas, envoyez ces seuls champs, jamais la fiche entière. Voir aussi Sites de génération de leads.
Abonnements : Stripe, MRR, rétention
Pour un SaaS, Stripe se branche sans relais : dans Workbench → Webhooks, créez une destination « Snapshot » vers l’adresse de votre espace, cochez les événements ci-dessous, et collez son secret de signature (« whsec_… ») dans la page Abonnements de Probant, qui le chiffre.
POST https://app.probant.app/api/v1/stripe/<identifiant de l’espace>
checkout.session.completed customer.subscription.created
customer.subscription.updated customer.subscription.deleted
invoice.paid invoice_payment.paid
charge.refundedÀ l’inscription, votre serveur place l’identifiant visiteur (champ caché ou probant.leadId()) dans la session Checkout ; Probant retrouve ensuite le visiteur par le client Stripe :
stripe.checkout.sessions.create({
mode: "subscription",
client_reference_id: probantId,
subscription_data: { metadata: { probant_id: probantId } },
…
})- Signature vérifiée comme Stripe la décrit (HMAC-SHA256, signatures
v1, comparaison à temps constant), avec la tolérance de 5 minutes de ses bibliothèques : un événement intercepté ne se rejoue pas. Chaque événement ne compte qu’une fois. - Aucune donnée personnelle. Ni e-mail, ni nom, ni adresse : des montants, des dates, un statut, et chaque identifiant Stripe en empreinte à clé.
- Revenu net, MRR, toujours abonné. Les remboursements se retranchent du revenu encaissé ; le MRR ramène un plan annuel au mois, comme Stripe ; « toujours abonné à J+N » est un état à J+N. Fenêtres jusqu’à 365 jours, avec la même censure.
- Suivi de cohorte. Le rapport d’un test arrêté continue de lire ses visiteurs exposés à J+30, J+90, J+180 et J+365, et dit quand la décision se renverse ; une estimation descriptive, sur des fourchettes simultanées.
- Hors Stripe :
POST /api/v1/subscriptions(statut et valeur mensuelle) etrefundOfsurPOST /api/v1/conversions. Voir aussi Abonnements (SaaS).
Affecter côté serveur, sans clignotement
assignRequest ne dépend d’aucun framework : proxy Next.js, worker, middleware Express… La variante est connue avant le rendu, et la page arrive déjà modifiée.
assignRequest({ cookieHeader, url, userAgent, config, consent })
// → { visitorId, setCookie, assignments }config: la réponse deGET /api/v1/config, à garder en mémoire une minute plutôt que de la demander à chaque requête.consent: le choix du visiteur, lu dans le cookie de votre outil de consentement. Si l’espace exige le consentement, rien n’est affecté sanstrue; unfalseexplicite est toujours respecté.setCookie: à ajouter à chaque réponse quand il n’est pas nul : cookie d’un nouveau visiteur, même cookie reposé avec son échéance d’origine (identité durable sur Safari), ou effacement après un refus.assignments: pour chaque test en cours ou en déploiement, la variante ou la raison de l’exclusion (holdout,outside-layer-slice,url-mismatch,device-mismatch).assignedVariants(assignments)en tire un objet du type{ "date-livraison": "b" }; pendant un déploiement, un visiteur resté sous l’originale y figure aveca.
L’aide serveur se copie dans votre projet : sdk/edge.ts, sdk/visitor.ts, lib/assignment/ et lib/experiments/types.ts. Elle existe aussi en paquet, @probant/edge (voir la documentation de l’API).
Exemple : proxy.ts de Next.js 16
// proxy.ts, à la racine du site
import { NextResponse, type NextRequest } from "next/server";
import { assignRequest, assignedVariants } from "./probant/sdk/edge";
import type { WorkspaceConfig } from "./probant/lib/experiments/types";
// source=edge : Probant sait que l'aide serveur est en place (contrôle de l'identité sur Safari).
const CONFIG_URL = `https://app.probant.app/api/v1/config?key=${process.env.NEXT_PUBLIC_PROBANT_KEY}&source=edge`;
let cache: { config: WorkspaceConfig; expires: number } | null = null;
// Dans un proxy, fetch ignore les options de cache de Next.js : la
// configuration est gardée une minute en mémoire, et l'ancienne sert si
// Probant ne répond pas (le site ne doit jamais attendre Probant).
async function loadConfig(): Promise<WorkspaceConfig | null> {
if (cache && cache.expires > Date.now()) return cache.config;
try {
const response = await fetch(CONFIG_URL, { signal: AbortSignal.timeout(1000) });
if (response.ok) cache = { config: await response.json(), expires: Date.now() + 60_000 };
} catch {
// Probant indisponible : on garde la configuration précédente.
}
return cache?.config ?? null;
}
export async function proxy(request: NextRequest) {
const config = await loadConfig();
if (!config) return NextResponse.next();
const { setCookie, assignments } = assignRequest({
cookieHeader: request.headers.get("cookie"),
url: request.url,
userAgent: request.headers.get("user-agent"),
config,
consent: request.cookies.get("consentement")?.value === "oui", // votre outil de consentement
});
// Les variantes passent au rendu par un en-tête de requête.
const headers = new Headers(request.headers);
headers.set("x-probant-variants", JSON.stringify(assignedVariants(assignments)));
const response = NextResponse.next({ request: { headers } });
if (setCookie) response.headers.append("set-cookie", setCookie);
return response;
}
export const config = {
matcher: ["/((?!_next/|api/|favicon.ico).*)"],
};Dans la page, un composant serveur lit la variante. Avec Cache Components, headers() rend le composant dynamique : placez-le sous <Suspense>.
import { headers } from "next/headers";
export async function DeliveryEstimate() {
const variants = JSON.parse((await headers()).get("x-probant-variants") ?? "{}");
if (variants["date-livraison"] !== "b") return null;
return <p>Livraison estimée : sous 2 jours ouvrés</p>;
}Gardez le script navigateur sur la page : il retrouve la même variante grâce au cookie et envoie l’exposition et les conversions. Pour un test rendu côté serveur, laissez la variante sans modifications dans l’éditeur : le script n’applique alors rien et se contente de mesurer.
Identité durable sur Safari
Safari efface au bout de 7 jours un cookie écrit par un script (Intelligent Tracking Prevention). Posé par le seul script, probant_id disparaît donc chez un visiteur Safari qui revient après une semaine : il reçoit un nouvel identifiant, et peut changer de variante.
L’aide serveur corrige ce trou : à chaque page, setCookie repose le même cookie, avec les mêmes attributs que le script, depuis la réponse de votre site et avec son échéance d’origine (390 jours après la première visite, lue dans l’identifiant). Posé par votre serveur, il n’est plus soumis à la limite de 7 jours, et le script le réutilise sans le réécrire. Posez-le sur la réponse de la page elle-même (proxy, worker, middleware), pas depuis un autre domaine.
Réglages, onglet Script et API, et la Vue d’ensemble disent où en est votre espace, d’après deux signaux mesurés : l’aide serveur vue par Probant quand elle lit la configuration, et l’âge des identifiants reçus de Safari (un identifiant de plus de 8 jours prouve un cookie posé par le serveur).
Mesurer la vitesse des pages
Un module à part, /sdk/v1-vitesse.js, mesure LCP, INP et CLS à chaque page, selon les définitions des Core Web Vitals, et les rattache à chaque test appliqué, avec sa variante. Le script ne le charge que si l’espace mesure la vitesse sur toutes les pages, ou si un test en cours a une métrique de vitesse, et toujours après le consentement quand l’espace l’exige.
Chaque espace a trois métriques intégrées, lues au 75e centile des pages vues comme les Core Web Vitals, et proposées comme garde-fous de chaque test : une variante qui ralentit la page au-delà de la limite est arrêtée seule. Le module mesure aussi ce que coûte Probant chez vos visiteurs : téléchargement de v1.js et temps d’exécution dans les images longues, là où le navigateur les expose.
Envoyer des événements par l’API
Pour envoyer des événements depuis un serveur, par exemple une conversion confirmée par votre back-office. Les commandes elles-mêmes, avec leurs lignes et le coût de leurs produits, passent par l’API des commandes, avec la clé secrète : la marge se calcule alors sur le serveur de Probant, jamais dans la page. Pour un envoi :
POST /api/v1/events
x-probant-key: pk_…
Content-Type: application/json
{ "events": [
{ "type": "exposure", "visitorId": "…", "experiment": "barre-livraison", "variant": "b", "timestamp": 1794299400000 },
{ "type": "conversion", "visitorId": "…", "metric": "commande", "value": 89.9, "timestamp": "2026-11-10T08:30:00Z" },
{ "type": "metric", "visitorId": "…", "metric": "lcp", "value": 2.41, "experiment": "barre-livraison", "variant": "b", "timestamp": 1794299400000 }
] }- 500 événements et 256 Ko au plus par envoi.
timestamp: millisecondes Unix ou date ISO 8601 avec fuseau ; pas plus de 5 minutes dans le futur ni de 7 jours dans le passé.experimentetvariantvont ensemble ; une conversion ou une mesure peut s’en passer (visiteur hors test).- La clé peut aussi passer en paramètre (
?key=pk_…), ce que fait le script avecsendBeacon, qui ne pose pas d’en-tête.
Réponses
| Code | Signification |
|---|---|
202 | { accepted, rejected, errors } : chaque événement est validé seul, un événement fautif n’empêche pas les autres. |
401 | Clé absente, inconnue, révoquée ou secrète. |
413 | Corps trop volumineux. |
503 | Service momentanément indisponible. |
Pour piloter les tests depuis un serveur ou un assistant (créer, lancer, arrêter, lire les résultats) : API de gestion et serveur MCP.
Configuration publique
GET /api/v1/config?key=pk_… renvoie la configuration publique de l’espace : tests en cours, déploiements, holdout et consentement. La réponse est mise en cache 60 secondes.
Déploiements progressifs
Une fois un test décidé, sa variante gagnante peut être déployée par paliers depuis Probant (25 %, 50 % et 100 % par défaut). Rien à changer sur le site : le script et l’aide serveur lisent le déploiement dans la configuration, où l’expérience porte un champ rollout :
{
"key": "barre-livraison",
"status": "stopped",
"rollout": { "variant": "b", "share": 0.25 },
"variants": [ … ]
}- Qui voit la variante. Un tirage propre au déploiement, indépendant de celui des variantes du test, place chaque visiteur dans un seau ; il voit la variante si son seau est sous la part. La part ne fait que monter : un visiteur exposé à un palier l’est encore aux suivants, et personne ne revient à l’originale après avoir vu la nouvelle version.
- Population. Tous les visiteurs ciblés par le test (adresse, appareil). Le holdout de l’espace reste à l’écart : il mesure l’impact cumulé des gagnants, déployés compris.
- Mesure. Les visiteurs restés sous l’originale reçoivent la première variante (
a) ; les deux groupes envoient leur exposition et leurs conversions, ce qui permet de comparer exposés et originale sur les garde-fous à chaque palier. - Statuts. Un déploiement en cours ou suspendu est servi à sa part ; terminé, la variante est servie à tous (
share: 1) sans plus rien mesurer ; ramené à l’originale, l’expérience disparaît de la configuration.
Un script plus ancien ignore les expériences arrêtées : il montre alors l’originale, jamais une variante non voulue.
Comment l’affectation fonctionne
Le script navigateur et l’aide serveur partagent le même code d’affectation : le même visiteur obtient la même variante partout, sans que rien ne soit stocké côté serveur.
- Hachage. MurmurHash3 (32 bits), dont les vecteurs de référence publiés sont vérifiés par les tests.
- Seaux. Chaque visiteur tombe dans l’un des 10 000 seaux, d’après le hachage de son identifiant et d’un sel : une part de trafic se règle au centième de pour cent.
- Trois tirages indépendants, chacun avec son sel : le holdout d’abord, puis la tranche de couche, puis la variante, pondérée. Le ciblage (motif d’adresse avec
*, liste d’appareils) vient en dernier. - Couches. Les tests d’une même couche occupent des tranches disjointes : ils ne voient jamais le même visiteur.
- Clés stables. La clé d’un test ne change plus une fois le test lancé. Un test arrêté ne repart pas : on en crée un nouveau.
Données et conservation
- Base de données et calculs hébergés en région Paris.
- Événements des visiteurs conservés 13 mois après leur réception, puis supprimés chaque nuit.
- Cookie
probant_idinterne au site, de 390 jours au plus après la première visite, jamais prolongé au-delà. - Clés d’API : seuls une empreinte, un préfixe non secret et les quatre derniers caractères sont conservés.
Clé publique
La clé publique est lisible dans la page, comme toute clé de navigateur. Remplissez la liste des origines autorisées, dans les réglages de l’espace, pour la réserver aux domaines de votre site. L’API limite aussi le débit, par clé et par adresse.
Probant Ou pas encore…
Créez votre espace, ou explorez d’abord la démo avec les données d’une boutique fictive.
- Démo ouverte, sans inscription
- Forfait fixe, jamais de dépassement
- Données hébergées à Paris