Marketing

Tracking Referrals End to End: Links, Cookies and Attribution

Antonio Evans11 min read

A clean technical diagram on a light background: on the left.
Key points

Key points

  1. 1Every referral link should carry a code that maps to exactly one referrer ID, plus UTM parameters so analytics can separate referral traffic from other channels.
  2. 2Store the code in a first-party cookie and also carry it as a URL parameter into signup, then fire a server-side event when signup completes so attribution does not depend on a third-party cookie.
  3. 3When two referral codes appear on one signup, apply a fixed written rule (last code entered wins or first code seen wins), log both codes with timestamps, and never leave the choice to whoever reads the report.
  4. 4Reconcile payouts by joining the payout table to the signup table on signup ID and confirming that every paid signup has exactly one winning code and that no winning code goes unpaid.

A referral program rests on one design choice: the referrer's identity must be explicit in your own data, not inferred from a browser. The cleanest way is to give every advocate a unique code or ID inside their referral URL, capture that identifier when the friend arrives, and carry it through to signup or purchase. This is the simplest and most durable method, and it works even when cookies are blocked the Extole cookieless tracking guide. Salesforce describes the same mechanic from the program side: referral software uses the referring customer's unique identifier to track who is referring, who is accepting the referral, and which incentive applies the Salesforce referral guide.

Build the link in two layers. The first layer is your own code, for example ?ref=AB12CD, where AB12CD is a short opaque token that maps to one row in a referrers table. Never put an email address or a sequential user ID in the URL; a sequential ID lets anyone enumerate your customer base, and an email address leaks personal data into browser history and referrer headers.

The second layer is standard UTM parameters so analytics tooling sees the click as a campaign. Google's documentation lists the parameters (utm_source, utm_medium, utm_campaign, utm_id, utm_content and others) and recommends always using source, medium and campaign the Google Analytics URL builder documentation. UTM parameters are unaffected by third-party cookie changes and are typically well integrated into analytics platforms the AdRoll UTM guide. A referral link for a customer named in your database as referrer AB12CD, shared by email, looks like this:

https://www.example.com/join?ref=AB12CD
  &utm_source=referral&utm_medium=referral_link
  &utm_campaign=refer_a_friend&utm_content=email_share

Two rules keep this clean. First, keep values lowercase and separated by dashes, and enforce one documented naming convention across the team; inconsistent source naming splinters traffic data and makes analysis hard the utm.io best practices checklist. Second, never tag internal links with UTMs; those parameters are for incoming traffic from external sources the AdRoll UTM guide. A referral link that a logged-in user clicks inside your own app should pass the code, not a UTM string, or your campaign reports fill with self-referrals.

One practical warning about codes: make them long enough to be unguessable and short enough to survive a screenshot. Six to eight characters from a mixed alphabet is workable. A friend who types a code by hand is a real case, so validate on input, normalize case, and reject any code that does not resolve to an active referrer.

Cookies are no longer something you can count on for referral attribution. Safari's tracking prevention blocks all third-party cookies by default and caps JavaScript-set first-party cookies at 7 days, or 24 hours for link-decorated traffic; Firefox partitions cookies per site by default; and Google confirmed it would maintain third-party cookie choice in Chrome rather than roll out a new standalone prompt the Extole cookieless tracking guide. Privacy law adds a second constraint: GDPR in Europe and CCPA in California require consent before setting many types of cookies, and EU regulators jointly issued over 1.2 billion euros in GDPR fines in 2024, subject to appeal the Extole cookieless tracking guide.

The resilient pattern uses three layers, and you should build all three rather than picking one:

LayerWhat it doesWhat it survivesWhat it does not survive
URL parameter (?ref=CODE)Carries the code from click to landing pageEverything, including blocked cookiesUser navigating away and returning later
First-party cookie on your domainRemembers the code across pages and sessionsNormal browsing sessionsSafari caps (7 days, or 24 hours for link-decorated traffic) and cookie refusal
Server-side event on signupRecords the referral on your own servers via an API callBrowser limits, ad blockers, cookie expiryNothing, if consent and identifiers are handled correctly

