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:
- Looks up the link by the key in its path.
- Records a
Clickrow: link, partner, program, landing URL, hashed IP, user agent, referer and UTM parameters. - Sets a
_crefcookie 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). - 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.