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.
Emitting: decorate outbound links
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
wwwsubdomain, andwww.example.comdoes not matchexample.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 page | After launchDefault() |
|---|---|
https://shop.example.com/cart?step=1 | https://shop.example.com/cart?step=1&rfsn_v4_id=<id>.<base64key>&rfsn_v4_aid=1234567&rfsn_v4_cs=ab12cd34 |
https://shop.example.com/cart | https://shop.example.com/cart?rfsn_v4_id=<id>.<base64key> |
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 tagThe 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:
- A tracking ID already in storage wins outright. The URL is not consulted.
- Otherwise the URL value is adopted, but only when all of these hold: the
rfsn_v4_idparameter is present,url_id_detectionis on, the second segment base64-decodes, and the decoded key is exactly equal to the destination's configured Public key. - Otherwise a new tracking ID is minted with
POST /start.
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:
| Value | Precedence | Gated by url_id_detection? |
|---|---|---|
rfsn_v4_id | A stored value wins; the URL value is used only when storage is empty | Yes |
rfsn_v4_aid / rfsn_v4_cs | A valid pair on the URL overwrites whatever is in storage | No |
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.
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
rfsnparameter short-circuits the standalone pair entirely. - The standalone pair is honoured only alongside an
rfsn_v4_idwhose decoded key matches. - Attribution already in pixel storage is never overwritten by the standalone pair. A combined
rfsnparameter does overwrite it whenever its value differs from the stored attribution, registering a new click at the same time. rfsn_v4_aidis capped at 10 characters andrfsn_v4_csat 20.
When the pair is adopted, carts already sent to Refersion are re-posted under the new attribution.
Other parameters the pixel reads
| Parameter | Effect |
|---|---|
rfsn | <aid>.<cs> or <aid>.<cs>.<creative_id>. Registers an affiliate click and stores the attribution. Takes precedence over the standalone pair. |
creative_id | Creative ID sent with the click event. |
subid | Sub-ID passed through on the click event. |
rf_test | Marks the click as a test. Defaults to 0. |
Supported directions
| From | To | Supported |
|---|---|---|
| Non-Shopify site running the v4 SDK | Non-Shopify site running the v4 SDK | Yes. Emit and receive are both available. |
| Non-Shopify site running the v4 SDK | Shopify storefront running the web pixel | Yes. Receive-only on the Shopify side. |
| Shopify storefront running the web pixel | Any destination | No. The strict sandbox cannot rewrite links, so the pixel cannot emit. |
Validation rules
| Value | Rule |
|---|---|
rfsn_v4_id | Exactly 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_aid | Digits only. The Shopify pixel additionally caps it at 10 characters |
rfsn_v4_cs | Case-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 explicitly — launchDefault() 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
| Symptom | Cause | Fix |
|---|---|---|
| Parameters never appear on outbound links | urls is missing, an entry carries a scheme or path, an entry has a leading space after a comma, or the hostname does not match exactly | Use bare hostnames, comma-separated with no spaces, matching the link's hostname character for character |
| Parameters appear on some links but not others | A matched link with no existing query string receives only rfsn_v4_id, and the affiliate values are appended only when both are already in storage | Give the link a query string |
| Links added after page load are never decorated | Decoration 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 minted | url_id_detection was never set in JavaScript, or the two sides use different Public keys | Set r.settings.url_id_detection = true before r.launchDefault(), and confirm both sides use the same Public key |
| The destination keeps an older tracking ID | A value already in storage wins by design | Clear localStorage on the destination and retest |
| Values are silently dropped | They failed validation | Check 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 adopted | Adoption is off by default on the pixel | Ask Refersion to enable URL ID detection for your account |
Related
- Order Tracking overview — how visit tracking and order reporting fit together.
- JavaScript v4 tracking — the client-side tracker this guide extends.
- Server-side tracking — reporting orders from your backend.
- API Reference — how to authenticate, and every endpoint the Refersion API exposes.