The URL parameter is the source of truth at the moment of arrival. On the landing page, read it, validate it against your referrers table, write it into a first-party cookie, and immediately attach it to the session or server-side identifier you will use at signup. The cookie exists only to bridge page views where the parameter is no longer in the address bar.

The server-side event is what closes the loop. Server-side attribution means recording the referral event on your own servers rather than in the visitor's browser, sent from your systems to your platform via an API, so the data survives browser limits and ad blockers the Extole cookieless tracking guide. Fire it from your signup handler, not from the client, so a blocked script cannot erase a legitimate referral. A minimal payload looks like this:

{
  "event": "signup_completed",
  "signup_id": "s_88213",
  "account_id": "acct_4410",
  "ref_code": "AB12CD",
  "ref_source": "cookie",
  "clicked_at": "2025-03-04T10:12:44Z",
  "signed_up_at": "2025-03-04T10:19:02Z",
  "consent_referral": true
}

A worked example of why all three layers matter. Suppose 1,000 people click referral links in a month, and 60 percent of clicks are in Safari or Firefox. If you rely on third-party cookies only, a meaningful share of the 24-hour link-decorated cap cases will fail. If you pass the parameter to signup and fire server-side, the only loss is people who click a link, leave, and return more than 7 days later without the URL. You can measure that gap directly: compare signups where ref_source is url against those where it is cookie and those where it is none. The none count is your floor of unattributed referrals, and it should be small and stable.

If you are building this plumbing yourself rather than patching a third-party platform, Quanta can take a written spec for the tracking service and build it with you, including the cookie handling and the server-side signup event.

Settling credit when a signup carries two referral codes

Two codes on one signup is normal, not an edge case. A person clicks a friend's link on Monday, ignores it, clicks a different friend's link from a group chat on Wednesday, and signs up on Thursday. Both codes exist in your logs. You need a written rule, applied by code, not by judgment.

The three defensible rules are:

  • Last code entered wins. The most recent code before signup gets credit. This matches the intuition that the last nudge caused the conversion, and it is simple to explain to customers. It can reward a referrer who did very little.
  • First code seen wins. The earliest code in the window gets credit. This protects the original advocate and discourages code sniping, where someone pushes their code at people who are already about to sign up.
  • First touch in a rolling window, tie broken by last touch. The code seen first within, say, 30 days wins, unless it has expired, in which case the most recent valid code wins. This is more work but avoids the "I referred them six months ago and never spoke to them again" complaint.

Whichever you pick, write it into the program terms. Referral Factory's guidance on program best practices emphasizes clear rules as a foundation for programs that scale the Referral Factory best practices guide. Whatever you choose, the tie-break must be deterministic and reproducible from stored data alone.

Here is a decision rule worth adopting: choose last code entered wins when your reward is small and your sharing is frequent (consumer apps, ecommerce), because speed and simplicity matter more than perfect fairness. Choose first code seen wins when rewards are large or your advocates are B2B partners who invest real effort in an introduction, because the effort, not the last click, is the contribution.

The data model that makes any rule auditable:

create table referral_touches (
  id            bigserial primary key,
  visitor_id    text not null,
  ref_code      text not null,
  seen_at       timestamptz not null,
  source        text not null  -- 'url' | 'cookie' | 'manual'
);

create table signup_attribution (
  signup_id     text primary key,
  winner_code   text,          -- null if unattributed
  rule_applied  text not null, -- e.g. 'last_touch_30d'
  decided_at    timestamptz not null
);

Every touch is written as an append-only row with a timestamp. The winning code is computed once at signup and stored with the rule name, so six months later you can explain any payout without re-litigating history. When a support ticket arrives, you show the customer their own touch list with times, which ends most disputes quickly. Note also that self-referral and fraud are the real margin risk in two-sided programs: without strict rules for what counts as a valid conversion, who qualifies as "new," and how many rewards one advocate can earn, programs attract self-referrals and leakage the Voucherify guide to double-sided referrals. A duplicate-code case is usually an accident; a pattern of duplicates from one device is a signal.

