# Cross-domain and cross-subdomain tracking

Keep one visitor identity when traffic moves between domains, automatic for subdomains, one config line for different root domains. Works for the script tag and the SDK.

Source: https://sourceloop.ai/help/cross-domain-and-subdomain-tracking/
Updated: 2026-06-29

---

When a visitor moves from one site to another (marketing to app), you want SourceLoop to treat them as **one person**, not two strangers. How you do that depends on whether you are crossing **subdomains** or **different root domains**.

## First, which case are you in?

| Going from → to | Example | What you need |
|---|---|---|
| **Subdomain → subdomain** (same root domain) | `www.yoursite.com` → `app.yoursite.com` | **Nothing.** Automatic. |
| **Domain → different root domain** | `yourbrand.com` → `yourapp.io` | **One config line** (and SourceLoop on both sites). |

The reason: SourceLoop stores the visitor id (`_sl_aid`) in a cookie on your **root domain** (e.g. `.yoursite.com`). Cookies are shared across subdomains, so `www.` and `app.` already see the same id. They are **not** shared across different root domains, so those need an explicit hand-off.

> **Requirement for any cross-site setup:** SourceLoop must be installed on **both** sites using the **same `websiteId`**. If the two sites have different website ids, they can never be the same visitor.

## Case 1, subdomains (automatic, nothing to do)

`www.yoursite.com` and `app.yoursite.com` (or `marketing.`, `dashboard.`, etc.) share identity automatically as long as both have the SourceLoop script/SDK with the **same `websiteId`**. No `crossDomains`, no special links. This is the common SaaS setup and it just works.

Verify: load `www.yoursite.com`, note the `_sl_aid` cookie value (DevTools, Application, Cookies), then go to `app.yoursite.com`, the `_sl_aid` value is the same.

## Case 2, different root domains

Example: marketing site on `yourbrand.com` (Webflow), app on `yourapp.io` (Next.js). Because the cookie cannot cross root domains, SourceLoop passes the id in the URL when the visitor clicks through, and the destination adopts it on arrival.

You configure a list of **other domains you own** (`crossDomains`). When a visitor clicks a link from your site to one of those domains, SourceLoop appends identity params (`?sl_id=...&sl_sid=...&sl_ts=...`) to the link. The destination site (also running SourceLoop with the same `websiteId`) reads those params on load, adopts the same visitor id, and strips the params from the URL.

### 2a. Script tag (Webflow, WordPress, no-code)

Add `crossDomains` to the config **on both sites**, listing the *other* domain. Replace `YOUR_WEBSITE_ID` with the **same** id on both.

On `yourbrand.com` (marketing):

```html
<script>
  window.SourceLoopConfig = {
    websiteId: 'YOUR_WEBSITE_ID',
    crossDomains: ['yourapp.io'],   // the app domain you link to
  };
</script>
<script async src="https://app.sourceloop.ai/tracking-v3.js"></script>
```

On `yourapp.io` (app):

```html
<script>
  window.SourceLoopConfig = {
    websiteId: 'YOUR_WEBSITE_ID',
    crossDomains: ['yourbrand.com'],  // link back to marketing
  };
</script>
<script async src="https://app.sourceloop.ai/tracking-v3.js"></script>
```

That is it. Now when someone clicks a normal link on `yourbrand.com` that points to `yourapp.io` (e.g. your "Start free trial" button), SourceLoop automatically appends the identity params, and `yourapp.io` adopts them. You do not change your links, this happens on click.

You can list multiple domains: `crossDomains: ['yourapp.io', 'docs.yourbrand.io', 'community.yourbrand.com']`.

### 2b. Coded app / SDK

With the SDK, link-click decoration also works, configure `crossDomains` when bundling the tracker (via the same `SourceLoopConfig` or your init). In addition, the SDK gives you two helpers for cases the click-listener cannot catch, **programmatic navigation** (JS redirects, `window.location`, or `<form action>` to another domain):

```ts
import { getTrackingParams, buildCrossDomainUrl } from '@sourceloop-analytics/sdk';

// Easiest: build a ready-to-use URL with the params appended.
window.location.href = buildCrossDomainUrl('https://yourapp.io/signup');
// → https://yourapp.io/signup?sl_id=...&sl_sid=...&sl_ts=...

// Or get the raw params to attach yourself (e.g. as hidden form fields):
const params = getTrackingParams();
// → { sl_id: '...', sl_sid: '...', sl_ts: '...' }
```

The destination (`yourapp.io`) must be running SourceLoop with the same `websiteId`; it adopts the params automatically on page load, no code needed there.

## How the hand-off works (so you can reason about it)

1. Visitor on `yourbrand.com` clicks a link to `yourapp.io` (a configured cross-domain).
2. SourceLoop appends `?sl_id=<visitorId>&sl_sid=<sessionId>&sl_ts=<timestamp>` to the destination URL.
3. On `yourapp.io`, SourceLoop reads those params **before** computing identity, adopts the same `sl_id` as the visitor id, and removes the params from the address bar.
4. Both domains now report the **same visitor**, so the marketing source carries through to the app.

**Built-in safety:** the timestamp (`sl_ts`) must be within about 5 minutes, so stale/shared links cannot hijack an identity, and only valid id formats are accepted.

## Common mistakes

- **Different `websiteId` on the two sites.** They can never merge. Use the same id everywhere.
- **`crossDomains` on only one site.** Put it on both, and make sure the destination has SourceLoop installed so it can adopt the params.
- **Expecting subdomains to need `crossDomains`.** They do not. Only different *root* domains do. Adding `crossDomains` for subdomains is unnecessary (harmless, but not needed).
- **Programmatic redirects without `buildCrossDomainUrl`.** The automatic decoration only fires on real `<a>` clicks. For `window.location = ...`, `router.push` to another domain, or form posts, use `buildCrossDomainUrl()` / `getTrackingParams()`.
- **Listing a domain you do not control.** `crossDomains` should only be your own properties; the destination must run SourceLoop to adopt the id.

## Verify

1. On `yourbrand.com`, note the `_sl_aid` cookie value.
2. Click your link to `yourapp.io`. The destination URL should briefly contain `?sl_id=...` (then SourceLoop cleans it).
3. On `yourapp.io`, check the `_sl_aid` cookie, it should now match the value from `yourbrand.com`.
4. In the SourceLoop dashboard, the journey shows as a single visitor spanning both domains, with the marketing source preserved.

## Frequently Asked Questions

### Do subdomains need any cross-domain setup?

No. www.yoursite.com and app.yoursite.com share identity automatically as long as both run SourceLoop with the same websiteId. The visitor id cookie (_sl_aid) is stored on your root domain, and cookies are shared across subdomains, so nothing extra is needed. Adding crossDomains for subdomains is unnecessary (harmless, but not needed).

### How do I keep one identity across two different root domains?

Install SourceLoop on both sites with the same websiteId, then add a crossDomains list to the config on each site naming the other domain. When a visitor clicks a link from one site to the other, SourceLoop appends identity params to the URL and the destination adopts them on load. Put crossDomains on both sites, not just one.

### My redirect is a JS redirect or a form post, not a normal link. Does decoration still work?

The automatic decoration only fires on real anchor clicks. For programmatic navigation (window.location, router.push to another domain, or a form action to another domain), use buildCrossDomainUrl(url) to get a ready URL with the params appended, or getTrackingParams() to attach them yourself as hidden fields.

### Can a shared or stale link hijack someone's identity?

No. The handoff includes a timestamp (sl_ts) that must be within about 5 minutes, and only valid id formats are accepted, so stale or shared links cannot adopt an identity.
