Flows
Un flow, c'est la règle de routage d'une campagne : qui (filtre geo/device/OS) voit quelle landing et quelle offre, dans quelle proportion (rotation pondérée), pour combien de temps (visitor binding).

Concept
Une campagne sans flow est inutile : /c/<slug> renvoie 404. Un flow est l'objet qui décide, pour CHAQUE clic, quelle landing afficher et quelle offre cibler. Tu peux en avoir 1 (le cas 80 % du temps) ou plusieurs.
Trois types de flows, évalués DANS CET ORDRE pour chaque clic entrant :
forced— overrides ciblés (geo, device, browser…). Le premier qui matche gagne. Sert au geo-targeting (US → landing US,EU → landing EU) ou au device-routing (mobile → landing mobile-first).regular— règles intermédiaires avec filtres souples (rare en pratique).default— le filet : matche TOUS les clics qu'aucunforced/regularn'a capté. Une campagne doit toujours avoir undefaultpour ne pas perdre du trafic en 404.
Dans chaque flow, la rotation interne (schema.landings[] + schema.offers[]) est pondérée : chaque entrée a un weight, Trackily tire au hasard proportionnellement aux poids.
Schéma du flow
{
"type": "default", // forced | regular | default
"name": "Default flow", // libellé libre
"weight": 100, // utilisé uniquement quand plusieurs regular flows coexistent
"filters": [ // tableau de filtres, AND logique
{ "key": "country", "op": "in", "value": ["US", "CA"] },
{ "key": "device", "op": "eq", "value": "mobile" }
],
"schema": {
"landings": [ // rotation pondérée des landings
{ "id": 31, "weight": 25 },
{ "id": 32, "weight": 25 },
{ "id": 33, "weight": 25 },
{ "id": 34, "weight": 25 }
],
"offers": [ // rotation pondérée des offres
{ "id": 12, "weight": 100 }
],
"pairs": [], // utilisé seulement avec rotation_mode=paired
"rotation_mode": "independent" // independent | paired
},
"binding": "cookie", // none | cookie | ip+ua
"binding_hours": 24, // combien de temps le binding tient
"position": 0, // ordre relatif au sein du même type
"is_active": true
}
Voir le code source du tool MCP : autopilot-actions.js:954 (create_flow) et autopilot-actions.js:584 (toolCreateFlow).
Comment faire — via MCP
Tool : create_flow (scope campaigns:write, Tier-1).
{
"name": "create_flow",
"arguments": {
"campaign_id": 42,
"type": "default",
"schema": {
"landings": [{ "id": 17, "weight": 100 }],
"offers": [{ "id": 12, "weight": 100 }]
}
}
}
Trackily valide tout en cascade :
campaign_iddoit exister (non soft-deleted).- Chaque
landing.iddoit exister danslanding_pages(check batch — pas de N+1). - Chaque
offer.iddoit exister dansoffers. weightdoit être un nombre fini ≥ 0.type∈ {forced,regular,default}.rotation_mode∈ {independent,paired}.
Si une validation pète, le flow N'EST PAS créé — tu reçois une erreur avec la liste des IDs manquants.
Rotation pondérée — la maths
Trackily fait une roue de loterie pondérée :
landings: [
{ id: 31, weight: 25 },
{ id: 32, weight: 25 },
{ id: 33, weight: 50 }
]
Pour 100 clics, en moyenne tu obtiens :
- Landing 31 : ~25 clics (25 / (25+25+50) = 25 %)
- Landing 32 : ~25 clics
- Landing 33 : ~50 clics
Les poids sont relatifs, pas en pourcentages absolus. {w:1, w:1, w:2} donne le même split que {w:25, w:25, w:50} ou {w:100, w:100, w:200}.
Les offres dans schema.offers[] suivent EXACTEMENT la même logique, indépendamment de la landing choisie.
Rotation mode : independent vs paired
independent (défaut)
La landing et l'offre sont tirées séparément. Tu peux avoir 4 landings × 2 offres = 8 combinaisons possibles. Utile quand n'importe quelle landing peut router vers n'importe quelle offre.
paired
Les paires sont définies explicitement dans schema.pairs[] :
{
"schema": {
"landings": [],
"offers": [],
"pairs": [
{ "landing_id": 31, "offer_id": 12, "weight": 50 },
{ "landing_id": 32, "offer_id": 13, "weight": 50 }
],
"rotation_mode": "paired"
}
}
Utile quand chaque landing est COPYWRITÉE pour une offre précise (ex : landing "Diet Keto FR" → offre Keto FR, jamais croisée avec l'offre Crypto).
Visitor binding (sticky rotation)
Sans binding, chaque clic d'un même visiteur peut tirer une landing différente. Tu casses ton A/B test (le visiteur voit la variante A au clic 1, B au clic 2 — la mesure est polluée).
Trois modes :
binding |
Comment Trackily reconnaît le visiteur | Quand l'utiliser |
|---|---|---|
none |
Pas de binding — chaque clic rejoue la roue | Pas d'A/B test, juste rotation pure |
cookie |
Cookie HTTP posé sur le visiteur, expire à binding_hours |
A/B test honnête, traffic desktop / web stable |
ip+ua |
Hash de IP + User-Agent, expire à binding_hours |
Push mobile (pas de cookies persistants fiables) |
binding_hours par défaut : 24h. Pour les funnels longs (multi-step, email follow-up), monte à 168 (7 jours).
Quand utiliser quel type
default seul (cas 80 %)
Une seule règle de routage, tout le trafic est traité pareil.
{
"name": "create_flow",
"arguments": {
"campaign_id": 42,
"type": "default",
"schema": { "landings": [{ "id": 17, "weight": 100 }], "offers": [{ "id": 12, "weight": 100 }] }
}
}
forced geo-targeting
{
"name": "create_flow",
"arguments": {
"campaign_id": 42,
"type": "forced",
"name": "US route",
"filters": [{ "key": "country", "op": "in", "value": ["US"] }],
"schema": { "landings": [{ "id": 50, "weight": 100 }], "offers": [{ "id": 12, "weight": 100 }] },
"position": 0
}
}
{
"name": "create_flow",
"arguments": {
"campaign_id": 42,
"type": "forced",
"name": "EU route",
"filters": [{ "key": "country", "op": "in", "value": ["FR", "DE", "ES", "IT", "BE", "NL"] }],
"schema": { "landings": [{ "id": 51, "weight": 100 }], "offers": [{ "id": 13, "weight": 100 }] },
"position": 1
}
}
Puis un default qui catch tout le reste :
{
"name": "create_flow",
"arguments": {
"campaign_id": 42,
"type": "default",
"schema": { "landings": [{ "id": 52, "weight": 100 }], "offers": [{ "id": 14, "weight": 100 }] }
}
}
L'ordre d'évaluation : forced[position=0] → forced[position=1] → default. Le premier qui match les filtres gagne.
A/B test 50/50
{
"name": "create_flow",
"arguments": {
"campaign_id": 42,
"type": "default",
"binding": "cookie",
"binding_hours": 168,
"schema": {
"landings": [
{ "id": 17, "weight": 50 },
{ "id": 18, "weight": 50 }
],
"offers": [{ "id": 12, "weight": 100 }]
}
}
}
Le binding: cookie + binding_hours: 168 garantit qu'un visiteur revoit TOUJOURS la même variante pendant 7 jours — indispensable pour ne pas polluer la mesure. Voir A/B testing pour le workflow complet.
Test 4 variantes de prix (MemoMind, campagne #8)
C'est l'exact setup live qu'on a sur trackily.online pour tester les 4 prix MemoMind :
{
"name": "create_flow",
"arguments": {
"campaign_id": 8,
"type": "default",
"binding": "cookie",
"binding_hours": 168,
"schema": {
"landings": [
{ "id": 31, "weight": 25 },
{ "id": 32, "weight": 25 },
{ "id": 33, "weight": 25 },
{ "id": 34, "weight": 25 }
],
"offers": []
}
}
}
Chaque landing (31-34) override le prix via price_overrides (cf. ab-price-testing). Le flow ne route pas vers une offre — c'est le checkout natif qui gère la suite.
Erreurs courantes
- Pas de
default: tu fais 3forcedciblés, et tout le reste du trafic tombe en 404. Ajoute TOUJOURS undefault, même avec une landing fallback minimaliste. - Filtres invalides :
filtersaccepte un tableau ou un objet, mais les clés (key,op,value) doivent être correctes. Une faute de frappe fait passer le filtre — donc le flow matche n'importe quoi. - Poids à 0 : un poids 0 = jamais tiré. Si tu veux désactiver une landing temporairement, mets
weight: 0; pour la réactiver, repasse à100. - Landing supprimée référencée : si tu soft-delete une landing présente dans un flow, le flow garde l'ID mais
/c/<slug>plante. Toujours retirer la landing du flow AVANT de la supprimer. binding: nonesur A/B : ton A/B test devient aléatoire — chaque revisite est un nouveau coup de dé. Toujours utilisercookieouip+uasur de l'A/B.- Trop de
forcedqui se chevauchent : seul le premier dans l'ordrepositionest appliqué. Vérifie tes positions si tu vois un flow ignoré.
Voir aussi
- Créer une campagne — prérequis
- A/B testing — workflow complet de test
- Landings — types — types de landings utilisables dans
schema.landings - Cloaking — filtre AVANT le flow engine