> ## Documentation Index
> Fetch the complete documentation index at: https://www.referly.so/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracker JavaScript API

> Reference for the Referly Atlas tracker in the browser: the script tag and its data-atlas attributes, referlyAtlas.track, identify, consent and getVisitorId, the command queue, event names and properties, product markup for ViewContent, Stripe elements and debugging.

The Atlas tracker is the Referly tracking script with Atlas mode switched on. Once it runs on a page it records page views, carts, checkouts and orders on its own, and it exposes `window.referlyAtlas` so your code can send conversions, identify the visitor and pass consent. This page is the reference for that object. For what to install where, see [Tracking script](/docs/help-center/atlas/setup/tracking-script.md); for events sent from a server, see the [Events API](/docs/help-center/atlas/developers/events-api.md).

## Load the tracker

Shopify loads the tracker through the Referly app's theme app embed, and WooCommerce through the Referly plugin. Every other site pastes the snippet from **Store settings**, under **Tracking and connection**, before the closing `head` tag of every page:

```html theme={null}
<script
  async
  src="https://www.referly.so/affiliate-tracker.js"
  data-affiliate
  data-program-id="YOUR_STORE_ID"
  data-atlas="1">
</script>
```

**data-affiliate** `attribute` *required*

The marker the script uses to find its own tag. Without it the script does nothing. Frameworks that need a value accept `data-affiliate=""`.

**data-program-id** `string` *required*

Your store id, already filled in on the snippet Atlas gives you.

**data-atlas** `'1'` *required*

Turns Atlas mode on for this tag. The snippet Atlas generates always carries it.

**data-atlas-debug** `'1'`

