Skip to main content

Cross-domain Tracking

Refersion stores a shopper's tracking state in origin-local browser storage, so a shopper who starts on https://www.example.com and then moves to https://shop.example.com cannot use the first site's local storage directly. If both sites use the same Public key, initializeXDLS() may restore a shared tracking ID; when XDLS is unavailable or has no value, URL injection provides the fallback.

Cross-domain tracking closes that gap. The originating page appends the tracking variables to every outbound link pointing at one of your other domains, and the receiving page adopts them instead of minting a new session.

There are two halves to set up, and you configure them separately:

  • Emit — the originating site decorates its outbound links. Only the JavaScript v4 tracker can do this.
  • Receive — the destination adopts the incoming values. Both the JavaScript v4 tracker and the Refersion Shopify web pixel can do this.

The Shopify web pixel can only do the receive half. See Supported directions.

What travels on the URL

?rfsn_v4_id=<32-char-alphanumeric-id>.<base64(publicKey)>&rfsn_v4_aid=<aid>&rfsn_v4_cs=<cs>

rfsn_v4_id is a single compound parameter: the tracking ID, a period, then the originating site's Public key in base64. There is no separate public-key parameter on the wire. The destination splits the value on the period, decodes the second segment, and compares it to its own configured Public key.

rfsn_v4_aid and rfsn_v4_cs are the affiliate ID and checksum of the affiliate who should be credited.

The originating site's tracking script decides which links to decorate from an allowlist you supply as a urls query parameter on the tracking script's own src.

  • Entries are comma-separated bare hostnames. No scheme, no path, no port, and no space after a comma — a leading space becomes part of the hostname and can never match.
  • Matching is exact hostname equality. A bare apex host does not match a www subdomain, and www.example.com does not match example.com.

Copy the snippet below into the <head> of every page on the originating site. It is the JavaScript v4 bootstrap with two additions: the urls allowlist on s.src, and r.settings.url_id_detection = true so that this site can also receive.

<!-- REFERSION TRACKING: BEGIN -->
<script>
! function(e, n, t, i, o, c, s, a) {
e.TrackingSystemObject = "r", (s = n.createElement(t)).async = 1,
s.src = "https://cdn.refersion.com/refersion.js?urls=shop.example.com,checkout.example.com",
s.onload = function() {

// Replace with your Refersion Public API Key
r.pubKey = "YOUR-PUBLIC-KEY";

// Adopt a tracking ID supplied on the URL by one of your other domains
r.settings.url_id_detection = true;

r.initializeXDLS().then(() => {
r.launchDefault().then(() => {

// Send a custom event that can be listened to later
const rfsnTrackingEvent = new Event("refersion-loaded");
document.dispatchEvent(rfsnTrackingEvent);

})
})
}, (a = n.getElementsByTagName(t)[0]).parentNode.insertBefore(s, a)
}(window, document, "script");
</script>
<!-- REFERSION TRACKING: END -->

A <script> tag that only sets src is not enough. The tracker does not launch itself — it must be given a pubKey and then started with launchDefault(), which is what the snippet above does.

With the allowlist above in place, links on the originating page are rewritten like this:

Link on the originating pageAfter launchDefault()
https://shop.example.com/cart?step=1https://shop.example.com/cart?step=1&rfsn_v4_id=<id>.<base64key>&rfsn_v4_aid=1234567&rfsn_v4_cs=ab12cd34
https://shop.example.com/carthttps://shop.example.com/cart?rfsn_v4_id=<id>.<base64key>
Links with no query string receive only the tracking ID

A matched link that already carries a query string receives rfsn_v4_id plus rfsn_v4_aid and rfsn_v4_cs. A matched link with no query string receives only rfsn_v4_id — the affiliate values are not appended.

If the destination needs the affiliate values, give the link a query string.

The affiliate values are appended only when both are already in the originating site's storage, so a visitor who arrived with no affiliate attribution gets rfsn_v4_id alone on every link.

Decoration runs once, while launchDefault() executes. Links that your own scripts or a single-page-app router add to the page afterwards are not decorated.

