A/B testing + conversion goals
Translator note: machine-translated baseline. Needs human review by a native UK speaker. EN source is canonical.
Per-campaign N variants з deterministic per-player assignment + conversion attribution проти будь-якої канонічної події.
Variants
Кожна campaign має 1..N rows у campaign_variants:
{
"name": "Variant A",
"label": "A", // короткий badge label
"template_id": "...", // оператор clones template, tweaks copy
"weight": 50, // relative weight 0..10000
"sort_order": 0
}
Weights не повинні sum to 100 — assignment normalises по total. Дві variants at weight=1 each = 50/50 split. Одна at 70, одна at 30 = 70/30.
Deterministic assignment
bucket = FNV1a32(`${campaign_id}|${player_id}`) mod sum_weights
variant = перший variant where cumulative_weight > bucket
Той самий гравець на тій самій campaign завжди потрапляє у той самий variant — навіть через re-sends. Запобігає variant-drift attribution mud.
Той самий гравець на різних campaigns може потрапляти у різні variants (campaign_id varies hash input).
Conversion goals
// На campaign:
{
"goal_event": "deposit_confirmed", // canonical event name
"goal_attribution_days": 7, // 1..90
"goal_value_property": "amount_base" // jsonpath для revenue attribution
}
GET /v1/campaigns/:id/conversion-stats joinить message_sends (PG) з raw_events (CH):
- Для кожного гравця, що отримав message в
(sent | delivered | opened | clicked)статусі, - Знайти найраніший
goal_eventу[sent_at, sent_at + attribution_days * 24h) - Group by
variant_id→ count + value sum
Earliest, тому що гравець може зробити goal кілька разів — credit ОДНУ conversion per гравець per campaign.
Stats response
{
"campaign_id": "...",
"goal_event": "deposit_confirmed",
"goal_attribution_days": 7,
"total_recipients": 3085,
"total_conversions": 151,
"variants": [
{
"variant_id": "...",
"variant_name": "Variant A",
"variant_label": "A",
"recipients": 1547,
"conversions": 87,
"conversion_rate": 0.0563,
"conversion_value": 870000 // sum у tenant base валюті у cents
},
{
"variant_id": "...",
"variant_name": "Variant B",
"variant_label": "B",
"recipients": 1538,
"conversions": 64,
"conversion_rate": 0.0416,
"conversion_value": 640000
}
],
"winner_variant_id": "...A's id"
}
Winner picker
Variant declared winner коли:
- Має найвищу conversion rate серед усіх variants
- Його
recipientscount is ≥ 30 (avoids declaring winners on noise) - Tie-break: найвищий recipient count (more confidence) → first by sort_order
Below 30 sample size, winner_variant_id is null until more sends accumulate.
Mid-campaign weight changes
Operators можуть edit weights mid-campaign — only нові sends respect новий split. Already-enqueued message_sends keep their assigned variant_id so conversion attribution stays clean.