Reconciling payouts against signup records

Payout reports drift from signup records for four reasons: the report counts rewards, the signup table counts people; reversals (refunds, chargebacks, cancellations) land in one but not the other; a retry in the payout job pays twice; a manual adjustment gets forgotten. Reconciliation is therefore a join, not a vibe check.

The check runs in three passes.

First, one winning code per paid signup. Join the payout table to the signup table on signup ID and confirm each paid signup has exactly one row in signup_attribution and that its winner_code matches the code in the payout. Any signup with a payout but no attribution row is a bug in the event pipeline. Any signup with a payout and two attribution rows means your signup handler is not idempotent.

Second, no winning code goes unpaid. The reverse join finds attributed signups with no payout row after your qualified period elapses. Referral economics depend on paying only for conversions you define, so a missing payout is often a qualification rule working as intended, but you need to see the list to tell the difference the Salesforce referral guide.

Third, no payout without a qualifying event. This catches manual payouts and reversals that were never applied. A reconciliation query looks like this:

-- one winning code per paid signup
select p.signup_id, count(*) as payout_rows
from payouts p
join signup_attribution a on a.signup_id = p.signup_id
group by p.signup_id
having count(*) <> 1;

-- paid signups with no attribution
select p.signup_id
from payouts p
left join signup_attribution a on a.signup_id = p.signup_id
where a.signup_id is null;

Run both queries before every payout cycle and keep the outputs for a year. The count of rows in each result is a run metric: a jump means something in the pipeline changed. This matters because referral value is real and measurable. In a study tracking roughly 10,000 customers of a German bank for almost three years, referred customers had a higher contribution margin and a higher retention rate, and the average referred customer was at least 16 percent more valuable than a similar nonreferred customer the Wharton referral program study. If you are paying for referrals, you want the paid set to match the set you are measuring.

A simple worked example. A month closes with 4,000 rewards paid and 3,960 attributed signups. The 40-row gap splits into 25 refund reversals that were queued but not applied and 15 retries that paid twice. Fix the reversal job, refund the 15 duplicate payouts, and add a unique index on payouts(signup_id) so a retry fails loudly instead of quietly. From then on, the first query should return zero rows on every run.

Choosing rewards without wrecking the margin, and what to reconcile against

Reward design and attribution design are coupled: the more you pay per referral, the more correctness matters. Double-sided programs reward both the referrer and the friend. Referral Factory reports that over 90 percent of the programs run on its platform are double-sided, and argues the reciprocity makes sharing feel like a gift rather than a pitch the Referral Factory double-sided guide. A double-sided reward can be identical for both parties or different, for example a gift card for the referrer and a discount for the friend the Referral Rock rewards guide.

Before you set a number, size the reward against what a referred customer is worth to you. Customer lifetime value is conventionally computed as average order value times purchase frequency times customer lifespan minus acquisition cost; worked example from one guide: spending 50 to acquire a customer who spends 100 per purchase, three times a year, for five years yields a lifetime value of 1,450 the Mention Me lifetime value guide. Note the arithmetic is stated in the guide's own example, so redo it with your own numbers rather than borrowing theirs.

A decision rule for the reward ceiling: set the total two-sided reward at no more than half of the first-year contribution margin of a referred customer, and set the qualification event where fraud is cheapest to detect. If you reward on signup, self-referral is trivial to attempt; if you reward on first payment, the reward is smaller because fewer signups qualify, but every payout maps to real revenue. The Voucherify analysis is blunt on this: double-sided referrals are not twice as expensive unless you design them that way, and the real risk is weak validation and missing guardrails the Voucherify guide to double-sided referrals.

Whatever you choose, the qualification event must be recorded in the same server-side pipeline that fires the signup event, so the payout table and the signup table speak the same language. If your qualification is "first payment within 60 days," store that timestamp on the signup record, not only in the payments system.

