source of truth for media planner

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:

StepEvent nameCategoryvalue parameterTriggered by
salesLeadreportingn/adiagnostic tool submit
corePurchaseoptimizationcore_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)
bumpPurchaseoptimizationbump_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
oto1Purchaseoptimizationoto1_price = 47 (or whatever the live test sets)OTO1 one-click upsell success
oto2Purchaseoptimizationoto2_price = 497OTO2 upsell success — this is the lead-qualifier event; revenue from OTO2 buyers drives the ascension math
ty_corePurchaseConfirmedreportingn/athank-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_caseCallBookedreportingn/athank-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}
FieldRule
utm_sourceLowercase platform id. meta, google, youtube, email, affiliate — set by the platform's auto-tagging, not by hand.
utm_mediumLowercase medium bucket. paid_social, paid_search, paid_video, email, affiliate.
utm_campaignLowercase campaign key from registry/campaigns.json — this must match exactly so the planner can correlate ad spend to planner scenarios.
utm_contentThe 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_termLowercase 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:

  1. Land on core checkout URL with the platform's click ID appended as a query string (fbclid, gclid, yclid, msclkid, epik, etc. — the field is per-platform, stored in tracking.click_id_param).
  2. After successful payment the processor redirects to ty_core and includes the click ID in the redirect URL. Verify this is wired in production — most processors strip query strings by default.
  3. From ty_core, fire PurchaseConfirmed with fbclid (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.
  4. Each OTO page reads document.cookie and 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.
  5. On ty_case (the call-booking thank-you), write the click ID to the CRM record as external_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:

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:

  1. 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.
  2. Confirmed — webhook received, order line item visible in the warehouse. This is what the media planner assumes.
  3. Refunded — buyer refunded within the window. Subtract from gross in the day's net. Net contribution and ROAS numbers drop. Auto-decrement the refund_rate in the media planner back to reality.
  4. 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:

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

Heads-up The media planner is calibrated against the events, parameters, and windows described above. When you change any of them, the planner is wrong until its assumptions are re-baselined.