Receiving: adopt an incoming tracking ID

url_id_detection cannot be set from the script tag

The core refersion.js does not read url_id_detection from the script tag's query string. It defaults to false, and only the Shopify and Bold Cashier wrappers assign it from the src. Appending ?url_id_detection=true to refersion.js changes nothing and reports no error. While URL ID detection is disabled, the incoming tracking ID is silently ignored, but an existing v4 ID is still reused, including one produced by a v3 upgrade or restored from local/XDLS storage. A new ID is minted with POST /start only when no tracking ID is available.

Set it in JavaScript instead, to boolean true, before you call launchDefault():

r.pubKey = 'YOUR_PUBLIC_KEY';
r.settings.url_id_detection = true;
r.launchDefault();

The destination resolves the tracking ID in this order:

  1. A tracking ID already in storage wins outright. The URL is not consulted.
  2. Otherwise the URL value is adopted, but only when all of these hold: the rfsn_v4_id parameter is present, url_id_detection is on, the second segment base64-decodes, and the decoded key is exactly equal to the destination's configured Public key.
  3. Otherwise a new tracking ID is minted with POST /start.
A URL tracking ID is an unverified claim

The public-key match is not authentication. The Public key is published — it appears in the pixel's settings, is sent as the plaintext Key request header, and is base64-encoded into link URLs by the tracking SDK itself — so anyone can build an rfsn_v4_id that passes the comparison for a given destination.

What the match buys is cross-merchant isolation: a URL decorated for one merchant cannot seed a tracking ID under another merchant's key. It does not establish that the ID was ever issued by POST /start, and an adopted ID becomes the visitor identity sent on /checkout and /affiliate_click.

That is why adoption is off by default on both the JavaScript SDK and the Shopify pixel, and has to be turned on per destination.

A check for legacy JavaScript v3 state runs ahead of all three, and applies only to merchants still carrying v3 tracking data.

Affiliate values follow the opposite precedence

The two rules differ, so configure with both in mind:

ValuePrecedenceGated by url_id_detection?
rfsn_v4_idA stored value wins; the URL value is used only when storage is emptyYes
rfsn_v4_aid / rfsn_v4_csA valid pair on the URL overwrites whatever is in storageNo

Valid affiliate values arriving on the URL are written to storage unconditionally, whether or not url_id_detection is on and whether or not the destination already had attribution.

Why injected affiliate values do not register a new click

A click is registered only when the raw rfsn parameter is on the URL. Standalone rfsn_v4_aid and rfsn_v4_cs seed storage without dispatching one.

That is deliberate. The click was already recorded on the originating domain when the shopper followed the affiliate's link. Firing it again on the destination would double-count it for the affiliate, inflating their click count on a single visit.

An inbound rfsn parameter behaves differently: it is split on periods into the affiliate ID, the checksum and an optional creative ID, and it does register a click.

Shopify storefronts

The Refersion Shopify web pixel is a receive-only participant. It runs in Shopify's strict web pixel sandbox, which has no DOM access, so there is no pixel equivalent of link decoration and no cross-domain localStorage iframe.

Off the storefront URL the pixel accepts rfsn_v4_id, rfsn_v4_aid and rfsn_v4_cs, and resolves the tracking ID with the same three-step precedence as the JavaScript SDK.

The originating domain has to do the work

Nothing reaches the storefront unless the originating domain runs the JavaScript v4 tracker with the storefront's hostname in its urls allowlist. Without that, no parameters are ever appended and the hop cannot work.

Pixel settings

The pixel exposes three settings: publicKey, apiUrl and urlIdDetection. There is no allowlist field. The public-key comparison below applies to rfsn_v4_id and the standalone rfsn_v4_aid / rfsn_v4_cs pair; a raw rfsn parameter follows its separate click-registration flow.

Adopting a tracking ID from the URL is off by default. Ask Refersion to enable URL ID detection for your account.

The decoded public key carried in rfsn_v4_id must equal the pixel's configured publicKey. It buys exactly what it buys on the SDK, and no more — see A URL tracking ID is an unverified claim.

