# Tracker JavaScript API reference

> Tracker JavaScript API reference: track custom events, send pageviews, add properties to every hit, opt out, use ready() and queue calls before pa.js loads.

Once loaded, the tracker exposes `window.privatus` (or the name in
[`data-namespace`](/docs/tracker/attributes)).

## `privatus.track(name, props?, callback?)`

Sends a custom event.

```js
privatus.track('Signup', { plan: 'pro', seats: 3 })
privatus.track('Purchase', { revenue: 49.99, currency: 'EUR', sku: 'A1' })
```

- `name`: up to 120 characters, any language. Trimmed and Unicode
  normalised (NFC). Case is kept.
- `props`: up to 30 properties of type string (≤ 500 characters), number,
  boolean or array of strings. `revenue` and `currency` are reserved. See
  [Properties](/docs/events/properties) and [Revenue](/docs/events/revenue).
- `callback`: called once the request has completed, or straight away if
  nothing is sent (opted out, excluded, debug mode). It's called after the
  first attempt and never waits for a
  [retry](/docs/tracker/payload#retries). The tracker adds no timeout of
  its own, so add one when you navigate away:

```js
link.addEventListener('click', (e) => {
  e.preventDefault()
  let done = false
  const go = () => { if (!done) { done = true; location.href = link.href } }
  privatus.track('Outbound click', { domain: link.hostname }, go)
  setTimeout(go, 300)
})
```

Requests are sent with `keepalive`, so in most cases you don't need to
wait at all: the hit survives the navigation.

You can pass the callback as the second argument when there are no
properties: `privatus.track('Logout', done)`.

## `privatus.pageview(options?)`

Sends a pageview for the **current URL**, even if the path hasn't changed.

```js
privatus.pageview()
privatus.pageview({ props: { variant: 'b' } })
```

- `options.props`: properties for this pageview only.
- The URL is always `location.href` (after exclusions, masks, canonical and
  hash handling). To record a different path, change the URL first with
  `history.pushState`, or use a [mask](/docs/tracker/exclusions-and-masking).

Use it with [manual mode](/docs/tracker/manual-mode) or for virtual
pageviews such as steps of a wizard that change the URL.

## `privatus.props(object)`

Adds properties to every later pageview and event on this page load.
Calls merge. Set a key to `null` to stop sending it.

```js
privatus.props({ theme: 'dark', logged_in: true })
```

Properties live in memory only and are gone on the next full page load.

## `privatus.optOut()`, `privatus.optIn()`, `privatus.isOptedOut()`

Stop or resume tracking in this browser. See [Opt-out](/docs/tracker/opt-out).

## `privatus.ready(fn)`

Runs `fn` once the tracker has loaded. Most useful with the queue stub,
where the call is queued until `pa.js` arrives.

```js
privatus.ready(() => console.log('Privatus Analytics is loaded'))
```

## Queue stub

`pa.js` loads with `defer`, so it isn't available to inline scripts that
run earlier. Put this stub **before** the tracker tag. Calls made before
the tracker loads are queued and replayed in order.

```html
<script>
  window.privatus = window.privatus || { q: [] };
  ['track', 'pageview', 'props', 'optOut', 'optIn', 'ready'].forEach(function (m) {
    window.privatus[m] = window.privatus[m] || function () {
      window.privatus.q.push([m, Array.prototype.slice.call(arguments)]);
    };
  });
</script>
<script defer src="https://privatusanalytics.com/js/pa.js" data-site="pa_YOURSITEID"></script>
```

The queue format is `window.<namespace>.q = [[method, [args…]], …]`.
`isOptedOut()` returns a value, so it can't be queued. Call it after
`ready()`.

If you use a custom `data-namespace`, use the same name in the stub.

## TypeScript

The npm package `@privatus/tracker` ships types and a loader (`init()`,
`getClient()`) that installs the stub for you. See the
[React guide](/docs/install/react).

> **Warning:** `@privatus/tracker` is not published on npm yet. Until it is,
> don't install a package with this name from the public registry,
> because anyone could have published it. Use the type declarations
> below instead.

```ts
declare global {
  interface Window {
    privatus?: {
      track(name: string, props?: Record<string, string | number | boolean | string[] | null>, callback?: () => void): void
      pageview(options?: { props?: Record<string, unknown> }): void
      props(props: Record<string, unknown>): void
      optOut(): void
      optIn(): void
      isOptedOut(): boolean
      ready(fn: () => void): void
    }
  }
}
```
