Relevance score
Every session gets a relevance score between 0 and 100 that says how close it came to being a lead. It turns "1,200 sessions from campaign A" into "38 hot sessions from campaign A" – the number that matters for ad spend. Scores are computed on the server after every ingest with the project's relevance model, and the same function renders the "why this score" explanation, so the stored score and its explanation can never disagree.
The default model
| Signal | Points |
|---|---|
| Forced 100 | quiz submitted, meeting booked, form submitted, confirmed server-side conversion |
| Forced 0 (floor) | engaged < 3 s and none of: interactive click, video start, quiz answer, CTA, WhatsApp, calculator, quiz contact step |
| Engaged time | ≥ 10 s +5 · ≥ 30 s +10 · ≥ 60 s +15 · ≥ 180 s +20 (highest reached) |
| Scroll | ≥ 90 % +10 · ≥ 50 % +7 · else left the first screen +3 |
| Pages | 2 pages +5 · ≥ 3 pages +10 |
| Video | started +8 · watched ≥ 30 s +10 · ≥ 50 % covered +7 · completed +10 |
| Interactive clicks | +2 each, at most +8 |
| CTA click | +10 |
| Calculator used | +15 |
| Quiz | viewed +2 · first answer +25 · each further answer +5 (max +20) · contact step +15 |
| WhatsApp click | +40 |
Identified (identify() or a lead) | +30 in new projects (0 in the CPI preset) |
The sum is capped at 100. The CPI preset (relevance v1) reproduces the cpi-website2 model exactly –
a property test over 20 000 random sessions proves it.
Tiers and thresholds
| Tier | Score |
|---|---|
| cold | 0–9 |
| interested | 10–29 |
| engaged | 30–59 |
| hot | 60–89 |
| lead | 90–100 |
KPIs use two thresholds: engaged+ (≥ 30) and hot+ (≥ 60). Ad cost per relevant session
uses settings.ads.relevantMinScore (default 30).
Customizing the model
Edit it under Settings → Relevance, with PUT /v1/projects/{project}/relevance, or the MCP tool
relevance_update_config. A model is JSON (RelevanceConfig):
{
"schema": 1,
"forced": { "flags": ["quiz_submitted", "booked", "form_submit", "confirmed_conversion"], "events": ["trial_started"], "goals": [] },
"floor": { "engagedMsBelow": 3000, "interactions": ["int_clicks", "video", "cta_click"], "events": ["pricing_toggle"] },
"engaged": [[180, 20], [60, 15], [30, 10], [10, 5]],
"scroll": { "steps": [[90, 10], [50, 7]], "foldScrolled": 3 },
"pages": [[3, 10], [2, 5]],
"video": { "started": 8, "watched": { "seconds": 30, "points": 10 }, "coverage": { "pct": 50, "points": 7 }, "completed": 10 },
"intClicks": { "each": 2, "max": 8 },
"flags": { "cta_click": 10, "calculator": 15, "quiz_viewed": 2, "quiz_contact": 15, "whatsapp": 40, "identified": 30 },
"quizAnswers": { "first": 25, "furtherEach": 5, "furtherMax": 20 },
"events": [{ "event": "pricing_toggle", "points": 5, "each": 2, "max": 9, "label": "Compared plans" }],
"goals": [{ "goal": "pricing_page", "points": 10 }],
"tiers": [{ "key": "cold", "min": 0 }, { "key": "interested", "min": 10 }, { "key": "engaged", "min": 30 }, { "key": "hot", "min": 60 }, { "key": "lead", "min": 90 }],
"thresholds": { "engagedPlus": 30, "hotPlus": 60 }
}eventsrules:pointsfor the first occurrence,eachfor every further one, at mostmaxin total. Works with built-in and custom events.goalsrules add points when the session reached a goal (Goals and funnels).forced.events/forced.goalsforce 100 – use them for your real conversions.- Tiers: 2–8 tiers, the first starts at 0, minimums increase; labels optional (
{"en": …, "de": …}).
Preview before saving
POST /v1/projects/{project}/relevance/preview scores up to 10 000 recent sessions in memory with a
proposed model and returns the tier distribution and examples. Nothing is stored.
Versioning and rescoring
Every saved model is a new immutable version. Saving sets the project's current version and
starts retroactive rescoring: a background job rescores all sessions in chunks of 2 000, newest
data first, and updates the daily rollups. Progress is shown in the project status
(pendingSessions); until it reaches zero, sessions scored with an older version are flagged in the
dashboard. A conversion recorded later (for example a WhatsApp lead marked via MCP) rescores its
session immediately.
Why this score
The session view and the MCP tool sessions_get show the breakdown: each item with its points, the
cap note when the sum exceeded 100, and whether the score was forced (conversion or floor).