If your next step is deciding which referral value metric to optimize and which experts should own the payout policy, Quanta Experts can take your context, work it and hand back a finished deliverable such as a reward-policy memo or a reconciliation runbook. And if you have coding agents building or maintaining the tracking service, Quanta Intelligence skills for coding agents keep them working from the same definitions of winning code, qualification and payout.

A pre-launch checklist for the plumbing

Before you turn the program on, verify each of these items. Each one maps to a failure seen in practice.

CheckPass conditionWhat it prevents
Link builderEvery link resolves to exactly one active referrer rowBroken or shared codes
UTM conventionLowercase, dashed, one value per field, documentedFragmented campaign reports
CookieFirst-party, on your domain, set with consentLoss to Safari and Firefox limits
Signup handlerReads URL parameter first, cookie only as fallbackAttribution lost on blocked cookies
Server eventFired from the backend, idempotent on signup IDDouble counting and missed conversions
Tie-break ruleWritten in terms and applied in code, with rule name storedDisputes you cannot settle
ReversalsRefunds and cancellations update the payout tableOverpayment drift
Reconciliation queriesBoth run clean before every cycleSilent payout errors

Two final habits pay off. Keep the touch log append-only: never update a row, only insert, because the whole point is to be able to replay the decision. And measure your unattributed share monthly. Referral Factory's core thesis that referrals are exceptionally cost-effective applies only when you can actually see which referrals happened the Referral Factory best practices guide. An unattributed share that creeps up means one of the three layers has quietly stopped working, and the reconciliation queries will not catch it, because they only see signups you already attributed.

FAQ

FAQ

What is the difference between a referral code and a UTM parameter?

A referral code identifies the specific person who referred someone, and it is yours to define and resolve against your own database. A UTM parameter identifies the campaign and channel for analytics reporting; Google recommends always using source, medium and campaign the Google Analytics documentation. You need both: the code settles who gets paid, the UTMs tell you which sharing channel produced the click.

Do I still need cookies at all if I pass the code as a URL parameter?

Yes, as a bridge, not as the source of truth. The URL parameter disappears as soon as the visitor moves to another page, so a first-party cookie keeps the code alive across the session. But do not rely on it for long windows: Safari caps JavaScript-set first-party cookies at 7 days, or 24 hours for link-decorated traffic the Extole cookieless tracking guide. The server-side event at signup is what makes attribution durable.

What should I do when two referral codes appear on one signup?

Apply a fixed rule stored in your code and written in your program terms, such as last code entered wins or first code seen wins, and record both codes with timestamps plus the name of the rule applied. This lets you explain any payout later from stored data alone. Also watch for patterns rather than single cases: weak validation is the real fraud risk in two-sided programs the Voucherify guide to double-sided referrals.

How often should I reconcile payout reports against signup records?

Before every payout cycle, at minimum, and store the query outputs. Run the two joins that check for duplicate payouts and for paid signups with no attribution, and treat the row counts as run metrics that should stay at zero and stable. Reversals from refunds and cancellations are the most common source of drift, so apply them before the cycle closes rather than after.

Should referrals be rewarded at signup or at first payment?

Reward at signup only if your reward is small and fraud is cheap to unwind, because self-referral is trivial at that point. Rewarding at first payment maps every payout to revenue and is easier to defend at reconciliation, though fewer signups qualify. In either case, record the qualification timestamp on the signup record so the payout table and signup table can be joined and checked.

Sources

Sources

  1. How to Track Referrals Without Cookies: First-Party and ...
  2. How to Build an Effective Referral Program - Salesforce
  3. URL builders: Collect campaign data with custom URLs
  4. UTM Best Practices: How to Track Every Click and ...
  5. 21 UTM Parameters Best Practices: Checklist (2026)
  6. Referral Program Best Practices: The Holy Grail of Referral Marketing
  7. Double-sided referrals: worth the hassle or a margin trap? - Voucherify
  8. PDF Referral Programs and Customer Value - Wharton Faculty Platform
  9. What Is A Double Sided Referral Program? With Examples
  10. How to Choose the Best Referral Rewards 24+ Examples
  11. How to Increase Customer Lifetime Value CLTV with Referral Marketing
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles