Attribution
How each payment gets the channel that introduced the customer and the one that closed them.
Every payment gets two answers, not one:
- Introduced by — the earliest qualifying touch inside the window. This is who found the customer.
- Closed by — the latest qualifying touch before the money. This is who finished the job.
A blog post that brought someone in six weeks ago and an ad they clicked yesterday both did work. One number would hide one of them.
What counts as a touch
A touch is a human $session_start, $pageview, or $screen that
carries a real source: a channel other than direct or internal, or a click
ID.
- A visit with no campaign and no referrer is not a qualifying touch.
- A move from one page of your own site to another is not a touch.
- Bot visits never count. See Bot traffic policy.
- If no qualifying touch exists in the window, the payment is Direct. FounderHQ never promotes a random visit to fill the gap, and Direct never erases a known earlier source.
- A payment with no known buyer is not Direct. It shows as Not linked to a visit. So does a payment imported from before you installed FounderHQ, and one whose window reaches past the events your plan keeps.
The window
The window is counted back from the day the money was earned, not the day the webhook arrived. That keeps a renewal on the renewal's own merits. The default is 90 days. You can also use 30, 60, or 180. Each result records the model and the window that produced it, so two reports never disagree silently.
Where the source comes from
The SDK reads campaign parameters and ad click IDs from the URL on
every landing, then keeps both the first and the latest value on the
contact. There are 24 keys: the five utm_* parameters plus 19 click
IDs from Google, Meta, Microsoft, LinkedIn, TikTok, Reddit, and others.
The full list is in the
event taxonomy. Mobile deep
links and the Play install referrer feed the same keys.
Keep the chain through checkout
The gap most products lose is checkout. Attach the visitor's IDs to the payment so the payment can find the person.
const metadata = founderhq.checkoutMetadata();
// pass metadata into your Stripe or Dodo checkout sessionRenewals do not carry that metadata, so FounderHQ also remembers the provider customer once matched. See Checkout metadata and payment links.
If a payment cannot be matched to anyone, FounderHQ does not guess. The payment still counts in your revenue, and it shows as Not linked to a visit in the channel breakdown.
When facts arrive late
A touch that shows up after a decision was made does not quietly rewrite history. FounderHQ recomputes the decision when the payment's buyer changes or when you change the attribution window, and the new answer is added as a new version, so a number you screenshotted last week still explains itself.