DEA SDK for Web¶
Detect whether a browser session comes from a real person. The SDK observes pointer, wheel, keyboard and device-motion input on the page and scores the session in the browser.
On this page: Prerequisites · Install · Initialize the SDK · Use the classification · Verify your setup · Before you release · Next steps
Prerequisites¶
- An
appIdandappKeyfor this app — see Accounts and keys - A page served over HTTPS, or from
localhostduring development. The SDK needs a secure context: on any other page the browser does not providecrypto.randomUUID(), andinitthrows. - Chrome or Edge 96, Firefox 95, Safari 15.4 (iOS 15.4) or later, with WebAssembly
enabled. These are the first versions that provide both
crypto.randomUUID(), which the SDK calls when it starts, and the WebAssembly features its scoring engine uses. - A bundler or other tool that loads ES modules (Vite, webpack, Rollup, esbuild). The package is published as ES modules only.
Install¶
Note:
@dea/sdkis installed from DEA's release URL, not from the npm registry. The URL names the release; to upgrade, install the URL of the new release. The@dea/basepackage it depends on comes from the same release and is installed with it.
npm¶
yarn¶
pnpm¶
Note: pnpm 11 stops this install with
ERR_PNPM_EXOTIC_SUBDEP, because@dea/sdkdepends on@dea/baseby URL. Add this line to thepnpm-workspace.yamlat the root of your project (create the file if it does not exist), then run the install again:
Content Security Policy¶
If your page sends a Content Security Policy, it must allow the following, or the SDK cannot load its scoring engine or reach DEA:
| Directive | Allow | Why |
|---|---|---|
script-src |
'wasm-unsafe-eval' |
The SDK compiles its WebAssembly scoring engine in the page |
connect-src |
data: |
The engine is embedded in the package's JavaScript and loaded from a data: URL. If you set coreUrl, allow that URL instead |
connect-src |
https://sdk.dep-protocol.com |
Updates of the scoring model. Not needed with modelUpdates: false |
connect-src |
https://poh-api.dep-protocol.com |
attest() and ad proofs |
For example:
Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval'; connect-src 'self' data: https://sdk.dep-protocol.com https://poh-api.dep-protocol.com
The package loads part of its own code with a dynamic import(). Your bundler emits those chunks
with the rest of your scripts, so the script-src that allows your bundle allows them too.
Initialize the SDK¶
Initialize once, as early as possible in page load, so that capture covers the whole session.
import { init } from '@dea/sdk';
const dea = init({
appId: 'YOUR_APP_ID',
appKey: 'YOUR_APP_KEY',
});
appIdandappKeyare the pair DEA issued for this app. Neither is a secret: both ship in your page. Scoring works withoutappKey, but on a productionappIdwithout it,attest()answers'failed'and no ad proof is signed or sent. See Accounts and keys.initis safe to call more than once. A repeat call returns the running instance and keeps its session; the options of the repeat call are ignored.- If the scoring engine cannot load,
initstill returns and the SDK keeps running without a score: the classification stays'insufficient'. It reports why ondea.erroroncedea.ready()has resolved. - Capture starts immediately. There is nothing to wrap and nothing to register. To hold capture back until the user agrees, see User Consent.
- The SDK works out whether the page runs on a desktop or a mobile browser. To choose yourself,
see
profile.
Anywhere else in your code, get the running instance with DeaSdk.current:
Use the classification¶
Read the classification at the moment you make a decision. The read is synchronous and does not wait for the network.
import { DeaSdk } from '@dea/sdk';
async function onSignUp(form: SignUpForm) {
const result = DeaSdk.current?.getClassification().result ?? 'insufficient';
if (result === 'human') {
await submitSignUp(form);
} else {
showChallenge(); // 'bot', 'undetermined' or 'insufficient': a captcha, a delay, a manual review
}
}
SignUpForm, submitSignUp and showChallenge stand for your own code. How the score and the
thresholds are produced is explained in How scoring works.
Verify your setup¶
-
Check that the SDK loaded. In a page that has called
init:import { DeaSdk } from '@dea/sdk'; const dea = DeaSdk.current!; await dea.ready(); console.log(dea.error); // null: the scoring engine loaded console.log(dea.getSessionInfo()); // { session: '…', device: { … }, identity: { … }, motion: 'off', sdk: { version: '0.19.0', core: '…' } }If
dea.erroris notnull, see Troubleshooting. -
Interact with the page — move the pointer, click, scroll, type — and read the classification again:
console.log(dea.getClassification()); // { result: 'insufficient', value: null, actions: 6, botThreshold: …, humanThreshold: …, modelVersion: … }Each tap, drag, scroll or run of key presses is an action.
actionscounts the ones the score is based on, andresultstays'insufficient'until the session is sufficient: until there are as many as the model asks for. With the built-in model, that is 20 in a desktop browser, from at least two kinds of input (pointer, scroll, keyboard) with at least three each, and 8 in a browser on a phone or tablet. See Evidence. -
To watch the SDK work while you integrate, install the staging package in place of the production one. It is the same SDK with its log switched on, written to the browser console:
Reload the page. On a first visit the console shows:
dea 2026-10-16T01:39:29.699Z INFO sdk.started ok session.id=none account=production | SDK started on a production account dea 2026-10-16T01:39:29.731Z INFO model.installed ok session.id=ee0962fd-… model.version=0 model.source=embedded model.theta_bot=… | Model 0 installed from embedded dea 2026-10-16T01:39:29.731Z INFO sdk.ready ok session.id=ee0962fd-… core=web-desktop profile=web-desktop | SDK ready: core web-desktop, profile web-desktopIf you see
sdk.core_failedinstead, see Troubleshooting.
Warning: The staging package downloads its scoring models from DEA's staging channel, so its classification can differ from production's. Install the production package again before you release. The production package writes no log at all.
Before you release¶
- [ ] The production package is installed: its URL has no
/staging/in it - [ ]
initgets theappIdandappKeyDEA issued for this app — see Accounts and keys - [ ] The page is served over HTTPS
- [ ] Your Content Security Policy allows the SDK — see Content Security Policy
- [ ] The classification is read at the decision point, with a fallback for
'bot','undetermined'and'insufficient'— see Human Scoring - [ ] Capture is paused on payment fields and any screen your privacy policy excludes — see Sensitive Screens
- [ ]
reset()is called on logout — see Sessions - [ ] Consent is asked for where your users need it — see User Consent
- [ ] The cross-platform checklist is done — see Going to production
Next steps¶
- Human Scoring — read the classification and the evidence behind it
- User Consent — hold capture back until the user agrees
- Sensitive Screens — pause capture on payment fields
- Sessions — start a new session on logout
- Attest and Ads — register the browser's key and sign ad proofs
- Options — every option
initaccepts - Privacy and network — what the SDK captures, stores and sends
- Troubleshooting