Skip to content

Cookies + cref

OpenPartner attribution is a cross-domain problem. The click happens on the partner’s share URL (share.creator.com/r/foo or app.openpartner.dev/r/...). The conversion happens on the brand’s site (acme.com/signup). Different domains. The browser won’t share cookies between them. Referer headers get stripped by Safari ITP, by privacy-focused browsers, and by any HTTPS → HTTP transition.

So how does the conversion get attributed back to the right partner?

The trick: cref

Every click on a share link generates a unique clickId (a ULID). The router:

  1. Records the Click row server-side, keyed by the clickId
  2. Appends ?cref=<clickId> to the destination URL as a query param (or, when the destination contains the {click_id} macro, puts the clickId there instead)
  3. 302-redirects the user to that URL

The user lands on https://acme.com/signup?cref=01ARZ3NDEKTSV4RRFFQ69G5FAV.

From here the brand’s site needs to hold on to that clickId until the visitor signs up, then tell OpenPartner who signed up. There are two ways to do it.

With the OpenPartner SDK (the script tag or @openpartner/sdk), on page load the SDK:

  1. Reads cref from the URL and keeps it in local storage (openpartner:cref), so it survives single-page-app navigation and later visits.
  2. Optionally mirrors it into a first-party cookie on the brand’s own domain, op_ref, so the brand’s server can read it too (for example as a Stripe Checkout client_reference_id). The script tag turns this cookie on by default (data-cookie="off" turns it off). In @openpartner/sdk it’s off unless you pass referralCookie, whose days option sets its lifetime (default 90).

When the visitor signs up or logs in, the brand’s app calls:

await op.identify(user.id);

Without the SDK: the brand’s server reads cref from the signup URL, carries it through its own signup flow, and calls POST /attribution/identify with { cref, userId } when the account is created. Nothing is stored in the visitor’s browser on the brand’s side, which also makes it easy to wait for cookie consent.

Either way, identify does the stitching server-side:

  1. The cref is matched back to the original Click row, so we know the partner.
  2. An Identity row links the userId to the clickId, so every later event from this userId attributes without needing the cref again.
  3. Conversions are reported from the brand’s server or Stripe, not the browser: POST /attribution/events (or the server SDK’s trackEvent) with the same userId, or the Stripe App. See Events.
  4. The program’s attribution model decides who gets credit (last-click usually, but see Attribution models), and a Commission row is created from the rule.

Why first-party cookies work where third-party don’t

Third-party cookies (a cookie set on openpartner.dev while the user is on acme.com) are blocked (or, in Firefox, kept in a separate jar for each site) by:

  • Safari ITP (default since 2020)
  • Firefox Total Cookie Protection (default since 2022)
  • Brave (default)
  • Chrome in Incognito windows (default), and in normal windows for anyone who turns them off in settings

Chrome still allows them in normal windows by default. Google dropped its plan to phase them out in July 2024, said in April 2025 that it wouldn’t add a new prompt for them either, and in October 2025 retired the Privacy Sandbox APIs that were meant to replace them, such as Topics, Protected Audience and Attribution Reporting.

Chrome’s default doesn’t make them safe to depend on. A platform that still needs a third-party cookie to carry the click from the partner’s link to your site can lose it in any of the browsers above, and the conversions that follow go unattributed.

First-party cookies (set on acme.com itself, by acme.com’s own scripts) aren’t blocked by any of these. Safari does limit how long storage written by scripts lasts (see below). The cref query-param-to-first-party-storage hop is what lets attribution survive ITP and the other protections above.

These are different things:

  • The router’s _cref cookie is set when the click happens, on the router’s configured cookie domain, and lasts 90 days. A hosted tracking link can’t set a cookie for the brand’s own, unrelated domain, so the clickId reaches the brand’s site through the cref URL parameter instead.
  • The SDK’s storage on the brand’s site: local storage, kept until cleared (though Safari can clear script-written storage after 7 days without a visit), plus the optional op_ref cookie with a lifetime you choose.
  • The attribution window is the program’s commission policy, set per program from 1 to 365 days (default 60). It’s measured from the click to the conversion. See Attribution window.

Browser storage only has to bridge the gap between the click and identify. Once identify has linked the userId to the click, nothing in the browser matters any more: later events are attributed from the Identity row.

The customer’s first attributed conversion has to fall within the attribution window. With “Renewals stay with the partner who earned the customer” on (the default), what follows keeps that attribution after the window closes. A repeat of a conversion type (a renewal invoice, say) keeps the partners that conversion had. A new type of conversion with no click left in the window goes to the partners of the customer’s latest attributed conversion.

What if the user clears cookies?

They lose the cref → the next conversion from them isn’t attributable to that click. Same fate as any cookie-based system.

What we can do (and openpartner does): if the same userId was identified earlier with a cref, we have an Identity row linking userId ↔ clickId. So if the user signs up on mobile (cookie present) and then converts on desktop after signing in (cookie absent but userId known), we still attribute via the Identity table.

This “logged-in stitching” is what lets attribution survive cross-device. See Identity stitching for the detail.

Why not UTMs?

UTMs (utm_source, utm_campaign, etc.) are display-layer marketing tags — they go in the URL, get logged in analytics, and disappear. They don’t track individual users; they track campaigns.

For partner attribution, you need a unique-per-click identifier that persists across the session and follows the user to conversion. That’s what cref is. UTMs and cref are complementary, not alternatives — many brands set both.

What if I see double-attribution?

A user clicks Partner A’s link, then Partner B’s link, then converts. Who gets credit? Depends on the campaign’s attribution model:

  • Last-click (default) — Partner B
  • First-click — Partner A
  • Linear — A and B split 50/50
  • Position 40/20/40 — A 40%, B 40% (since they’re first and last with no middle)

The cref cookie on the brand’s site holds the last click — but the server has all clicks in the window from the same user (via the Identity table) and computes the model server-side.

Full deep-dive: Attribution models.