Trackily Docs

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).

Liste des flows

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 :

  1. 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).
  2. regular — règles intermédiaires avec filtres souples (rare en pratique).
  3. default — le filet : matche TOUS les clics qu'aucun forced/regular n'a capté. Une campagne doit toujours avoir un default pour 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_id doit exister (non soft-deleted).
  • Chaque landing.id doit exister dans landing_pages (check batch — pas de N+1).
  • Chaque offer.id doit exister dans offers.
  • weight doit ê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 3 forced ciblés, et tout le reste du trafic tombe en 404. Ajoute TOUJOURS un default, même avec une landing fallback minimaliste.
  • Filtres invalides : filters accepte 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: none sur A/B : ton A/B test devient aléatoire — chaque revisite est un nouveau coup de dé. Toujours utiliser cookie ou ip+ua sur de l'A/B.
  • Trop de forced qui se chevauchent : seul le premier dans l'ordre position est appliqué. Vérifie tes positions si tu vois un flow ignoré.

Voir aussi