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

# sa.js reference

> What the tracking snippet reads from the page, and the window.Affixo functions you can call.

`sa.js` is the browser snippet you paste into your site's `<head>`. It reads the
referral code from the URL, remembers the visitor, records the click and gives
your page a small `window.Affixo` object to call. For where to paste it, see
[Integrate Affixo tracking](https://docs.affixo.dev/integrate.md).

```html theme={null}
<script async src="https://go.affixo.dev/sa.js?w=YOUR_PUBLIC_KEY"></script>
```

`?w=` is your workspace's public key, from **Settings → Tracking & install**.
It's meant to be public, and it can't create a payable sale (see `track`
below).

## Referral parameters

On every page load the snippet looks for a referral code in the URL. It checks
these query parameters in this order and uses the first one it finds:

| Parameter | Where it comes from |
| - | - |
| `ref`, `sa_ref` | Affixo's own links |
| `fp_ref`, `fpr`, `via`, `deal`, `_from`, `_by`, `_go` | Link formats used by FirstPromoter and similar platforms, so links your affiliates already shared keep working after you switch |
| `am_id` | HighLevel's Affiliate Manager |

So `https://yoursite.com/?via=jane` and `https://yoursite.com/?ref=jane` credit
the same affiliate.

**Codes match whatever their case.** `?ref=Jane20`, `?ref=jane20` and
`?ref=JANE20` all find the affiliate whose code is `jane20`. The same goes for
codes sent to `/v1/track` and for manual codes typed at checkout.

When there's no code in the URL, the snippet uses the code it stored from an
earlier visit. Failing that, it looks for a referral cookie left on your domain
by FirstPromoter, Rewardful or Tolt, so a visitor who clicked an affiliate link
before you switched to Affixo is still credited.

## Sub-IDs

Affiliates can tag their own traffic, for example one link per ad, with a
sub-ID: `?ref=jane&sub_id=fb-ad-3`. The snippet reads it from the first of
these it finds:

`sub_id`, `sa_sub_id`, `tid`, `fp_sid`

The sub-ID travels with the click that carried it. A new referral link replaces
the stored sub-ID (or clears it, if the new link has none). Sub-IDs show up in
**Reports → Sources**.

## Test links

Add `?_sa_test=1` (or `?test_mode=1`) to a link to run a test. For the next 30
minutes, the visit and the first conversion from that browser are recorded as
test data, which never reaches payouts, alerts or reports. The flag clears
itself after that first conversion.

## `window.Affixo`

The snippet defines three functions once it loads. Because the script is
`async`, guard your calls (`if (window.Affixo) ...`) or run them after the page
has loaded.

### `Affixo.ready()`

Returns a promise that resolves to the visitor id once the snippet has
recorded this visit. Use it when you need the id and want to be sure it's
there, for example right before creating a Stripe Checkout Session.

```js theme={null}
const visitorId = await window.Affixo.ready();
```

### `Affixo.getVisitorId()`

Returns the visitor id straight away, or `null` if this visitor hasn't been
recorded yet. It's the value to pass to Stripe as `client_reference_id`, or to
your backend for `POST /v1/track/sale`.

```js theme={null}
const visitorId = window.Affixo.getVisitorId();
```

### `Affixo.track(event, data)`

Reports a conversion from the page. It waits for `ready()` itself, and returns
a promise. `event` is `'signup'` or `'sale'`; anything else is refused.

```js theme={null}
window.Affixo.track('signup', {
  email: 'sam@example.com',
  name: 'Sam Buyer',
});
```

`data` is optional. The fields Affixo reads:

| Field | Meaning |
| - | - |
| `email` | The person who signed up or bought. Shown on the affiliate's Referrals list, and used to match a later Stripe payment to the same affiliate. |
| `name` | Their name. `first_name` and `last_name` (or `firstName` / `lastName`) and `full_name` work too. |
| `external_id` or `order_id` | Your id for this event. Repeated calls with the same id are recorded once. |
| `manual_code` | A code the customer typed, when manual codes are turned on. |
| `amount`, `currency` | Only used for test sales (see below). |

> [!WARNING]
>
> **A browser sale never books money.** On a live site, `track('sale', ...)`
> is recorded as an attributed signup with no amount, whatever amount you
> pass, because anyone can read your public key from the page. Real sales come
> from your connected Stripe account, or from your server with
> [`POST /v1/track/sale`](https://docs.affixo.dev/tracking-overview) and a secret API key. In test
> mode a browser sale does book as a test sale, so you can check your setup.

A signup without an `external_id` is recorded once per visitor, so reloading a
thank-you page doesn't add more.

## HighLevel

For HighLevel funnels, load `sa.highlevel.js` instead of `sa.js`, with the same
`?w=` key. It does everything above, and also captures the email and name from
HighLevel order forms and opt-ins and calls `track('signup')` for you. When
the payment later reaches your connected Stripe account, Affixo matches it to
the same affiliate by that email. **Settings → Tracking & install** shows the
HighLevel version of the snippet with your key already in it.


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