Logs every decision to the browser console with the prefix `[Referly Atlas]`. See [Debugging](#debugging).

**data-atlas-autodetect** `'1'`

On every form submit, reads an email input (`type="email"`, or a name containing "email") and a phone input (`type="tel"`, or a name containing "phone") and calls `identify` with them. Opt in; off by default.

**data-atlas-tcf-vendor** `string`

Your IAB TCF v2 vendor id. With a TCF banner on the page, the tracker reads purpose 1 and, when this is set, the consent given to your vendor as well.

When the page loads, the tracker:

1. Fetches the store's configuration from `GET https://www.referly.so/api/atlas/config?programId=YOUR_STORE_ID`:

   ```json theme={null}
   {
     "enabled": true,
     "cookieDays": 400,
     "consentMode": "balanced",
     "attributionWindowDays": 30,
     "fingerprintEnabled": true,
     "collect": "https://www.referly.so/api/atlas/collect",
     "debug": false,
     "googleAds": []
   }
   ```

   `enabled` is `false` for a store that is not tracking yet or has been disabled; the API then becomes a no-op and `getVisitorId()` returns `null`. `googleAds` lists the store's enabled Google Ads pixels, each with the event filter rules the tracker applies before it fires a conversion in the browser; it is empty on Shopify, where the custom pixel fires them. The configuration is cached in the browser and refreshed in the background once it is older than 10 minutes, so a pixel or filter change reaches visitors within about 15 minutes.

2. Sets the visitor cookie `_rly_vid` on your root domain for as many days as the store's **Visitor cookie** setting allows (400 by default), and a session id in `_rly_sid`. A session ends after 30 minutes without activity.

3. Records a PageView. The first event of a session carries the landing URL, the referrer, every `utm_*` parameter plus `utm_id`, `adset_id`, `ad_id` and `placement`, the click ids (`gclid`, `gbraid`, `wbraid`, `fbclid`, `msclkid`, `ttclid`, `twclid`, `li_fat_id`, `rdt_cid`, `sccid`, `epik`, `tblci`, `gad_source`) and the platform cookies it can read (`_fbp`, `_ttp`, `_scid`, `_rdt_uuid`, `_shopify_y`).

4. Replays any queued commands, and records a new PageView on each single-page navigation.

Events travel in batches of up to 25 per request (64 KB at most). Purchases, checkouts and conversions are flushed at once; the rest go within about a second.

## track(name, props)

```js theme={null}
referlyAtlas.track("AddToCart", { value: 59, currency: "USD", contents: [{ id: "SKU-123", quantity: 1 }] });
```

Records one event. Unknown names are ignored (and logged in debug mode). Names are matched after lowercasing and removing anything that is not a letter, so `Sign up`, `sign_up` and `SignUp` all resolve.

| Event | Aliases |
| - | - |
| `ViewContent` | `viewcontent` |
| `AddToCart` | `addtocart` |
| `InitiateCheckout` | `initiatecheckout` |
| `AddPaymentInfo` | `addpaymentinfo` |
| `Purchase` | `purchase` |
| `Search` | `search` |
| `Lead` | `lead`, `generatelead` |
| `CompleteRegistration` | `completeregistration`, `signup`, `register`, `registration` |
| `StartTrial` | `starttrial`, `trial` |
| `Subscribe` | `subscribe`, `subscription` |

`PageView` is automatic and cannot be sent. `ViewContent` is also detected on its own on Shopify and WooCommerce product pages and on any page with JSON-LD, OpenGraph or `data-rly-*` product markup (see [Product markup](#product-markup)).

### Properties for site and checkout events

**value** `number`

The amount of the event. Strings that parse as numbers are accepted.

**currency** `string`

Three-letter code, uppercased for you.

**orderId** `string`

Your order id, for `Purchase`. It deduplicates the order against the copy that arrives from your platform or server. Up to 128 characters.

**contents** `array`

Up to 50 items. Each needs an id (`id`, `productId` or `sku`, with `name` as a last resort) and may carry `variantId`, `sku`, `name`, `price`, `currency` and `quantity` (defaults to 1). Items without an id are dropped.

**cartToken** `string`

The cart token, when your platform has one.

**checkoutToken** `string`

The checkout token, when your platform has one.

### Properties for conversions

`Lead`, `CompleteRegistration`, `StartTrial` and `Subscribe` take the same `value` and `currency`, plus the person:

**id** `string`

The conversion id. It deduplicates repeats, such as a reload or the same trial also reported by Stripe. `eventId`, `userId` or `subscriptionId` are read when `id` is missing.

**email** `string`

Lowercased and trimmed in the browser, hashed by Atlas before it reaches a platform.

**phone** `string`

The person's phone number.

**firstName** `string`

The person's first name.

**lastName** `string`

The person's last name.

**externalId** `string`

Your own customer id. `userId` is accepted as an alias.

Passing any of these on a conversion also calls `identify` for you.

> [!WARNING]
>
> `Purchase` does not read `email` or the other identity fields from its props. Call `referlyAtlas.identify(...)` before `track("Purchase", ...)`, or the order goes out with the visitor's cookies and click ids only.

A purchase sent from the browser waits 60 seconds for the matching order from your platform or server (same order id) and is enriched with it, so send the same id in both places.

```js Purchase theme={null}
referlyAtlas.identify({ email: "jane@example.com", firstName: "Jane", lastName: "Doe" });
referlyAtlas.track("Purchase", {
  orderId: "1001",
  value: 129.0,
  currency: "USD",
  contents: [{ id: "SKU-123", variantId: "V-9", name: "Trail jacket", price: 129.0, quantity: 1 }],
});
```

```js Lead theme={null}
referlyAtlas.track("Lead", {
  id: "lead_7f3a",
  email: "jane@example.com",
  firstName: "Jane",
});
```

```js Start trial theme={null}
referlyAtlas.track("StartTrial", {
  id: "sub_1Q8xVe",
  userId: "user_123",
  email: "jane@example.com",
  value: 0,
  currency: "USD",
});
```

The tracker also dispatches a DOM event named `referly-atlas:event` on `document` for every call, with `detail.name` and `detail.props`.

## identify(traits)

```js theme={null}
referlyAtlas.identify({ email: "jane@example.com", phone: "+15555550123", externalId: "user_123" });
```

Attaches a person to the current visitor. Accepted traits: `email`, `phone`, `firstName`, `lastName` and `externalId` (`userId` works too). Empty or malformed values are ignored; an email must contain `@`. Nothing is hashed in the browser; Atlas hashes the email, phone, names and external id with SHA-256 before anything reaches an ad platform.

Where consent is required and not yet granted, the identity is held and sent as soon as the visitor consents. Call it at sign up, sign in and checkout, and on any page where you know who the visitor is. `data-atlas-autodetect="1"` does it for you on form submits.

## consent(...)

```js theme={null}
referlyAtlas.consent({ marketing: true, analytics: true });
```

Tells the tracker what the visitor answered on your banner. An explicit choice is remembered in the browser for 182 days, so a banner that does not re-signal on every page only needs to call it once. Calling it again (for example with `marketing: false`) overwrites the stored choice. Signals from Shopify's Customer Privacy API and from an IAB TCF v2 banner still win when they fire.

What the choice does depends on the store's **Consent mode** in Store settings:

| Consent mode | Without consent | After `marketing: true` |
| - | - | - |
| Strict | Nothing is sent for the visitor. | Events and identifiers flow. |
| Balanced (default) | Visitors from the EEA, UK and Switzerland wait, or are skipped, according to **Visitors with unknown consent**. Everyone else is tracked. | Events and identifiers flow. |
| Off | Everything is sent, except after an explicit decline. | No change. |

The tracker dispatches `referly-atlas:consent` on `document` whenever the effective consent changes, with `detail.marketing`, `detail.analytics` and `detail.implicit` (`true` when no answer was needed). Google Ads conversions fired by the tracker follow Google Consent Mode: denied by default in consent regions until marketing consent is granted, unless the store's consent mode is Off.

## getVisitorId() and the queue

```js theme={null}
const vid = referlyAtlas.getVisitorId(); // a UUID, or null
```

Returns the visitor id, the value of the `_rly_vid` cookie. Before the tracker has loaded it reads the cookie (or its local storage copy) directly; it returns `null` when Atlas is disabled for the store or the visitor has no id yet. Use it to tie a server-side event to the visit: send it as `visitorId` to the [Events API](/docs/help-center/atlas/developers/events-api.md), or put it in your own checkout metadata as `referly_vid` for the billing integrations (see [Connect your data](/docs/help-center/atlas/setup/connect-your-data.md)).

The script loads asynchronously, so code that runs before it can queue commands. Two styles work:

```js Queue array theme={null}
window.referlyAtlasQ = window.referlyAtlasQ || [];
window.referlyAtlasQ.push(["track", "ViewContent", { value: 49, currency: "USD" }]);
```

```js Stub object theme={null}
window.referlyAtlas = window.referlyAtlas || [];
window.referlyAtlas.push(["identify", { email: "jane@example.com" }]);
```

Queued commands run after the page view, in order. `referlyAtlas.loaded` is `true` once the tracker has finished loading and the real methods are in place, and `referlyAtlas.push([...])` keeps working after that.

## Product markup

`ViewContent` is recorded automatically when the tracker can tell which product the page shows. It reads Shopify and WooCommerce product pages, JSON-LD `Product` objects and OpenGraph product tags (`og:type`, `product:price:amount`, `product:price:currency`). On any other page, mark one element up:

```html theme={null}
<div
  data-rly-product-id="SKU-123"
  data-rly-variant-id="V-9"
  data-rly-sku="SKU-123"
  data-rly-product-name="Trail jacket"
  data-rly-price="129.00"
  data-rly-currency="USD">
</div>
```

**data-rly-product-id** `string` *required*

The product id. Without it the element is ignored.

**data-rly-variant-id** `string`

The variant id, when you sell variants.

**data-rly-sku** `string`

The SKU.

**data-rly-product-name** `string`

The product name.

**data-rly-price** `number`

The unit price.

**data-rly-currency** `string`

Three-letter currency code.

Product views are deduplicated per page, so a single-page app that re-renders the same product does not record it twice.

## Stripe elements

Stripe Checkout runs on Stripe's domain, so the visit is handed over in the checkout's client reference id. The tracker sets it to `rly_<visitorId>` on every Stripe Buy Button and Pricing Table on the page (the `client-reference-id` attribute) and on every payment link to `buy.stripe.com` or `donate.stripe.com` (a `client_reference_id` query parameter), including elements added later by a single-page app and links clicked before they were tagged. A reference your site set itself is never replaced, so affiliate ids passed there keep working.

Under Strict consent mode nothing is tagged until the visitor consents. For your own Checkout Sessions, pass the visitor id as `referly_vid` in the session metadata instead; the Stripe integration reads both. See [Connect your data](/docs/help-center/atlas/setup/connect-your-data.md).

## Debugging

Add `data-atlas-debug="1"` to the script tag, or turn on [debug mode](/docs/developer-documentation/tracking/debug-mode.md) for the program, and open the browser console. Every line starts with `[Referly Atlas]`:

| Line | Meaning |
| - | - |
| `active` with the program, visitor and session ids and the consent mode | Atlas mode is running on this page. |
| `disabled for` and the store id | The configuration returned `enabled: false`: the store is not live. |
| `unknown event name` | `track` was called with a name that is not in the table above. |
| `consent restored` | A stored consent choice was applied. |
| `api call failed` | A method threw; the error follows. |

Then check the other end. **Live activity** in Atlas shows every event as it arrives, with its Status and the reason it was Skipped, Filtered or Deduped, and **Test connection** in Store settings confirms the script was seen.

> **Purchases arrive without a customer**
>
> `Purchase` ignores identity props. Call `identify` first.

> **Nothing is sent and getVisitorId() returns null**
>
> The store is not live, so the configuration came back disabled. Finish the setup wizard and make sure a plan is active, then reload with the console open and look for `active`.

> **Events show as Skipped: Consent unknown**
>
> Under Balanced mode a visitor from a consent region has not answered the banner, and the store skips unknown consent. Wire your banner to `referlyAtlas.consent(...)`, or switch **Visitors with unknown consent** to **Send the event** if you accept the responsibility.

> **Events from a staging site never arrive**
>
> **Allowed domains** in Store settings limits which hosts may send events. Add the staging domain, or leave the list empty while testing.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.