# Cross-domain tracking

> Keep one visitor across separate domains of the same site, such as a landing site and an app, with consent-aware, short-lived links.

Source: https://metricraven.com/docs/cross-domain  
Markdown: https://metricraven.com/docs/cross-domain.md  
Section: Features

When a visitor follows a link from `example.com` to `app.example.io`, MetricRaven can keep counting them as the same visitor, so the signup or payment on the app is credited to the campaign that brought them to the landing site.

Subdomains of one domain (`www`, `app`, `shop`) don't need this: they share the visitor through `data-domain`. Use cross-domain tracking for different root domains, like a marketing site and an app, a site and a separate store, or several brand domains.

## Set it up

1. **Add every domain to the site**

   In **Settings → General → Site**, list all the domains under the same site. Visits are only accepted from these domains, and links are only joined when they come from one of them.

2. **Install the same script on each domain**

   Use the same site ID everywhere, and add `data-allowed-hostnames` with your other domains, comma-separated. Listing the current domain too is fine, so the same tag works on all of them.

   ```html
   <!-- On example.com and on app.example.io: the same tag, listing the other domain(s). -->
   <script
     defer
     src="https://metricraven.com/js/script.js"
     data-site-id="YOUR_SITE_ID"
     data-allowed-hostnames="example.com,app.example.io"
   ></script>
   ```

3. **Links and forms are handled for you**

   When a visitor clicks a link or submits a form that leads to a listed domain (or one of its subdomains), the tracker adds a short `mr_link` parameter. The page that opens reads it, removes it from the address bar straight away and continues the visit.

## Links you build in code

Navigation through `window.location`, `window.open` or a router doesn't go through a link click, so add the parameter yourself, right before navigating:

```javascript
// Links and forms are linked automatically. For navigation in code, add the token first:
window.location.href = window.analytics.linkUrl("https://app.example.io/signup?plan=pro");

// Or append the parameters yourself (empty until the tracker has a fresh token):
const params = new URLSearchParams(window.analytics.linkParams());
```

With the npm package:

```typescript
import { init, linkUrl, linkParams } from "@metricraven/web";

init({ siteId: "YOUR_SITE_ID", allowedHostnames: ["example.com", "app.example.io"] });

// Build the URL right before navigating: tokens expire after 30 minutes.
window.open(linkUrl("https://app.example.io/signup"), "_blank");
linkParams(); // { mr_link: "…" } or {}
```

`linkUrl` only changes URLs that lead to one of your listed domains, so it is safe to call on any URL. It returns the URL unchanged until the tracker has its first response. See [NPM package](https://metricraven.com/docs/npm-package#api).

## Privacy model

No visitor ID ever appears in a URL. The parameter is an opaque token that only MetricRaven's server can read, and it is useless anywhere but in the browser it was made for.

| Rule | Why |
| --- | --- |
| Encrypted and signed | The token holds the visitor ID and an expiry, encrypted and authenticated by the server. Nobody can read the ID from it or forge one. |
| Bound to one browser | It only opens for a request from the same IP address and browser it was issued to. A copied, shared or bookmarked link doesn't merge someone else into the visitor. |
| Short-lived | It expires 30 minutes after it was issued. The tracker keeps it in memory only and refreshes it with each pageview. |
| Only from your own pages | The arrival page's referrer must be another of the site's domains, so a link pasted into chat or email is ignored. |
| Added at the moment of the click | Links get the parameter as they are followed and lose it right after, so the token isn't left in your page or in copied links. |

Consent works the same as on a single domain:

- **No consent on the receiving domain:** nothing is stored in the browser there. The server remembers that this browser's daily ID on that domain belongs to the linked visitor until the end of the UTC day, the same lifetime as the daily ID itself, then forgets it.
- **Consent on the receiving domain:** the visit continues with the linked visitor's ID, as subdomains do with the shared cookie. If the visitor only had a daily ID on the first domain, today's visits from it join this domain's consented ID, just as visits from before consent do on one domain.
- **Consent granted later on the receiving domain:** the stored ID becomes the linked visitor's, so the journey stays in one piece.

See [Consent & visitor IDs](https://metricraven.com/docs/consent) for how IDs work before and after consent. Deleting a person also deletes these same-day links.

> **Verify it works**
>
> On the first domain, add `?metricraven_debug=1` to the URL and open the console: it lists the linked hosts. Click through to the other domain: the address bar shows no `mr_link`, and in **Traffic → Visitors** the visitor's journey shows pages from both domains.

## Details

- Clicks to your listed domains aren't counted as outbound clicks. Visits between them aren't referrals.
- GET forms carry the token as a hidden field; other forms carry it on their action URL. Both are removed right after submitting.
- Linking needs the browser to send a referrer. The default policy does; a page with `Referrer-Policy: no-referrer` can't link. If you proxy the tracker on one domain only, forward the visitor's IP (`x-visitor-ip`) so both domains see the same address. See [Proxy](https://metricraven.com/docs/proxy).
- A token that is expired, from another browser or not from one of your domains is ignored silently and the visit counts as a new visitor. Debug mode logs it.
- Native apps don't use this. To join web visits to app users, use [identify](https://metricraven.com/docs/identify-users) on both.
