Skip to content

How attribution works

OpenPartner is event-sourced. The raw layers (clicks, identities and events) are kept as they were recorded, and attribution and commissions are derived from them, so every commission can be traced back to the data that produced it.

The chain

click → identity → event → attribution → commission → payout
(raw) (derived) (raw) (derived) (derived) (ledger)

Click

When a user hits a partner link (e.g. https://go.yourdomain.com/r/abc123), the click router:

  1. Looks up the link by the key in its path.
  2. Records a Click row: link, partner, program, landing URL, hashed IP, user agent, referer and UTM parameters.
  3. Sets a _cref cookie holding the click id, for 90 days, on the router’s configured cookie domain. A hosted tracking link can’t set a cookie for the customer’s own, unrelated domain, so the customer’s site gets the click id from the URL instead (next step).
  4. Redirects to the destination URL, with the click id added as ?cref= (or wherever the destination’s {click_id} macro puts it).

On the customer’s own site, the SDK picks the click id up from ?cref= and keeps it in localStorage, so it survives single-page-app navigation and later visits. Or the customer’s server reads cref from the signup URL and calls identify itself, with no browser storage at all. See Cookies + cref.

Identity

When the customer’s app calls op.identify(user.id) after signup or login, OpenPartner writes an Identity row linking that user to the click. Identity is the bridge that lets us attribute revenue to a click that happened weeks earlier, even on a different device, as long as the same logged-in user is involved. Every click a user is linked to is kept, so multi-touch models can see all of them. If that user’s events arrived before the link was made, they’re attributed at that point.

Event

Revenue events (subscription_created, invoice_paid, etc.) come from your backend, through the server SDK’s trackEvent(). They reference the userId, which chains back through Identity → Click. See Tracking events.

Attribution

The attribution engine walks Event → Identity → Click, takes the clicks inside the program’s attribution window (60 days by default), and applies the program’s model:

  • last_click (default): the most recent click gets 100%
  • first_click: the first click in the chain gets 100%
  • linear: credit splits equally across all qualifying clicks
  • position: 40% first / 40% last / 20% split across the middle (50/50 with two clicks)

The result is an Attribution row for each credited click, with its share. Repeats of an event type the customer already converted on, such as renewals, keep the split of that first conversion unless the program turns this off. See Renewals.

Commission and payout

Commissions are computed from each Attribution and the program’s commission rules: a percent of revenue or a fixed amount, per event type, optionally recurring. Each credited click earns its share.

A new commission starts as accrued and waits in Admin → Review queue, where you approve or reverse it (or call POST /commissions/{id}/approve and POST /commissions/{id}/reverse). If the partner’s terms or the program set a holdback, the commission can’t be approved until the holdback ends. The scheduled jobs then approve it automatically (on a self-hosted instance, when the scheduler is on). With no holdback, a commission is approved only by hand.

Approved commissions settle on the next payout run: by Stripe Connect or by hand on a self-hosted instance, or paid by the brand through its own rail on hosted plans.

Why event-sourcing matters

You can audit any commission back to the event and the raw click that drove it. Late information reaches earlier data: when a user is identified after their events arrived, those events are attributed then, and when you unflag a click in fraud review, the events skipped while it was flagged are attributed again.

Changing a program’s attribution model or window applies to events recorded after the change. Existing attributions and commissions stay as they were.

And when you export your data, the bundle carries the raw layers alongside the derived ones, so the receiving system can re-derive attribution from them. See Backups and portability.