Device Context
How to collect OS version, device model, accessibility settings, and screen scale for age assurance.
Overview
Device context carries a 10% weight in the Signal Fusion engine, but its
impact extends beyond scoring — providing device_model enables hardware-tier
normalization that adjusts touch precision scoring for budget devices. Without
it, a legitimate adult on a Galaxy A14 could be falsely flagged for low touch
precision.
Fields Reference
| Parameter | Type | Required | Description |
|---|---|---|---|
os_version | string | Required | OS version string (e.g., 'iOS 18.2', 'Android 14'). Required when device_context is present. |
is_high_contrast_enabled | boolean | Required | Whether high-contrast accessibility mode is enabled. Required when device_context is present. |
screen_scale_factor | number (0.5–5.0) | Required | Display scale factor (device pixel ratio). Required when device_context is present. |
device_model | string | Optional | Device model identifier (e.g., 'iPhone 15 Pro', 'Galaxy A14'). Enables hardware-tier normalization. |
OS Version
What it measures: The operating system and version running on the user's device. Legacy OS versions (iOS 10–15, Android 5–11 in 2026) indicate hand-me-down devices, which are more common among children.
Signal impact: Legacy OS detection reduces the device score by 0.15.
How to Collect (Web)
Use the User-Agent Client Hints API (modern approach) with a fallback to
the legacy navigator.userAgent string.
import { useState, useEffect } from 'react';
async function detectOsVersion(): Promise<string> {
if ('userAgentData' in navigator) {
const ua = navigator.userAgentData as NavigatorUAData;
const hints = await ua.getHighEntropyValues(['platform', 'platformVersion']);
return `${hints.platform} ${hints.platformVersion}`;
}
const ua = navigator.userAgent;
const ios = ua.match(/OS (\d+[_.]\d+)/);
if (ios) return `iOS ${ios[1].replace('_', '.')}`;
const android = ua.match(/Android (\d+\.?\d*)/);
if (android) return `Android ${android[1]}`;
const mac = ua.match(/Mac OS X (\d+[_.]\d+)/);
if (mac) return `macOS ${mac[1].replace('_', '.')}`;
if (ua.includes('Windows NT 10.0')) return 'Windows 10';
return 'Unknown';
}
export function useOsVersion() {
const [osVersion, setOsVersion] = useState<string | null>(null);
useEffect(() => {
detectOsVersion().then(setOsVersion);
}, []);
return osVersion;
}
// Usage: const os_version = useOsVersion();The User-Agent Client Hints API requires a secure context (HTTPS) and is currently supported in Chromium-based browsers (Chrome, Edge, Opera). Safari and Firefox fall back to user-agent parsing.
Device Model
What it measures: The specific device model, used for hardware-tier normalization. Budget devices have lower-quality touchscreens that produce inherently lower touch precision — without normalization, the engine would falsely flag adult users on cheap hardware.
Signal impact: Enables touch precision offsets: HIGH tier (+0.00), MID (+0.05), LOW (+0.10).
How to Collect (Web)
Use the Sec-CH-UA-Model Client Hint (Chromium only) or extract from the
user-agent string. On native platforms, use the OS-provided model identifier.
import { useState, useEffect } from 'react';
async function detectDeviceModel(): Promise<string | null> {
if ('userAgentData' in navigator) {
const ua = navigator.userAgentData as NavigatorUAData;
const hints = await ua.getHighEntropyValues(['model']);
if (hints.model) return hints.model;
}
const ua = navigator.userAgent;
// iOS Safari doesn't expose model — return null (defaults to MID tier)
if (/iPhone|iPad/.test(ua)) return null;
// Android — some browsers include the model
const match = ua.match(/;\s*([^;)]+)\s+Build\//);
return match ? match[1].trim() : null;
}
export function useDeviceModel() {
const [model, setModel] = useState<string | null>(null);
useEffect(() => {
detectDeviceModel().then(setModel);
}, []);
return model;
}
// Usage: const device_model = useDeviceModel();iOS Safari does not expose the device model in the user-agent string or
Client Hints. If you're building a web app, iOS devices will default to MID
tier (Apple hardware consistently has good touchscreens). For native iOS apps,
use uname to get the hardware identifier.
High Contrast Detection
What it measures: Whether the user has enabled a high-contrast or bold-text accessibility setting. This correlates with the 50+ age demographic and serves as a mild adult indicator (+0.15 to device score).
How to Collect
Use the prefers-contrast CSS media query via window.matchMedia.
import { useSyncExternalStore } from 'react';
const query = typeof window !== 'undefined'
? window.matchMedia('(prefers-contrast: more)')
: null;
function subscribe(cb: () => void) {
query?.addEventListener('change', cb);
return () => query?.removeEventListener('change', cb);
}
export function useHighContrast(): boolean {
return useSyncExternalStore(
subscribe,
() => query?.matches ?? false,
() => false, // server snapshot
);
}
// Usage: const is_high_contrast_enabled = useHighContrast();Screen Scale Factor
What it measures: The device's pixel ratio (window.devicePixelRatio).
Higher values (2.0+) combined with accessibility settings can indicate enlarged
display preferences associated with older users.
How to Collect
This is a single property read — the simplest signal to collect.
import { useSyncExternalStore } from 'react';
function subscribe(cb: () => void) {
// Re-check when resolution changes (e.g., moving window between monitors)
const mq = window.matchMedia(
`(resolution: ${window.devicePixelRatio}dppx)`
);
mq.addEventListener('change', cb);
return () => mq.removeEventListener('change', cb);
}
export function useScreenScale(): number {
return useSyncExternalStore(
subscribe,
() => window.devicePixelRatio || 1.0,
() => 1.0,
);
}
// Usage: const screen_scale_factor = useScreenScale();
// 1.0 (standard), 2.0 (Retina/HiDPI), 3.0 (iPhone Pro)Putting It All Together
# Complete device_context example payload:
curl -X POST https://api.a3api.io/v1/assurance/assess-age \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"os_signal": "18-plus",
"user_country_code": "US",
"device_context": {
"os_version": "Android 14",
"device_model": "Galaxy A14",
"is_high_contrast_enabled": false,
"screen_scale_factor": 2.0
}
}'Next Steps
- Input Complexity — the second-highest weighted category (28%)
- Behavioral Metrics — the highest-impact category (43%)
- Hardware Normalization — how device model affects touch scoring