# Measure the people who visit, including those sent by AI.

> Install and configure Virent Human Analytics - the browser script, consent and privacy controls, single-page apps, custom events, identify, goals, funnels and AI referrals.

Canonical: https://stage.virent.app/docs/analytics/human-analytics
Last updated: 2026-10-19

One browser script collects pageviews, sessions, sources and AI referrals. Add the SDK when you need custom events, identities or goals.

## Install

Add the script once, in the root layout or global template, so it loads on every page. Your framework's [installation guide](/docs#installation) shows where.

```html
<script
  defer
  src="https://app.virent.app/js/script.js"
  data-write-key="vha_pk_..."
></script>
```

The publishable key (`vha_pk_…`) is in **Settings → Connection**. It is meant for browser code; it only accepts events for your website's allowed origins.

To keep ad blockers from dropping visits, serve the script and events from your own domain. See [First-party analytics](/docs/installation/first-party).

## Script options

| Attribute | Default | Effect |
| --- | --- | --- |
| `data-write-key` | required | The website's publishable key. |
| `data-allow-localhost` | `false` | Set `"true"` to collect on `localhost` while testing. |
| `data-consent` | `granted` | Start as `pending` or `denied` when a consent banner decides first. |
| `data-respect-do-not-track` | `false` | Set `"true"` to skip browsers that send Do Not Track. |
| `data-api` | script host | Relay prefix, such as `/_vr`, for first-party collection. |

## Consent and pausing

The script exposes `window.virent` for consent banners and other controls. Commands sent before the script loads are queued if you define the queue first:

```html
<script>
  window.virent = window.virent || function () { (window.virent.q = window.virent.q || []).push(arguments) }
</script>
```

```js
virent("consent", "granted") // or "denied" or "pending"
virent("pause")              // stop collecting until resume
virent("resume")
virent("pageview")           // send a pageview manually
virent("event", { name: "signup_started", properties: { plan: "growth" } })
```

With consent `pending`, up to five pageviews are held and sent once consent is `granted`, or discarded on `denied`.

## Single-page apps

The script follows client-side navigation (`history.pushState`, `replaceState` and back/forward) and sends one pageview per new path or query. Test a few route changes after installing.

## Custom events, identity and goals

Install the SDK when the browser script is not enough:

```sh
pnpm add @virent.app/sdk@^0.3.0
```

```ts
import { identify, initVirent, pageview, trackEvent, trackGoal } from "@virent.app/sdk/browser"

initVirent({ writeKey: "vha_pk_..." }) // sends the first pageview

await trackEvent({ name: "signup_started", properties: { plan: "growth" } })
await identify({ userId: "customer_123", traits: { plan: "growth" } })
await trackGoal({ name: "signup_completed", idempotencyKey: "order_123" })
```

- `initVirent` sends one pageview. In a single-page app using the SDK alone, call `pageview()` on each route change.
- `identify` attaches an ID you choose to the visitor. Send your own opaque ID, not an email address or other personal data.
- `idempotencyKey` makes a retried goal count once.
- `resetVirent()` discards the active client so you can call `initVirent` again. It does not clear the stored visitor or session ID.

Send pageviews from either the script or the SDK, not both, so each page is counted once.

## Goals and funnels

### Add a goal

In Conversions, choose **Add goal** and enter the goal name your site sends with `trackGoal`, such as `signup_completed`. Add a description if it helps your team.
### Add a funnel

Choose **Add funnel**, give it a name, then set a starting page, an optional next action (an event name) and an optional completed goal. The funnel shows how many people reach each step.

## AI referrals

Visits are grouped by source: AI, search, social, referral, direct or unknown. A visit counts as an AI referral when it comes from one of these assistants, or carries its name as the UTM source:

| Assistant | Referrer |
| --- | --- |
| ChatGPT | chatgpt.com, chat.openai.com |
| Claude | claude.ai |
| Perplexity | perplexity.ai |
| Gemini | gemini.google.com |
| Copilot | copilot.microsoft.com |
| Grok | grok.com |
| Poe | poe.com |
| You.com | you.com |

Many AI apps send no referrer, so some AI traffic arrives as direct. AI referrals are a floor, not the total.

## Privacy

- The script sets no cookies; it keeps a visitor and session ID in the site's own browser storage.
- Do not send passwords, payment data, health data, government IDs, precise location or other sensitive personal data in events, traits or URLs.
- Avoid personal data in page paths and query strings; they are collected as part of the page URL.

## Troubleshooting

### No pageviews

Check that `/js/script.js` loads on the page, the key is the full publishable key, and you are not testing on localhost without `data-allow-localhost`.
### The browser blocks the script

Allow `https://app.virent.app` in your Content-Security-Policy `script-src` and `connect-src`, or relay it [first-party](/docs/installation/first-party).
### Pageviews look doubled

Check that only the script or only the SDK sends pageviews.
### Route changes are missing

With the SDK alone, call `pageview()` after each client-side navigation.
