# JavaScript Agent v6

You can integrate our device fingerprinting module directly into a web app
using our JavaScript agent. Use the CDN-hosted script to ensure you always load
the latest available version. Visit the
[SEON Web SDK GitHub](https://github.com/seontechnologies/seon-web-sdk-public)
page to see the latest version and its changelog.

The agent produces an encrypted payload that you send as the `session`
attribute of a [Fraud API request](fraud-api-request.html); the decrypted data
comes back as `device_details` in the
[Fraud API response](fraud-api-response.html).

{% callout type="note" title="Coming from v5?" %}
Migrating from JavaScript Agent v5 requires a few integration changes — see the
[migration guide](https://docs.seon.io/api-reference/migration-guides#js-sdk-v5-to-v6-guide).
{% /callout %}

---

## Table of Contents

1. [Integration](#integration)
2. [Configuration parameters](#configuration-parameters)
3. [Behavioural features](#behavioural-features)
4. [Content Security Policy](#content-security-policy)
5. [NPM integration](#npm-integration)
6. [Payload](#payload)
7. [Suspicious flags](#suspicious-flags)
8. [Example payload](#example-payload)
9. [Common issues](#common-issues)

---

## Integration

{% step n=1 title="Load the agent" %}
Include the script — for example inside the `<head>` tags of your site. You can
also lazy-load it or execute it on a specific action (clicking Login, Payment
or Registration), as long as you make sure the module has finished loading
before you invoke its methods.

```html
<html>
  <head>
    ...
    <script src="[source_url]"></script>
  </head>
  <body>
    ...
  </body>
</html>
```

Any of these script source URLs works as `[source_url]`:

- `https://cdn.dfsdk.com/js/v6/agent.umd.js`
- `https://cdn.deviceinf.com/js/v6/agent.umd.js`
- `https://cdn.seonintelligence.com/js/v6/agent.umd.js`
{% /step %}

{% step n=2 title="Call seon.init() on page load" %}
This starts behavioural analysis and yields more data points for bot detection
and more accurate intelligence signals.

```javascript
// On page load:
seon.init();

const config = {
  geolocation: {
    canPrompt: false,
  },
  networkTimeoutMs: 2000,
  fieldTimeoutMs: 2000,
  region: 'eu',
  silentMode: true,
};
```

Without `seon.init()` you still receive valid device intelligence signals from
most functions, but the payload will carry no behavioural signals, and bot
detection and the browser hash will be less precise.
{% /step %}

{% step n=3 title="Generate the session" %}
`seon.getSession(config)` collects the available information and returns an
encrypted, base64-encoded payload. Pass your config object if you are not using
the defaults.

```javascript
const session = await seon.getSession(config);
// 'session' holds the encrypted device fingerprint to send to SEON
```
{% /step %}

{% step n=4 title="Send it with the Fraud API request" %}
Post the payload string to your backend and set it as the `session` property of
your [Fraud API request](fraud-api-request.html). Still make the Fraud API call
if `session` is missing — the snippet may not have executed.

```javascript
await fetch('/api/score-transaction', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ userId, session }),
});
```

{% callout type="tip" %}
Put a timeout on the JavaScript side and make the Fraud API call afterwards, so
a slow or blocked agent never blocks the transaction.
{% /callout %}
{% /step %}

Including the lightweight JavaScript agent or the mobile SDKs is optional, but
it provides in-depth analysis of each user's device. Working examples live in
the [StackBlitz collection](https://stackblitz.com/@dfseon/collections/seon-web-sdk-v6-demos).

---

## Configuration parameters

To configure the module, create a config object and pass it to
`seon.getSession(config)` — or to `seon.init(config)` for the behavioural
options.

{% attributes schema="JsAgentConfig" /%}

---

## Behavioural features

Calling `seon.init()` enables behavioural analysis. Collection starts at
`seon.init()` and ends when `seon.getSession()` is called; the behavioural data
is included in the generated session string automatically. The recommended
pattern is therefore **`init` on form load, `getSession` on form submit**, so
the analysis covers the whole fill-out.

By default user interaction is analysed on the whole page. To target specific
inputs or forms, use the `behavioralDataCollection` option:

```javascript
// On load
seon.init({
  behavioralDataCollection: {
    targets: 'input[type="text"], .behavior', // querySelector string
    formFilloutDurationTargetId: 'myForm',    // form with id 'myForm'
  }
});

// On form submit
await seon.getSession();
```

To disable behavioural collection altogether, pass an empty string:

```javascript
seon.init({
  behavioralDataCollection: {
    targets: '', // pass an empty string for targets
  }
});
```

{% callout type="warning" %}
The targeted elements **must exist at the time of the `init()` call**. Elements
that match the selector but are added to the DOM afterwards are not part of the
evaluation.
{% /callout %}

Suspicious behaviour surfaces in the `suspicious_flags` response field, which
for behavioural signals can contain `suspicious_keypress_characteristics`,
`suspicious_mouse_movement`, `suspicious_form_fillout`, `paste_used`,
`autofill_used`, `potential_remote_interaction` and
`potential_remote_control`.

---

## Content Security Policy

If your site sends CSP headers, allow the following sources for full
functionality, depending on your host configuration:

| Directive | Value |
| --- | --- |
| `connect-src` | `*.seondnsresolve.com` (or the resolver domain matching your host) |
| `worker-src` | `blob:` — required for running fields in web workers |
| `img-src` | `data:` and `http://127.0.0.1:*` (the latter only when `silentMode` is disabled) |

Resolver domains by script host:

| Script host | `connect-src` value |
| --- | --- |
| Default | `*.seondnsresolve.com` |
| `deviceinf.com` | `*.deviceinfresolver.com` |
| `getdeviceinf.com` | `*.getdeviceinfresolver.com` |
| `seonintelligence.com` | `*.seonintelligenceresolver.com` |

---

## NPM integration

Alternatively, integrate the SDK through
[npm](https://www.npmjs.com/package/@seontechnologies/seon-javascript-sdk). With
this method you have to keep the package updated yourself to pick up our latest
features and bugfixes.

```bash
npm install @seontechnologies/seon-javascript-sdk
# or
yarn add @seontechnologies/seon-javascript-sdk
```

---

## Payload

The library collects device information and prepares an encrypted payload for
the Fraud API. The client-side data is not readable — it is revealed in the
[Fraud API response](fraud-api-response.html) and on the Admin Panel. Some
fields can be `null` when the browser does not support or return that data
point; in every other case data types are preserved.

{% attributes schema="DeviceDetails" /%}

---

## Suspicious flags

`device_details.suspicious_flags` is an array of fraud indicators. The default
rule score for each is listed below; flags with no default rule are available
for you to build your own rules on.

| Flag | Meaning | Default score |
| --- | --- | --- |
| `bots_and_automation` | The browser is automated. Detected tools include Selenium, Puppeteer, Playwright and PhantomJS. | 12 |
| `experimental_user_agent_spoofing` | User agent spoofing inferred from browser feature inconsistencies. | 8 |
| `high_risk` | The device includes highly suspicious details, such as VM detection. | 5 |
| `medium_risk` | As `low_risk`, with higher confidence of abnormality. | 3 |
| `privacy_extension` | A privacy extension such as Privacy Badger or Disconnect was detected. | 2 |
| `low_risk` | The browser has some suspicious values, making it low risk. | 1 |
| `potential_fraud_browser` | A known fraud browser such as Multilogin, Sphere or VMLogin. | 1 |
| `privacy_browser` | A browser with anti-fingerprinting, such as Brave or Tor. | 1 |
| `potential_ai_agent` | The session is controlled by an AI agent rather than human interaction. Where applicable an additional flag indicates the agent type. | — |
| `potential_remote_control` | Network analysis combined with behavioural signals indicates an active remote control session. | — |
| `potential_remote_interaction` | Remote access inferred from behavioural signals. Highly accurate in a browser, but dependent on your integration. | — |
| `potential_screen_sharing` | Possible screen sharing, based on port scanning. Requires `silentMode: false`. | — |
| `possible_device_farm` | Device orientation and motion patterns suggest the device is part of a device farm. | — |
| `potential_simulator` | Simulator usage. | — |
| `potential_inapp_browser` | A webview browser environment. | — |
| `reused_session` | An identical encrypted fingerprint appears across multiple transactions, suggesting a reused or compromised fingerprint. | — |
| `session_manipulation` | Experimental. Alterations in the fingerprint suggest the session was intentionally tampered with. | — |
| `navigator_spoof` | The Navigator API has been spoofed, so system data points such as `hardware_concurrency` and `device_memory` are unreliable. | — |
| `htmlcanvaselement_spoof` | The Canvas API has been spoofed or tampered with. | — |
| `webglrenderingcontext_spoof` | The WebGL API has been spoofed or altered. | — |
| `geolocation_spoof` | The Geolocation API is spoofed or manipulated, so the reported geolocation is unreliable. | — |
| `fake_os` | OS spoofing in the user agent field. JS SDK 5.6.0+. | — |
| `touch_radius_spoof` | Possible emulation or remote control, based on touch event properties. | — |
| `touch_timestamp_spoof` | Possible remote-controlled or emulated touch activity, based on event timestamps. | — |
| `no_hardware_acceleration` | Hardware acceleration is disabled in the browser. | — |
| `devtools_open` | DevTools are open. Requires `silentMode: false`. | — |
| `vpn_` | Probable VPN usage. JS SDK 5.7.1+. | — |
| `proxy_` | Probable proxy usage, based on network fingerprinting. JS SDK 5.7.1+. | — |
| `suspicious_keypress_characteristics` | Unusual typing characteristics, such as a very high keystrokes-per-minute rate. | — |
| `suspicious_mouse_movement` | Suspicious mouse movement, for example perfectly linear or unusually fast. | — |
| `suspicious_touch_movement` | Suspicious touch interactions. | — |
| `suspicious_form_fillout` | Unusual form filling behaviour, such as untimely navigation. | — |
| `paste_used` | A paste event occurred. | — |
| `autofill_used` | Autofill was used. | — |
| `no_user_interaction` | Behavioural collection is set up but ineffective, due to integration issues or missing event data. | — |

{% callout type="note" %}
The flag set is continuously extended with new signals.
{% /callout %}

---

## Example payload

```json
{
  "device_details": {
    "type": "web",
    "source": "js-6.5.1",
    "session_id": "14e671a5f503b0d0d8d978a67de0866b",
    "true_device_id": "0195943f-b231-7c7c-9586-b3b0cccb039a",
    "device_hash": "c92d19d5c29dbd2834e2281d28b35fd5",
    "browser_hash": "9fc8c3bdc369cdb1bd5c29542eedc092",
    "cookie_hash": "9831c012f5d3056e2399968b55296f91",
    "spoofing_hash": "fbdcfe51a27dadafac9ccf8a2e34b9bd",
    "suspicious_flags": [],
    "os": "macOS 10.15 Catalina",
    "platform": "MacIntel",
    "browser": "CHROME",
    "browser_version": "134.0.0.0",
    "browser_version_age": 0,
    "device_type": "desktop",
    "device_memory": 8,
    "hardware_concurrency": 8,
    "price_range": "medium",
    "private": false,
    "vpn": false,
    "proxy": false,
    "adblock": false,
    "extensions": [],
    "device_ip": "188.0.0.0",
    "device_ip_isp": "Magyar Telekom",
    "device_ip_country": "HU",
    "device_ip_region": null,
    "dns_ip": "141.101.104.187",
    "dns_ip_isp": "CloudFlare Inc",
    "dns_ip_country": "AT",
    "dns_ip_region": null,
    "webrtc_activated": true,
    "webrtc_ips": ["188.0.0.0"],
    "webrtc_count": 1,
    "device_location": {
      "latitude": 47.4379457,
      "longitude": 19.1125038,
      "accuracy": 20,
      "status": "SUCCESS",
      "country_code": "HU",
      "region": "HU-BU",
      "city": "Budapest",
      "zip": "1204"
    },
    "timezone": "Europe/Budapest",
    "timezone_offset": "+01:00",
    "timezone_country": "HU",
    "locale": "en-GB",
    "languages": ["en-GB", "en-US", "en"],
    "keyboard_layout_name": "Hungarian",
    "keyboard_layout_hash": "eac72b515383ac8b4bbbb232ff6841f1",
    "canvas_hash": "cf06210f828cb8744cb449a843b83305",
    "audio_hash": "124.04346607114712",
    "math_hash": "a931b6543effb809b4abb1ecb36431db",
    "mime_types_hash": "12ce62938a9f2e8b926a65dfc687824d",
    "system_colors_hash": "2a20eb8a0076d38e67fafce18f2c5377",
    "webgl": {
      "webgl_hash": "41f3cabaf3febc32cc1bb223db03867d",
      "webgl_image_hash": "9e0924fc01f9d18c4f8ea400b2de8bec",
      "webgl_parameters_hash": "97ec8905c1226b744a9edbffbc5f4a34",
      "webgl_parameters_noise": false,
      "webgl_renderer": "ANGLE (Apple, ANGLE Metal Renderer: Apple M1 Pro, Unspecified Version)",
      "webgl_vendor": "Google Inc. (Apple)"
    },
    "font_hash": "4160d90df34f64c3683c5b1e54bd7d57",
    "font_count": 17,
    "font_noise": false,
    "plugins": {
      "plugin_count": 5,
      "plugin_hash": "a61464d4341e30d7773ad797e19ea630",
      "plugin_list": ["PDF Viewer", "Chrome PDF Viewer", "WebKit built-in PDF"]
    },
    "screen_data": {
      "screen_width": 1512,
      "screen_height": 982,
      "screen_available_width": 1512,
      "screen_available_height": 869,
      "screen_color_depth": 30,
      "device_pixel_ratio": 2.0,
      "orientation_type": "landscape-primary",
      "orientation_angle": 0,
      "is_extended": false,
      "window_inner_width": 1512,
      "window_inner_height": 782
    },
    "unpopular_device_resolution": false,
    "unpopular_user_agent": false,
    "media_devices": {
      "audio_input_count": 1,
      "audio_output_count": 1,
      "video_input_count": 1
    },
    "battery": {
      "battery_charging": true,
      "battery_level": 100
    },
    "permissions": {
      "granted": ["accelerometer", "geolocation", "gyroscope"],
      "prompt": ["camera", "microphone", "notifications"],
      "denied": []
    },
    "drm_key_systems": ["org.w3.clearkey"],
    "touch_support": false,
    "max_touch_points": 0,
    "mouse_moved": false,
    "has_focus": true,
    "cookie_enabled": true,
    "do_not_track": null,
    "java_enabled": false,
    "flash_enabled": false,
    "referrer": "",
    "window_location": "https://randomsite.random",
    "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
    "user_agent_data": {
      "architecture": "arm",
      "bitness": "64",
      "mobile": false,
      "model": "",
      "platform": "macOS",
      "platform_version": "15.3.1",
      "ua_full_version": "134.0.6998.89"
    }
  }
}
```

---

## Common issues

| Symptom | Cause | Fix |
| --- | --- | --- |
| `session` is sent but `device_details` is `null`, and no device data appears on the Transaction Details page | The encrypted payload is corrupted | Review the integration and check that the payload reaches the API unmodified |
| Fields are missing from the payload | CSP is blocking the agent | Allow the resolver domains in `connect-src` — see [Content Security Policy](#content-security-policy) |
| No behavioural signals in the payload | `seon.init()` was never called, or was called after the target elements were added to the DOM | Call `seon.init()` on page/form load, before the targets render |
| Fingerprinting takes too long | No timeout configured | Set `fieldTimeoutMs` rather than wrapping `getSession()` — a partial result is still produced |

---

## Next steps

### Related references

- [Fraud API Request](fraud-api-request.html) — where the `session` payload is sent
- [Fraud API Response](fraud-api-response.html) — where `device_details` comes back
- [Migration guides](https://docs.seon.io/api-reference/migration-guides#js-sdk-v5-to-v6-guide) — upgrading from v5

### Other device fingerprinting SDKs

- [iOS SDK](https://docs.seon.io/api-reference/fraud-api#ios-sdk)
- [Android SDK](https://docs.seon.io/api-reference/fraud-api#android-sdk)

For additional support, contact your SEON representative.
