@paybyrd/device-risk
v1.0.0
Published
Paybyrd Device Risk SDK — collects browser signals for device intelligence
Readme
@paybyrd/device-risk
Paybyrd's browser SDK for device intelligence and behavioral fingerprinting. It collects hardware, environment, and behavioral signals from the user's browser and returns a stable deviceId + confidence score that feeds the Paybyrd Antifraud rule engine.
Use it on your checkout or login page to detect bot traffic, emulators, automation frameworks, repeat offenders across sessions, and anomalous input patterns — without storing any PII yourself.
Install
npm install @paybyrd/device-riskor
yarn add @paybyrd/device-riskQuick start
ES modules / React / Vue / Angular
import deviceRisk from '@paybyrd/device-risk';
const sdk = await deviceRisk({
workspaceKey: 'pk_live_xxx', // from the Antifraud portal → API Keys
});
// Call right before submitting the form / completing checkout:
const { deviceId, requestId, confidence, isNewDevice, persistentDeviceId } = await sdk.getDeviceSignal();
// Send deviceId + requestId with your payment / login request.Plain HTML / <script> tag
<script src="https://unpkg.com/@paybyrd/device-risk/dist/deviceRisk-web.js"></script>
<script>
(async () => {
const sdk = await deviceRisk({ workspaceKey: 'pk_live_xxx' });
const signal = await sdk.getDeviceSignal();
console.log(signal);
})();
</script>API
deviceRisk(props): Promise<DeviceRiskResponse>
Initializes the SDK and returns a handle with four methods.
props
| Field | Type | Default | Description |
| ------------------- | ------------------------------------ | -------------- | --------------------------------------------------------------------------- |
| workspaceKey | string | — (required) | Public API key (pk_...) from the Antifraud portal → API Keys page. |
| env | 'stage' \| 'production' | 'production' | Selects the backend. stage → api-antifraud-s.paybyrd.com, production → api-antifraud.paybyrd.com. |
| endpoint | string | — | Override the API endpoint entirely (e.g. http://localhost:5000 for local dev). |
| timeout | number | 10000 | Network timeout in milliseconds. |
| retries | number | 2 | Retry attempts on network failure. |
| targetElements | string | — | CSS selector for targeted behavioral monitoring (e.g. input[type="text"]). Omit to monitor the whole page. |
| encrypt | boolean | true | Encrypt the signal payload with AES-GCM before sending. Set false for local dev only. |
| onSignalCollected | (signals: SignalData) => void | — | Callback invoked with the raw signal payload before it's sent. Useful for debugging. |
Returned handle
| Method | Returns | Description |
| ------------------ | ----------------------------- | --------------------------------------------------------------------------- |
| init() | void | Start a new monitoring session. Call it as early as possible on the page. |
| getSession() | Promise<DeviceSignalResult> | End the session, collect and send signals, return the device identity. |
| getDeviceSignal()| Promise<DeviceSignalResult> | Convenience: calls init() immediately followed by getSession(). |
| destroy() | void | Stop all listeners and reset state. Call on component unmount. |
DeviceSignalResult
{
deviceId: string; // Stable id for this device within your workspace
requestId: string; // Unique per-call id — forward with your payment/login request
isNewDevice: boolean; // True if first time this device was seen
confidence: number; // 0–1 match confidence
persistentDeviceId: string; // Long-lived id persisted across sessions via localStorage
}Typical integration pattern
Call init() on page load, then getSession() right before the high-value action (payment, signup, login):
import { useEffect, useRef } from 'react';
import deviceRisk from '@paybyrd/device-risk';
function CheckoutPage() {
const sdkRef = useRef(null);
useEffect(() => {
(async () => {
sdkRef.current = await deviceRisk({
workspaceKey: process.env.NEXT_PUBLIC_ANTIFRAUD_KEY,
targetElements: 'input, button[type="submit"]',
});
sdkRef.current.init();
})();
return () => sdkRef.current?.destroy();
}, []);
const handlePay = async (paymentPayload) => {
const { deviceId, requestId } = await sdkRef.current.getSession();
await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({ ...paymentPayload, deviceId, requestId }),
});
};
return /* ... */;
}The longer the window between init() and getSession(), the more behavioral data (keystroke cadence, mouse movement, touch dynamics) the SDK has to work with.
What's collected
- Hardware & environment: canvas fingerprint, WebGL vendor/renderer, audio fingerprint, screen resolution, color depth, timezone, platform, CPU cores, device memory, touch support, languages, fonts, plugins, user agent.
- Privacy & integrity: Do Not Track, cookie support, private browsing, WebView context, hardware acceleration, devtools open.
- Automation detection: headless browsers, Selenium / Puppeteer / Playwright signals, canvas tampering, UA consistency checks.
- Behavioral: keyboard dynamics, mouse movement, touch patterns, form interaction — scoped to
targetElementsif provided. - Session: session id, session duration, persistent device id (localStorage), browser hash.
All payloads are AES-GCM encrypted in transit by default (encrypt: true). The SDK never sends form values or PII — only signal metadata.
Development
yarn install
yarn dev # starts webpack-dev-server, opens test-page
yarn build # builds both UMD and web distributions
yarn lint # eslint
yarn test # jestThe test-page/index.html provides a sandbox for manually exercising every option.
Release
Releases are cut via the Azure DevOps pipeline defined in devops/azure-devops.yml. Pushing a branch release/x.y.z or hotfix/x.y.z triggers the versioning template and publishes to npm.
License
MIT — see LICENSE.
