Tracking & measurement
This page is the operational source of truth. The numbers the media planner uses as benchmarks — front-end ROAS, blended CPA, retention rates — must reconcile back to the events and windows described here. If you change a value on this page, the planner should be re-baselined before it goes to a buyer.
Conversion events, by step
Each funnel step fires a single conversion event. Two categories:
- Optimization events feed the platform's bidding (Meta's "Purchase" optimization, Google's "Conversion" action). High-intent, value-bearing events only.
- Reporting events exist for internal measurement. They never go into a campaign's optimization goal — putting them there trains the algorithm on the wrong signal.
| Step | Event name | Category | value parameter | Triggered by |
|---|---|---|---|---|
| sales | Lead | reporting | n/a | diagnostic tool submit |
| core | Purchase | optimization | core_price — the variant that fired; usually 27 or 27+17 when the bump was taken. |
checkout success for the core offer (page reload or postMessage from processor) |
| bump | Purchase | optimization | bump_price only when the bump is taken. Do not fire Purchase again on bump — pass value: 17 as an additional parameter to the existing core event, or fire a separate BumpAdd event for the bump with no optimization weight. |
bump checkbox ticked + checkout success |
| oto1 | Purchase | optimization | oto1_price = 47 (or whatever the live test sets) | OTO1 one-click upsell success |
| oto2 | Purchase | optimization | oto2_price = 497 | OTO2 upsell success — this is the lead-qualifier event; revenue from OTO2 buyers drives the ascension math |
| ty_core | PurchaseConfirmed | reporting | n/a | thank-you page load. The unique difference from Purchase: this fires after the order is logged server-side, so it's the version that gets used for retention / LTV math, not bid optimization. |
| ty_case | CallBooked | reporting | n/a | thank-you page load for OTO2 buyers when the booking widget confirms a slot |
UTM parameter template
The template stored in registry/campaigns.json per campaign:
utm_source={source}&utm_medium={medium}&utm_campaign={campaign}&utm_content={adset}&utm_term={creative}
| Field | Rule |
|---|---|
utm_source | Lowercase platform id. meta, google, youtube, email, affiliate — set by the platform's auto-tagging, not by hand. |
utm_medium | Lowercase medium bucket. paid_social, paid_search, paid_video, email, affiliate. |
utm_campaign | Lowercase campaign key from registry/campaigns.json — this must match exactly so the planner can correlate ad spend to planner scenarios. |
utm_content | The ad-set name, lowercased for the URL. It's written upper-case inside the ad platform for human readability there — e.g. PROSPECTING-LAL1 in Meta Ads Manager — but this field always lowercases it to prospecting-lal1 so the param stays consistent with every other UTM field. See /docs/naming/ for the ad-set name pattern. |
utm_term | Lowercase creative id. Typically the filename sans extension, e.g. hook-v1-wagleak. See /docs/naming/. |
Click ID persistence across the OTO chain
The OTO pages are separate URLs (the buyer is taken off the processor's hosted page onto our own upsell page, then back to the processor). State must survive the round trip. The mechanism:
- Land on
corecheckout URL with the platform's click ID appended as a query string (fbclid,gclid,yclid,msclkid,epik, etc. — the field is per-platform, stored intracking.click_id_param). - After successful payment the processor redirects to
ty_coreand includes the click ID in the redirect URL. Verify this is wired in production — most processors strip query strings by default. - From
ty_core, firePurchaseConfirmedwithfbclid(or whichever) attached as both an event parameter and a first-party cookie. The cookie is read by OTO1 / OTO2 pages served via separate URLs. - Each OTO page reads
document.cookieand merges the click ID into both the upsell link (so it survives the next redirect) and the pixel fire. The OTO conversion events must include the click ID as an event parameter — this is the single most-broken integration we see; audit every campaign's OTO pages in the launch checklist. - On
ty_case(the call-booking thank-you), write the click ID to the CRM record asexternal_id. This makes attribution + re-engagement campaigns work later.
Attribution window vs. platform default
The platform defaults (Meta 7-day click, 1-day view; Google Ads default 30-day click; etc.) optimize for volume. We hold tighter windows for reporting, stored in tracking.attribution_window_days per campaign:
- The default is 7 days for low-ticket offers with short consideration cycles (the OTO2 → call funnel needs urgency).
- Stretch to 14 or 30 days only when running a higher-considered / higher-ticket offer where the buyer needs time between ad-click and purchase.
- The window is enforced in the warehouse SQL, not on the platform — we still let the platform optimize on its default and just re-attribute for our own CPA math.
Confirmed vs. pending sale, refunds, chargebacks
The pixel fires on the first signal from the processor (Purchase). Three days later the order may have been refunded. The four states the planner assumes:
- Pending — processor says paid but our warehouse hasn't received the webhook yet (typically < 60s, occasionally hours). Treat as revenue for the platform's optimization; treat as zero for our own CPA math until confirmed.
- Confirmed — webhook received, order line item visible in the warehouse. This is what the media planner assumes.
- Refunded — buyer refunded within the window. Subtract from gross in the day's net. Net contribution and ROAS numbers drop. Auto-decrement the
refund_ratein the media planner back to reality. - Chargeback — bank reversal. Different from refund: fees apply, the platform usually bills us, and the customer is barred from the upsell pages. Treat as a 100% refund + a configurable chargeback-fee % in the planner.
Both refund and chargeback events must reconcile backward against the original conversion event — when the planner reports a day's net contribution, that number is the difference of the day's confirmed events minus refunds, not the day's pixel-fires. The verifier doesn't catch refund lag; that's a nightly warehouse job.
The planner's numbers are downstream of this page
The default values for refund_rate, fee_rate, and the conversion rates in the campaign presets (Conservative / Base / Aggressive) are calibrated against the events and windows described above. If you change:
- which event fires at a step (e.g. swap
Purchasefor a customOTO2Lead) - the attribution window for a campaign
- how
valueis set on the optimization events
…then the planner's numbers will drift from reality. Update registry/campaigns.json's presets.* and the matching field defaults in /plan/ on the same day, and notify the campaign owner in the campaign notes.