Skip to content

Data Protection Impact Assessment

Facilitator Referral Attribution System

Field Value
Document ID DPIA-2026-001
Processing activity Facilitator referral attribution via HMAC-signed cookie
Controller Curaway Global Health Technologies
Date 2026-05-05
Plan reference PR #651 Phase 2 Task 2.2
Related issue #629
Related ADR ADR-0018 (cross-tenant section)
Status Draft — pending DPO sign-off
Companion LIA docs/compliance/lia-facilitator-referral-attribution.md (LIA-2026-001)

1. Description of Processing

What: When a patient arrives via a facilitator referral link, the system records facilitator_id on the patient and case records so commission can accrue correctly and so the facilitator can access shared case data after the patient grants consent.

Why: Facilitators are external licensed agents who bear patient acquisition costs and provide coordination services (appointment scheduling, translation, pre-travel logistics). Commission attribution is the contractual mechanism by which their services are funded. Without accurate attribution, the facilitator-mediated coordination model collapses and patients lose access to facilitator support. The legitimate interest is threefold: (a) contractual performance with facilitators, (b) accurate financial audit records, (c) enabling the consent-gated coordination workflow.

Scope: This DPIA covers the referral attribution sub-flow only — from link creation to commission accrual. It does not re-assess the broader patient data lifecycle (covered by Curaway's main privacy notice and data processing agreements).

Legal basis: Legitimate interests (GDPR Art. 6(1)(f)) for cookie placement and attribution recording. Processing is necessary for the performance of the facilitator service agreement and does not override patient privacy interests given the minimal data involved (only facilitator_id — not health data).

Who is affected: Patients who arrive via referral link. Facilitators (as data subjects whose ID is processed). No minors are anticipated; facilitators sign a commercial agreement and are institutional or professional actors.


2. Necessity and Proportionality

Why a cookie?

UTM parameters embedded in the URL persist only for the browser tab session and are lost on redirect or if the patient bookmarks the link and returns later. The referral funnel for cross-border medical travel typically spans days to weeks (patient researches, discusses with family, then registers). A persistent cookie is proportionate to that timeline.

Why not server-side IP-based attribution?

IP-based attribution would require logging patient IP addresses against facilitator IDs, creating a biometric-adjacent linkage without consent. This is more invasive than a cookie containing only facilitator_id.

Why not an explicit "I have a referral code" input?

Explicit code entry creates friction for patients who are not tech-fluent (a significant portion of cross-border medical travel patients). It also creates a failure mode where patients arrive via a link but abandon registration when prompted for a code they don't understand.

Why HMAC-signed rather than encrypted?

The cookie payload contains only facilitator_id (a non-personal, internal UUID) and a 16-hex-character HMAC truncated signature — format: {facilitator_id}.{HMAC-SHA256[:16]} (verified at app/services/referral_link_service.py:89-101). No timestamp is embedded in the cookie payload. There is no PII in the payload; encryption would add complexity without adding privacy benefit. HMAC signature provides tamper-detection sufficient to prevent slug-forging attacks without requiring symmetric key management for decryption.

TTL of 1 year: Proportionate to the referral funnel duration. The cookie is consumed one-shot at registration (apps/patient-app/src/lib/referralCookie.ts:66-80 in curaway-health-navigator), so it does not persist beyond the point of use on any patient who completes registration.

Minimisation audit:

Data element Necessary? Alternative considered
facilitator_id in cookie Yes — attribution target N/A: must identify which facilitator
HMAC signature (16 hex chars) Yes — tamper-detection Encryption: rejected (over-engineered; no PII to protect)
Timestamp in cookie No — absent by design N/A: not included in cookie payload (verified referral_link_service.py:89-101)
Patient identity in cookie No — absent by design N/A: not collected pre-registration
Raw IP address No — absent by design IP attribution: rejected (invasive)
ip_hash (SHA-256 of IP) in events row Yes — click analytics; consent-gated Stored only when has_consent=True (referral_link_service.py:571-583). Hashed, not raw. Counsel should confirm whether hashed IP is PII under applicable guidance.
ua_hash (SHA-256 of User-Agent) in events row Yes — bot/abuse detection; consent-gated Stored only when has_consent=True. Hashed, not raw. Same PII question as ip_hash.
Device fingerprint No — absent by design Fingerprinting: rejected (disproportionate)

3. Data Flow

Each step identifies what data moves, across what boundary, and what lawful ground applies.

Step Action Data in transit Boundary crossed PII?
1 Facilitator creates referral link facilitator_id, slug, landing_path Facilitator portal → backend No (facilitator_id is internal UUID)
2 Patient GETs /api/v1/public/r/{slug} slug inbound; 302 redirect. Two cookies set if and only if has_consent=True: (a) curaway_ref — HMAC-signed facilitator_id, format {facilitator_id}.{HMAC[:16]}, no timestamp (referral_link_service.py:89-101); (b) curaway_ref_slug — raw slug for analytics (public_referral.py:258-268). Pre-consent redirect: counter increment only, no cookies, no events row. Public internet → backend → patient browser Consent-gated: if has_consent=True, events row stores ip_hash (SHA-256 of IP) and ua_hash (SHA-256 of User-Agent) — hashed, not raw PII (referral_link_service.py:571-583). Pre-consent: counter-only, no PII captured.
3 Patient registers; patient-app reads cookie referred_by_facilitator_id posted to POST /api/v1/patients/register Patient browser → backend; cookie consumed (one-shot) Yes — joins with patient identity at this point; covered by registration consent
4 Case created Case.referred_by_facilitator_id frozen (immutable) Within backend; no external transfer Yes — frozen on case row; covered by case consent
5 Patient grants facilitator access case_shares row created Within backend Yes — consent event
6 Commission accrues payment_locked D4 lifecycle hook fires Within backend No — uses internal IDs only

Key data-minimisation note for Step 2: The slug-resolution endpoint (app/routers/public_referral.py:134-270) sets cookies only when has_consent=True (public_referral.py:244-268). Two cookies are set on consent: curaway_ref (HMAC-signed facilitator_id, no timestamp) and curaway_ref_slug (raw slug identifier, low PII materiality — slugs are random strings, not user-identifying). When consent is absent, the endpoint records a counter increment only — no cookies, no events row, no ip_hash or ua_hash stored. This consent-gated design strengthens the privacy posture: the cookie itself is consent-dependent, not pre-consent. When consent is given, the events row stores ip_hash and ua_hash (SHA-256 hashes — not raw values). Counsel should confirm whether hashed IP/UA constitutes PII under applicable supervisory authority guidance (ICO / CNIL).

Pre-consent UTM telemetry (Step 2a — FF-gated addition): When feature flag referral_preconsent_utm_enabled is ON, a second, slimmer event row (referral_link.click_preconsent) is emitted if has_consent=False AND any utm_* parameter is present in the redirect request. This row contains ONLY: slug, facilitator_id, utm_source, utm_medium, utm_campaign, utm_content, utm_term, landing_path. It does NOT contain ip_hash, ua_hash, region, or any patient identifier. Justification: UTM strings are business/analytics metadata describing which campaign or channel drove the visit — they are not user identifiers and are classified as non-PII business metadata under this DPIA. Consent-gated logging would miss dominant first-visit traffic (pre-consent arrivals), making campaign attribution structurally impossible. The flag is defaulted OFF so counsel can review before activation (see §9 item 6). Implementation: app/services/referral_link_service.py (record_click, pre-consent branch) and app/models/event.py (EventType.REFERRAL_LINK_CLICK_PRECONSENT).

Cross-border: EU patient browser → app.curaway.ai (Cloudflare edge, EU PoP) → backend on Railway (ap-south-1). Transfer is covered by Curaway's standard SCCs (see Section 8).


4. Recipients

Recipient Data they can see Condition
Curaway platform (controller) All attribution data Always
Coordinator (Curaway staff) Full case including referred_by_facilitator_id Role: coordinator
Facilitator Case content via case_shares; commission summary via /commissions Only after patient grants consent (Step 5). Attribution-only commissions are returned as commission rows only — GET /api/v1/facilitator/commissions does not join to case content. UI annotation ("case not shared" badge) tracked under plan PR #651 Task 5.5
Provider Redacted clinical snapshot No facilitator data exposed to provider
Analytics / Langfuse case_id, patient_id (UUID) No facilitator attribution in LLM traces

DP-4 constraint (attribution-only cases): A facilitator whose referral did not result in a consent grant can see that a commission is pending attribution to their account but cannot see any case details. The data isolation is structural — the commissions table holds only attribution + amount fields and the /commissions endpoint does not join to case content. UI distinction (showing the row in a muted/badged style) is FE work tracked under plan PR #651 Task 5.5.


5. Retention

Data Retention Basis
curaway_ref cookie (HMAC-signed facilitator_id) 1 year TTL; consumed one-shot at registration Minimum necessary for funnel duration
curaway_ref_slug cookie (raw slug) 1 year TTL; same lifecycle as curaway_ref Analytics identifier; low PII materiality
referral_links table Indefinite; links can be deactivated, not deleted Audit trail; facilitator agreement records
patients.referred_by_facilitator_id Lifetime of patient record; SET NULL on Art. 17 erasure FK with ON DELETE SET NULL
cases.referred_by_facilitator_id Lifetime of case record; SET NULL on Art. 17 erasure FK with ON DELETE SET NULL
referral_link_clicks events (incl. ip_hash, ua_hash) 90 days then aggregated to count-only Hashed analytics data; no raw PII
Commission accrual records 7 years (financial audit obligation) GDPR Art. 17(3)(b) — legal obligation exception

Erasure note: Patient-initiated erasure under GDPR Art. 17 triggers the existing cascade erasure handler. referred_by_facilitator_id on both patients and cases becomes NULL via FK constraint. Commission accrual records that have already settled are retained in anonymized form under the Art. 17(3)(b) legal obligation defence (financial audit, tax records). Unsettled commissions at time of erasure are voided.


6. Risks and Mitigations

Risk Likelihood Impact Mitigation Residual
Cookie spoofing (forging facilitator_id in HMAC payload) Low Low — only misattributes commission HMAC verification at POST /patients/register (app/routers/patients.py:58-68); mismatched signatures rejected and logged to telemetry Accepted
CSRF on /patients/register Low Medium — could force registration with attacker-chosen facilitator Clerk-issued JWT required on registration endpoint; cross-origin requests rejected by CORS policy Accepted
Unintended attribution persistence across devices Medium Low — patient uses different device, no cookie present → no attribution One-shot consumption at registration is definitive; no attribution without cookie is treated as organic; documented in facilitator T&Cs as expected behaviour Accepted
Two facilitators referring same patient Medium Low — only one attribution recorded Last-cookie-wins per public_referral_spec §14.1; both facilitators are notified of outcome; documented in facilitator agreement Accepted
Bot scraping referral links inflating click counts Medium Low — pre-consent: counter only; post-consent: hashed ip_hash/ua_hash stored (not raw PII) UA filter on /api/v1/public/r/{slug}; consent gate prevents events row on pre-consent scraping; bot-detection telemetry as Phase 5 follow-up Accepted
Patient erasure under GDPR Art. 17 breaking commission records Inherent (legal right) N/A — handled by design Cascade deletion sets referred_by_facilitator_id to NULL; settled commission accruals retained anonymized under Art. 17(3)(b) Compliant
Cookie write fails on strict CSP browsers Low Low — patient receives no attribution; organic funnel continues FE #182 strict CSP includes services.curaway.ai in connect-src; verified in preview before production Accepted
Facilitator ID exposed in client-side cookie (curaway_ref) Low Low — internal UUID, no PII, no health data Cookie is Secure; SameSite=Lax; httponly=False intentional (patient-app JS must read for registration submission, spec §5.2); payload contains only internal ID + HMAC Accepted
curaway_ref_slug cookie contains identifying slug Low Low — slugs are random strings, not user-identifying under current process controls If facilitators are ever permitted to set custom slugs containing identifiers (e.g., their own name), the slug cookie becomes PII-adjacent and this DPIA must be re-assessed. Currently a process control: slug generation is system-managed and non-identifying. Accepted (monitor)
ip_hash/ua_hash treated as non-PII when hashed Low Medium — supervisory guidance on hashed IP varies by jurisdiction Counsel must confirm whether SHA-256 hashed IP constitutes PII under ICO/CNIL guidance. Currently treated as pseudonymous analytics data. Requires legal review
UTM strings in pre-consent event classified as non-PII Low Medium — UTM strings could theoretically contain user-identifying content if facilitators embed such data in campaign URLs (1) UTM strings are business metadata (source/medium/campaign/content/term) with no structural requirement to contain PII. (2) Facilitator T&Cs should prohibit embedding PII in UTM params. (3) Flag defaulted OFF for counsel review before activation. (4) No ip_hash, ua_hash, or patient identifier is stored in pre-consent row. Requires counsel confirmation (§9 item 6)

Overall risk level: Low. No special-category data is processed in this flow. The cookie payload contains no health data, no racial or ethnic origin, no biometric data. The predominant risk is commission misattribution, which is a financial rather than privacy risk. The consent-gated cookie design and hashed-only telemetry strengthen the privacy posture relative to many comparable attribution implementations.


7. Data Subject Rights

Right How exercised Implementation
Access (Art. 15) Patient data export referred_by_facilitator_id included in standard GDPR data export package
Rectification (Art. 16) Not applicable Attribution is derived from an event (link click) and frozen at case creation (app/services/case_service.py:19-50). The immutability constraint (DP-C) is by design — post-hoc mutation would enable commission fraud. Facilitator T&Cs and patient privacy notice explain this.
Erasure (Art. 17) Standard erasure request flow CASCADE handler sets referred_by_facilitator_id to NULL on both patient and case rows. Settled commission records anonymized and retained under Art. 17(3)(b).
Portability (Art. 20) Standard data export Included in machine-readable export
Objection (Art. 21) Patient may decline consent grant (Step 5) Withholding consent prevents facilitator from accessing case data. Attribution-only commission accrues server-side (legitimate interest basis) but facilitator sees no case content. Patient may also submit erasure request to clear attribution entirely.
Automated decision-making (Art. 22) N/A Attribution is mechanical (cookie read → DB write), not a decision affecting the patient's legal status or similar significant effect.

Consent and attribution independence: Attribution (Step 4) is distinct from access consent (Step 5). A patient can be attributed to a facilitator without ever granting that facilitator access to their case. The facilitator earns a referral commission but sees no case data. This separation is architecturally enforced.


8. Cross-Border Considerations

EU patients are registered under tenant-curaway-patients with data residency in ap-south-1 (Mumbai, India). Facilitators may be located in any jurisdiction; the facilitator_id field is an internal UUID — it does not constitute personal data of the facilitator in isolation without the facilitator directory, which is held separately.

Transfer mechanism: EU patient data flowing to Railway backend (ap-south-1) is covered by Curaway's existing Standard Contractual Clauses (SCCs) under GDPR Art. 46(2)(c). This DPIA does not change the cross-border transfer posture established in the main DPA.

Special-category data: facilitator_id is not special-category data under GDPR Art. 9. It is not health data, racial or ethnic origin, political opinion, religious belief, biometric data, or data concerning sexual orientation. The referral attribution flow does not make the cross-border transfer of special-category data more complex than what is already covered by the main DPA.

Provider jurisdiction: Providers are in destination countries (India, Turkey, Thailand, Spain). Provider-facing data views do not include facilitator attribution data (see Section 4).


9. Consultation and Sign-Off

No high-risk processing requiring supervisory authority consultation has been identified. Risk level is Low (see Section 6). This DPIA is sufficient under GDPR Art. 35(7) without prior consultation under Art. 36.

Sections requiring legal review before finalisation:

  1. Art. 17(3)(b) defence for retaining anonymized commission accruals post-erasure — confirm with counsel that financial audit obligation is established under applicable jurisdiction law (India Companies Act 2013 / UK Companies Act 2006 as applicable to facilitator contracts).
  2. Legitimate interest basis (Art. 6(1)(f)) — confirm that the Legitimate Interests Assessment (LIA) is documented separately and that the balancing test has been completed. LIA is at docs/compliance/lia-facilitator-referral-attribution.md.
  3. Facilitator T&Cs — confirm that last-cookie-wins attribution policy and rectification exclusion are explicitly disclosed.
  4. ip_hash / ua_hash classification — confirm with counsel whether SHA-256 hashed IP address and User-Agent string constitute personal data under applicable supervisory authority guidance (ICO, CNIL). See §6 risk row and §2 minimisation audit.
  5. curaway_ref_slug cookie — confirm slug values are never configured to contain personally identifying strings (e.g., facilitator names). Currently a process control, not a technical enforcement (see §6 risk row).
  6. Pre-consent UTM telemetry — counsel must confirm UTM strings are not classified as PII or special-category data under their applicable guidance. Currently classified as business metadata (campaign source, medium, name, content, term). The referral_preconsent_utm_enabled feature flag is defaulted OFF so this flag can be disabled if counsel objects. No ip_hash, ua_hash, region, or patient identifier is stored in the pre-consent event row. Activation requires counsel sign-off on this item. See §3 (Step 2a) and §6 (UTM risk row).

DPO sign-off:
Name:  Srikanth Donthi
Title: CPO/CTO (acting DPO under Curaway MVP governance)
Date:  2026-05-05
Signature: ____________________

10. Review Schedule

Trigger Action
Annual review 2027-05-05
Material change to attribution flow (new data elements, new recipients, new cross-border transfers) Immediate re-assessment
Supervisory authority guidance on cookie attribution for healthcare platforms Review within 30 days of guidance publication
Phase 5 bot-detection implementation Update Section 6 bot-scraping row
Any change to slug generation (custom/vanity slugs) Reassess curaway_ref_slug PII classification immediately
Supervisory authority ruling on hashed IP as PII Immediate re-assessment of §2 and §5

This document was produced as part of PR #651 Phase 2 Task 2.2 and updated under issue #666. Source references: app/routers/public_referral.py:134-270, app/services/referral_link_service.py:54-67, app/services/referral_link_service.py:89-101, app/services/referral_link_service.py:557-583, app/routers/patients.py:58-68, app/services/case_service.py:19-50, curaway-health-navigator/apps/patient-app/src/lib/referralCookie.ts:66-80.