Pageview tracking
The beacon reference — modes, data-fs attributes, and the observation shape
The beacon is a single JavaScript file served at https://api.formshield.dev/js/formshield.js. Drop it on a page with a <script> tag and it auto-initializes from data-fs-* attributes — no extra code. This page is the full reference for those attributes, the three modes, and the observation a hit produces.
The beacon runs only where JavaScript runs, so it never sees pure crawlers. To capture AI agents and bots that fetch HTML without running scripts, pair it with server reporting.
Minimal embed
<script
async
src="https://api.formshield.dev/js/formshield.js"
data-fs-project-key="fs_pub_live_…"
data-fs-action="pageview"
data-fs-mode="pageload"
></script>data-fs-project-key is the only required attribute. Everything else has a default.
Modes
data-fs-mode selects what the beacon does on load. The default is auto.
| Mode | Pageview on load | Form handling | Use when |
|---|---|---|---|
pageload | Yes — one scored page.view | No | You only want pageview analytics. |
forms | No | Yes — attaches to forms | You only want to score form submissions. |
auto (default) | Yes | Yes | You want both from one tag. |
pageload
Records exactly one scored pageview on window load and does nothing else. This is the recommended mode for traffic analytics.
forms
Attaches to forms matching data-fs-form-selector (default form). On submit, the beacon collects signals and injects them as a hidden field named _fs_signals (configurable via data-fs-field). Your backend forwards that field’s JSON to the FormShield check API, which scores the submission.
auto
Both behaviors: records one pageview on load and attaches to forms. This is the default when data-fs-mode is absent or unrecognized.
Attributes
data-fs-project-key string path required Your publishable key, fs_pub_live_…. Safe to expose in the browser. Create one in the project’s Settings.
data-fs-mode string path default: auto pageload, forms, or auto. Unknown or absent values fall back to auto.
data-fs-action string path default: pageview A label for the hit, stored on the observation — for example pageview, signup, or contact. The pageload path defaults to pageview. A per-<form> data-fs-action overrides this global default for that form.
data-fs-form-selector string path default: form CSS selector for the forms the beacon attaches to in forms and auto modes.
data-fs-field string path default: _fs_signals Name of the hidden input the beacon injects with the signals payload on form submit.
data-fs-init-url string path Advanced. Override the handshake endpoint. Defaults to /v1/handshake derived from the script’s origin.
data-fs-collect-url string path Advanced. Override the collect endpoint. Defaults to /v1/collect derived from the script’s origin.
data-fs-probe-url string path Advanced. Override the client probe endpoint. Defaults are derived from the script’s origin.
What the beacon collects
On a pageview the beacon gathers signals that distinguish a real browser from automation, then sends them to FormShield, which scores the hit and stores the result. The signals include:
- Browser fingerprint and automation tells — webdriver, headless, and other automation markers.
- A signed handshake token — proves the beacon actually ran in a real browser. Its absence or failure is a strong bot tell on the client path.
- User-agent classification — browser, OS, device, and whether the UA is a named bot.
FormShield combines these with server-side IP reputation (VPN, proxy, datacenter, scanner, country, ASN) to produce the final score. The beacon does not collect form field contents on a pageview.
The observation
Each hit becomes one observation. A clean human pageview:
{
"score": 0.09,
"decision": "allow",
"reasons": [],
"action": "pageview",
"ua": {
"browser": "Chrome",
"os": "macOS",
"device": "desktop",
"bot_kind": null,
"bot_name": null
}
}An automated visit (webdriver) crosses the block threshold:
{
"score": 0.93,
"decision": "block",
"reasons": ["automation_detected", "ua_bot"],
"action": "pageview",
"ua": {
"browser": "Chrome",
"os": "Linux",
"device": "desktop",
"bot_kind": "automation",
"bot_name": null
}
}Fields
| Field | Type | Meaning |
|---|---|---|
score | float 0.0–1.0 | Risk probability. Higher is more bot-like. |
decision | allow | review | block | allow below 0.45, review from 0.45, block from 0.8. |
reasons | string array | Rules that fired (see below). |
action | string | The data-fs-action label. |
ua | object | User-agent classification: browser, os, device, bot_kind, bot_name. |
bot_id | string | null | The identified bot’s stable id, e.g. googlebot, gptbot. null when no bot matched. |
bot_operator | string | null | The bot’s operating company, e.g. Google, OpenAI. |
bot_verified | true | false | null | true = IP-verified; false = spoofed (UA claims a bot from the wrong IP); null = unverifiable. See bot detection. |
Common reasons
| Reason | Meaning |
|---|---|
automation_detected | A webdriver or headless automation tell was present. |
ua_bot | The user agent classified as a bot. |
bot:ai_crawler | A declared AI agent (GPTBot, ClaudeBot, PerplexityBot, Bytespider). |
bot:search_crawler | A search crawler (Googlebot, Bingbot). |
bot:id:<id> | A bot was identified from the registry, e.g. bot:id:googlebot. |
bot:verified | The bot is IP-verified — its user agent and IP both check out. |
bot:spoofed:<id> | The user agent claims a bot, but the IP is out of the operator’s published ranges. |
client_token_missing | The signed handshake token was absent — the beacon could not confirm a real browser ran. |
ip_datacenter | The IP belongs to a hosting or datacenter range. |
ip_vpn / ip_proxy | The IP is an anonymizing VPN or proxy. |
ip_scanner | The IP is a known scanner or abuse source. |
Bot detection
FormShield names the bot behind each hit and, for operators that publish their IP ranges (Google, Microsoft, OpenAI, DuckDuckGo), verifies that the request really came from them. A forged crawler — a user agent claiming to be Googlebot from an unrelated IP — is flagged as spoofed and scored high. You can also allow or block bots per project from the dashboard. See bot detection for the full reference and the allow/block controls.
Where to view observations
Open the project’s Logs in the dashboard. Each observation shows its score, decision, reasons, IP profile, and user-agent classification. Filter by decision, action, or IP, and click any IP to see its full profile across observations.
Single-page apps
In an SPA the beacon records a pageview on the initial document load. Route changes that do not reload the document are not yet tracked as separate pageviews — that is a follow-up. See the React and TanStack Start guides for SPA-specific placement.