# Scripts

> Optional browser script that adds browser-side signals and establishes a session up front.

Source: https://docs.centinelanalytica.com/install/scripts

## Overview

The Centinel collector script is an **optional** addition to your site. It runs in the browser, sets the `_centinel` cookie, and feeds browser-side signals back to the validator.

Server-only validation works without it, on a smaller signal set. The script adds the browser and network signals that no amount of server-side data can reveal, which is what catches automation driving a real browser. The `_centinel` cookie it sets gives the validator a session key before the first challenge.

The interstitial challenge does not need this script. The validator injects its own copy into the
interstitial it serves, so challenges work on a server-only install too.

## Add the script

Drop this in your page `<head>` and replace `SITE_KEY` with your site key (from the [Dashboard](https://docs.centinelanalytica.com/install/dashboard.md)).

```html
<html>
  <head>
    <script src="https://collector.centinelanalytica.com/script.js?site_key=SITE_KEY" defer></script>
    <!-- Rest of the head -->
  </head>
  <body>
    <!-- Page content -->
  </body>
</html>
```

Keep the `defer`. Several signals read the DOM, so a script that runs before `<body>` exists
collects less and weakens detection. `defer` also keeps the parser from blocking on the download.

## Where to install it

Add the script to any page where a protected action can be triggered:

* Login forms (or any page hosting a login modal)
* Signup and password-reset flows
* Checkout and payment pages
* Gated content: premium articles, paid downloads, members-only areas
* Comment, review, and other user-generated-content submission

> **Single-page apps:** Install the script once in the global layout, not per route, to avoid double-loading. It collects once per full document load and deliberately does not hook client-side navigation, so a route change does not re-run collection. That is fine: the session it established on first load carries across routes.

## Verify

Load a page and check three things in your browser's dev tools:

* The request to `collector.centinelanalytica.com/script.js` returns `200`. A `401` means the `site_key` is missing or wrong, and no signals are collected.
* A `POST` to the collector origin carries the payload.
* A first-party `_centinel` cookie is set.

In code, check `window.cen.initialized` to confirm the script finished. If it is not ready yet, wait
for the `CentinelInitialized` event.

> **Warning:** The event fires only after the payload upload succeeds, and a failed upload is not retried. Always
> pair the wait with a timeout, or a visitor on a flaky network can never complete the action you
> gated behind it.

```javascript
function whenCentinelReady(cb, timeoutMs = 2000) {
  if (window.cen && window.cen.initialized) {
    cb();
    return;
  }

  let done = false;
  const run = () => {
    if (done) return;
    done = true;
    window.removeEventListener('CentinelInitialized', run);
    cb();
  };

  window.addEventListener('CentinelInitialized', run);
  setTimeout(run, timeoutMs); // never block the action on the script
}

whenCentinelReady(() => {
  login();
});
```

## Next steps

Whether you added the script or chose to skip it, your backend needs to call `/validate` on every protected request.

* [Validation](https://docs.centinelanalytica.com/install/validation.md): Wire the /validate API into your backend.