Where the pixel is stricter than the SDK

The pixel's affiliate handling is not identical to the SDK's:

  • A combined rfsn parameter short-circuits the standalone pair entirely.
  • The standalone pair is honoured only alongside an rfsn_v4_id whose decoded key matches.
  • Attribution already in pixel storage is never overwritten by the standalone pair. A combined rfsn parameter does overwrite it whenever its value differs from the stored attribution, registering a new click at the same time.
  • rfsn_v4_aid is capped at 10 characters and rfsn_v4_cs at 20.

When the pair is adopted, carts already sent to Refersion are re-posted under the new attribution.

Other parameters the pixel reads

ParameterEffect
rfsn<aid>.<cs> or <aid>.<cs>.<creative_id>. Registers an affiliate click and stores the attribution. Takes precedence over the standalone pair.
creative_idCreative ID sent with the click event.
subidSub-ID passed through on the click event.
rf_testMarks the click as a test. Defaults to 0.

Supported directions

FromToSupported
Non-Shopify site running the v4 SDKNon-Shopify site running the v4 SDKYes. Emit and receive are both available.
Non-Shopify site running the v4 SDKShopify storefront running the web pixelYes. Receive-only on the Shopify side.
Shopify storefront running the web pixelAny destinationNo. The strict sandbox cannot rewrite links, so the pixel cannot emit.

Validation rules

ValueRule
rfsn_v4_idExactly two segments when split on a period
Tracking ID (segment 0)Alphanumeric and exactly 32 characters
Public key (segment 1)Base64-decodes without throwing, then equals the configured Public key exactly
rfsn_v4_aidDigits only. The Shopify pixel additionally caps it at 10 characters
rfsn_v4_csCase-insensitive alphanumeric. The Shopify pixel additionally caps it at 20 characters

A value that fails validation is discarded silently. There is no error and no console warning. An invalid or absent rfsn_v4_id causes the destination to mint a new tracking ID only when it has no stored ID. Invalid rfsn_v4_aid or rfsn_v4_cs values are discarded without changing existing affiliate attribution.

URL parameters versus cross-domain local storage

URL injection is not the only way to share a session between your domains. The two mechanisms are complementary.

URL parameters work between any two instrumented domains, require no third-party storage, and are the only mechanism available when the destination is a Shopify storefront.

initializeXDLS() loads a per-public-key iframe hosted on a Refersion subdomain and reads and writes it over postMessage, so any site loading the same Public key's iframe shares one store. It has to be called explicitlylaunchDefault() does not invoke it, which is why every published snippet calls it first. It feeds step 1 of the precedence order above rather than step 2: a value it returns counts as a value already in storage.

Browsers that partition third-party storage make the iframe unreliable. The tracker resolves after a short timeout whether or not the iframe ever loaded, so a partitioned browser degrades silently rather than erroring. The iframe is unavailable inside the Shopify pixel sandbox.

Troubleshooting

SymptomCauseFix
Parameters never appear on outbound linksurls is missing, an entry carries a scheme or path, an entry has a leading space after a comma, or the hostname does not match exactlyUse bare hostnames, comma-separated with no spaces, matching the link's hostname character for character
Parameters appear on some links but not othersA matched link with no existing query string receives only rfsn_v4_id, and the affiliate values are appended only when both are already in storageGive the link a query string
Links added after page load are never decoratedDecoration runs once, during launchDefault()Render the links before the tracker loads, or re-run decoration after your own DOM updates
Parameters arrive but a new tracking ID is still mintedurl_id_detection was never set in JavaScript, or the two sides use different Public keysSet r.settings.url_id_detection = true before r.launchDefault(), and confirm both sides use the same Public key
The destination keeps an older tracking IDA value already in storage wins by designClear localStorage on the destination and retest
Values are silently droppedThey failed validationCheck them against the validation table, and add ?rfsn_v4_dbg=true to the destination URL to log what was parsed
On a Shopify storefront the ID is never adoptedAdoption is off by default on the pixelAsk Refersion to enable URL ID detection for your account