Implemented in tracker/src/classify.ts, exercised by tracker/src/classify.test.ts. The classifier is a pure function of the parsed signals (tracker/src/signals.ts) and the page host. It produces three fields that can be mapped into hidden inputs: source, medium, channel.
Precedence (first match wins)
-
Explicit UTM parameters. If
utm_sourceorutm_mediumis present they are used verbatim (trimmed, lowercased, ≤ 100 chars). A missingutm_mediumis inferred from a click ID (cpc), else from an external referrer (organic,socialorreferral; this is how a ChatGPT link, which carriesutm_source=chatgpt.com, becomes a referral), otherwiseunknown. A missingutm_sourceis inferred from a click ID or the referrer, otherwise(direct). Channel comes from the medium:medium channel cpc,ppc,paidsearch,paid_search,sem,paidpaid_search(orpaid_socialwhen the source is a known social network)paid_social,paidsocial,paid-social,social_paidpaid_socialsocial,social-network,social-media,smorganic_socialemail,e-mail,newsletteremaildisplay,banner,cpm,cpv,videodisplayaffiliate,affiliates,partneraffiliateorganic,seoorganic_search(ororganic_socialfor a social source)referral,referer,referrer,linkreferralanything else other -
Click identifiers.
gclid/gbraid/wbraid→google/cpc/paid_search;msclkid→bing/cpc/paid_search;ttclid→tiktok/paid_social/paid_social;li_fat_id→linkedin/paid_social/paid_social;twclid→twitter/paid_social/paid_social(TikTok, LinkedIn and X only append these ids to ad clicks);fbclid→facebook/social/organic_social(Facebook attachesfbclidto every outbound click, so it is never treated as paid on its own). When several are present the order above wins: Google, then Microsoft, then TikTok, LinkedIn and X, thenfbclid. -
Referrer. Known search engines (
google,bing,yahoo,duckduckgo,ecosia,baidu,yandex,ask,aol,startpage,brave,qwant,seznam,naver;mail.*,gemini.*andbard.*hosts excluded, so Gemini is a referral rather than Google search) →<engine>/organic/organic_search. Known social networks (facebook incl.l./m.hosts, instagram, twitter/t.co,x.com, linkedin/lnkd.in, pinterest, tiktok, youtube/youtu.be, reddit, threads, snapchat) →<network>/social/organic_social. Any other host →<host>/referral/referral. Matching uses whole hostname labels, sonotgoogle.exampleis a referral. -
Direct. No referrer, a non-http(s) referrer, or a self-referral (same registrable domain, approximated as the last two labels, or three when the second-level label is ≤ 3 characters such as
co.uk) →(direct)/(none)/direct.
Notes
- Conflicts resolve by precedence:
utm_medium=emailwith agclidisemail. - The classifier never reads the URL or DOM itself; it only consumes
RawSignals. - First-touch retention (ADR-002) is handled separately: a later
directvisit never overwrites an earlier classification.