ADR 0018 — Founders Referral: cookie-primary attribution with URL-param fallback
Status: Proposed
Date: 2026-04-23
Deciders: software-architect
Related: docs/architecture/founders-referral-service.md §5
Parent card: #207
Context
When a visitor follows a Founder's referral link (/r/{slug}), Raptor must attribute that visit to the correct Founder's link so that if the visitor later signs up and converts to a paid subscription, the correct Founder receives the bonus. The attribution window is 30 days.
The core tension: cookies are the simplest server-side attribution mechanism, but they have GDPR constraints in the EU (functional cookies require disclosure; tracking cookies require consent under ePrivacy Directive) and are ephemeral under browser ICP (Intelligent Tracking Prevention), private-browsing mode, and aggressive cookie-blocking settings.
Four approaches were evaluated:
- Cookie only
- URL parameter only (pass-through to signup form)
- Cookie primary, URL parameter fallback
- Server-side session / IP-based fingerprinting
Decision
Cookie primary (raxx_ref={slug}), URL parameter fallback (?ref={slug}), with a GDPR consent gate for EU visitors on the cookie drop.
Cookie spec:
- Name: raxx_ref
- Value: slug (8 chars, no PII)
- Max-Age: 2592000 (30 days)
- SameSite=Lax
- Secure
- HttpOnly
GDPR gate: For visitors where the consent management platform (CMP) reports the user is in the EU and has not consented to functional cookies: do NOT set the cookie. Instead, include ?ref={slug} in the redirect URL. The signup flow reads ?ref as a form parameter if the cookie is absent.
Attribution read order at signup:
1. Check raxx_ref cookie. If present, use slug from cookie.
2. Else check ref query parameter (passed through the signup URL or hidden form field).
3. Else: no attribution; user is direct_signup cohort.
Consequences
Positive
- Cookie is simple and reliable for users who follow the link and sign up in the same browser within 30 days. This covers the majority of the expected referral flow.
- URL fallback recovers private-browsing cases where the user copies the link, opens a new window, or clears cookies between click and signup.
- No PII in the cookie value. The slug is a random token; it does not identify the referrer. Even if the cookie is inspected, it reveals nothing about the Founder.
- GDPR posture is correct. The cookie is functional (referral attribution), not advertising or tracking. Functional cookies under the ePrivacy Directive are permitted without consent in most EU member-state interpretations. However, because this is a new cookie and the consent surface is not yet fully specified, the conservative posture (consent gate for EU) is chosen to avoid regulatory risk.
- 30-day window is industry standard for referral attribution (Shopify, Stripe, Paddle all use 30-day windows).
Negative
- Cookie is lost on browser clear, private-browsing exit, or device switch. For these cases, attribution falls back to the URL param — which itself is lost if the user does not bookmark the link.
SameSite=Laxdoes not protect cross-site POST. This is intentional:raxx_refis a purely informational cookie read by GET requests. It carries no authentication or session information.- CMP dependency. The GDPR gate requires the CMP to report EU/non-EU status and consent state. If the CMP is not yet integrated, the conservative default (no cookie for all users, URL-only) must be used until CMP is available. This is an open question in the referral service design doc §13 open question #2.
Alternatives considered
Cookie only (no URL fallback)
Simple. Rejected because it silently drops attribution for private-browsing and strict-cookie users with no recovery path.
URL parameter only (no cookie)
Every redirect would carry ?ref={slug} through the entire signup flow as a form field or URL parameter. This is more fragile: if the user navigates away from the signup page and returns via bookmark (without the param), attribution is lost. Cookies are more durable for multi-step signup flows. Rejected as primary; retained as fallback.
Server-side fingerprinting (IP + User-Agent hashing)
Associates the click with an IP+UA hash, then matches at signup time. Rejected for v1 because: - IP hashing is PII under GDPR (recital 49, recital 26 — identifiable via ISP). Adds a new PII surface requiring explicit consent and retention policy. - Carrier-grade NAT means multiple users can share an IP; false attributions are possible. - The complexity is disproportionate to the v1 cohort size. - Flagged as a v2 enhancement if attribution miss rate proves significant from PostHog data (#214).
localStorage / sessionStorage
JavaScript-accessible, not HttpOnly. Rejected because it requires client-side JS to execute before attribution is recorded, which is not possible server-side on the redirect. Also not accessible to the server without an additional API call, complicating the attribution read at signup.
Revisit when
- The CMP integration ships and the GDPR gate can be properly evaluated. At that point, confirm whether the cookie qualifies as functional-without-consent under the applicable EU member-state implementation.
- PostHog data (#214) shows attribution miss rate exceeds an acceptable threshold (to be defined by product-manager). If missed attribution is significant, evaluate server-side fingerprinting as a supplement.