# JavaScript API

The snippet gives every page one global object, `window.tryfast`. Use it to track conversions, pass consent, set visitor attributes, trigger experiments and debug. This page is the reference; to install the snippet see [Install the snippet](install.md).

## Calling TryFast before it has loaded

Until the script has loaded, `window.tryfast` is an array that queues your calls. Push either an array of method name and arguments, or a function that receives the API:

```js
window.tryfast = window.tryfast || [];
window.tryfast.push(["track", "purchase", { revenue: 79.90, currency: "EUR", orderId: "A-10442" }]);
window.tryfast.push(function (tf) {
  console.log("TryFast", tf.version, tf.getActive());
});
```

When the script has loaded it replaces the array with the API object and runs the queued calls in order. After that, `push` runs a call immediately, so `tryfast.push([...])` works at any time and is the safest form in code that may run early. Methods never throw: invalid arguments are ignored and reported in the console when [debugging](#debugging) is on.

## Methods

### track(goalKey, options)

Records a conversion for the goal with that key (the goal's key, for example `purchase`; see [Goals](goals.md)).

```js
tryfast.track("purchase", { revenue: 79.90, currency: "EUR", orderId: "A-10442", props: { plan: "pro" } });
tryfast.track("signup");
```

| Option | Rules |
|---|---|
| `revenue` | A number from 0 to 1,000,000,000. Negative values are dropped. |
| `currency` | ISO 4217 code such as `EUR`. Defaults to the project currency. Revenue in another currency is stored but left out of revenue results. |
| `orderId` | Up to 128 characters. A second conversion with the same order id and goal within 90 days is dropped, so reloading a thank-you page never counts twice. |
| `props` | Up to 10 keys (lowercase letters, digits and `_`, up to 40 characters) with string (up to 200 characters), number or boolean values. Shown in the event list; not used in results. Never put names or email addresses here. |

A `track` call with a key that matches no goal yet is kept, so you can create the goal after you see the event arrive.

### set(key, value)

Sets a visitor attribute, stored in the visitor's browser and usable in `attribute` [targeting conditions](experiments/targeting.md). `null` deletes it. Attributes never leave the browser.

```js
tryfast.set("plan", "pro");
tryfast.set("cart_value", 120);
```

### identify(userId)

Stores your user id as the attribute `user_id`. It does not change how visitors are split. Ignored in the `exempt` consent mode.

### consent(granted)

Tells TryFast your visitor's cookie banner decision ([Privacy and consent](privacy-and-consent.md)). `true` stores the visitor id, sends buffered events and, with the default settings, applies experiments on the same page view. `false` clears TryFast's storage, drops buffered events and stops tracking. After `true`, later page views count as consented until `false`.

```js
tryfast.push(["consent", true]);
```

### optOut()

Excludes this browser: sets the cookie `tf_oo` for 390 days, clears TryFast's other storage, and stops tracking and experiments on later page views. The URL parameter `?tf_optout=1` does the same.

### event(name)

Fires a custom event. Experiments with the trigger `custom_event` of that name activate, and `custom_event` audience conditions see it.

```js
tryfast.event("opened_cart");
```

### activate(experimentKey)

Activates an experiment whose trigger is `manual`, by experiment key or id.

### variation(experimentKey)

Returns the key of the variation this visitor is assigned to (`"control"`, `"b"`, ...), or `null`.

### getActive()

Returns the experiments active on this page view: `[{ experiment: { id, key }, variation: { id, key } }]`.

### refresh()

Re-evaluates targeting now. Route changes in single-page apps are detected automatically; call `refresh()` when your app renders new content without changing the URL.

### onElement(selector, callback, options)

Calls `callback(element)` for every element matching `selector`, now and as they appear later. Options: `persist` (keep watching, default true), `timeout` (ms). Returns a function that cancels.

### when(condition, callback, options)

Calls `callback()` once `condition()` returns true, checking every `interval` ms (default 50) until `timeout` ms (default 10,000). Returns a function that cancels.

### state()

Returns what the script decided on this page view, for debugging: version, project key, consent state, visitor id (null before consent), visit number, preview, and for each experiment whether pages, trigger and audience matched, the assigned variation and its source (`sticky`, `hash`, `preview` or `shipped`), why it was excluded, whether it applied and any errors. Also the goals fired, the event queue and timings.

### Properties

`tryfast.version` is the engine version; `tryfast.ready` is true after the first evaluation of the page.

## Window events

TryFast dispatches `CustomEvent`s on `window`, so any tool on your page can react:

| Event | When | `event.detail` |
|---|---|---|
| `tryfast:ready` | After the first evaluation of a page view | `{version, bundle, active, timedOut, loadMs}` |
| `tryfast:exposure` | An experiment became active, once per experiment per page view | `{experiment: {id, key}, variation: {id, key}, source}` (`sticky`, `hash` or `preview`) |
| `tryfast:conversion` | A goal fired | `{key, revenue?, currency?, orderId?}` |
| `tryfast:aborted` | The script or one experiment stopped | `{reason, experiment?}` |

```js
window.addEventListener("tryfast:exposure", function (e) {
  console.log(e.detail.experiment.key, "shows", e.detail.variation.key);
});
```

Abort reasons: `disabled_param`, `unknown_project`, `killed`, `frozen`, `foreign_domain`, `bot`, `prerender_cancelled`, `opted_out`, `consent_denied`, `timeout`, `storage_unavailable`, `code_error`, `preview_invalid`, `redirect_off_domain`.

When the project setting **dataLayer events** is on and your page already has a `window.dataLayer` array (TryFast never creates one), exposures and conversions are also pushed there ([Integrations](integrations.md)).

## URL parameters

All `tf_*` parameters are removed from the URL TryFast uses for targeting, goals and page views.

| Parameter | Effect |
|---|---|
| `tf_preview=<token>` | Shows a variation from a [preview link](experiments/qa-and-preview.md) for this tab. `tf_preview=off` leaves the preview. |
| `tf_editor=<token>` | Opens the [visual editor](experiments/visual-editor.md). |
| `tf_debug=1` | Logs every decision to the console for this tab; `tf_debug=0` turns it off. |
| `tf_disable=1` | Switches TryFast off for this page view; `tf_disable=session` for the whole tab. |
| `tf_optout=1` | Excludes this browser, like `optOut()`; `tf_optout=0` undoes it. |

Preview, editor, debug and opt-out parameters work before consent: they write only to the tab's session storage (opt-out writes its one restricting cookie), and preview traffic is never counted.

## Storage

The script stores a random visitor id in the first-party cookie `tf_vid` (on your registrable domain, at most 390 days from its creation, never extended by visits) and its working state in `localStorage` and `sessionStorage` keys starting with `tf_`. With the default consent mode nothing is stored before consent. [Privacy and consent](privacy-and-consent.md) lists every key with its purpose and lifetime.

## Code in variations

Variation JavaScript, experiment-wide JavaScript and the project's global script are written as the **body** of a function that TryFast calls with two arguments:

```js
// runs as function (tf, ctx) { ...your code... }
var banner = document.createElement("div");
banner.textContent = "Free shipping on every order";
document.body.prepend(banner);
ctx.cleanup(function () { banner.remove(); });
```

| `ctx` member | Meaning |
|---|---|
| `ctx.experiment`, `ctx.variation` | `{id, key}` of the running experiment and variation (`null` for the global script; `variation` is `null` in experiment-wide code). |
| `ctx.cleanup(fn)` | Registers `fn` to run when the variation is reverted (a single-page app leaves the page, or a preview ends). |
| `ctx.onElement`, `ctx.when` | Like `tryfast.onElement` and `tryfast.when`, cancelled automatically on revert. |
| `ctx.activate()` | In trigger code only: activate the experiment later on this page view. |

- Variation and experiment-wide code: the return value is ignored. An exception stops that experiment for the page view (abort reason `code_error`) and leaves your page as it was.
- [Targeting conditions](experiments/targeting.md) of type `js`: return `true`, `false`, or a promise of either. Pending results count as false after the page's DOMContentLoaded plus 3 seconds.
- Triggers of type `js`: return `true` to activate now, or call `ctx.activate()` later on the page view.

Code is checked when you save it. A syntax error answers with the exact line and column, and nothing is published. TryFast never uses `eval` or `new Function`: your code is built into the project's script ahead of time, which keeps it compatible with a strict Content Security Policy.

## Debugging

Open any page with `?tf_debug=1`. The console then shows the consent state, each experiment, each page rule and audience condition with the actual value, the trigger state, the assignment and its source, the changes applied (and selectors that matched nothing), goals fired and batches sent. `tryfast.state()` returns the same information as an object. `?tf_disable=1` shows the page without TryFast for comparison.
