# device_apis

> Whether 18 device and platform APIs (HID, USB, serial, Bluetooth, XR, Web Share and others) exist, presence only, as a bit mask. Reported, not in either id.

Source: https://whiskerjs.com/signals/device-apis/ (last updated 2026-10-27)

**device_apis at a glance (from the library code, signalsVersion 14)**

| Field | Value |
| --- | --- |
| Component name | `device_apis` |
| In the visitorId | no (reported, kept out of both ids) |
| structuredId group | none |
| Hashing | plain value; a digest only when longer than 128 characters or not printable ASCII |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | [signalsVersion 12](https://whiskerjs.com/whats-new/) |
| Collector revision | [1](https://whiskerjs.com/docs/api/#component) (reported as `revision` with every result) |

> **In plain words:**
>
> Browsers offer web pages different ways to reach hardware: USB, Bluetooth, game controllers, virtual reality headsets, NFC tags. WhiskerJS only checks whether each of 18 such features exists, without using any of them, and writes the answer as one short code.

## What it reads

Whether each of these names exists, in this order (bit 0 first):

| Bit | API | Bit | API |
| --- | --- | --- | --- |
| 0 | `navigator.bluetooth` | 9 | `navigator.virtualKeyboard` |
| 1 | `navigator.hid` | 10 | `navigator.wakeLock` |
| 2 | `navigator.usb` | 11 | `navigator.ink` |
| 3 | `navigator.serial` | 12 | `window.NDEFReader` |
| 4 | `navigator.xr` | 13 | `window.PressureObserver` |
| 5 | `navigator.share` | 14 | `window.showOpenFilePicker` |
| 6 | `navigator.contacts` | 15 | `window.EyeDropper` |
| 7 | `navigator.devicePosture` | 16 | `window.IdleDetector` |
| 8 | `navigator.windowControlsOverlay` | 17 | `window.documentPictureInPicture` |

## How the technique works

Each name is tested with the `in` operator only: no getter runs and no method is called, so nothing can prompt or open a device chooser. The answers become a bit mask in hex, bit *i* for entry *i* (`0` when none exists). The order is fixed and new entries are appended.

## Why it helps

These APIs differ between engines, between desktop and mobile builds and between browsers built on the same engine, because a browser may remove some of them. Most need a secure context. It adds a little detail about the browser build for your server.

## Stability

Policies and browser settings switch these APIs on and off, and new browser versions add them. That is why it is reported outside both ids. Added in library PR #33 (2026-10-27) at signalsVersion 12: it is outside both ids, so the ids did not change. No integrity check uses it: a missing API is never a sign of anything.

## Browser support and what blocks it

In the self-hosted CI browser matrix of PR #33 on Linux:

| Browser | Value | Notes |
| --- | --- | --- |
| Chrome, Chromium, Edge, Opera | `3ef9e` | no Bluetooth, Web Share, Contacts or Web NFC there |
| Brave | `3af9e` | no `showOpenFilePicker` |
| Opera beta (Chromium 142) | `1ef9e` |   |
| Firefox | `20408` | `serial`, `wakeLock`, `documentPictureInPicture` |
| Playwright’s WebKit | `400` | `wakeLock` |

Expect other values on Windows, macOS, Android and iOS, where other APIs exist. `unavailable` / `no_api` only without a `navigator`. No browser class excludes it.

## Privacy

It reads whether interfaces exist, never their contents: no device list, no prompt, no request. Leave it out with `collect({ exclude: ['device_apis'] })` if you do not need it; no id changes.

## How WhiskerJS handles noise and failures

| Status | ruleId | When |
| --- | --- | --- |
| `ok` |   | a bit mask in hex |
| `unavailable` | `no_api` | no `navigator` |
| `blocked` | `threw`, `security_error` | the read threw |
| `excluded` | `caller_excluded` | you excluded it |
| `timed_out` | `timeout`, `aborted` | budget or abort |

## Example value

```json
"device_apis": { "status": "ok", "value": "3ef9e", "hashed": false, "durationMs": 0.1, "revision": 1 }
```

## Questions and answers

### Can device_apis show a permission prompt?

No. It only asks whether a name exists, with the in operator. No getter runs and no method is called, so no device chooser or prompt can appear.

### Does a missing API raise a flag?

No. Policies, browser settings and builds remove these APIs for good reasons, so a missing API is never treated as a sign of anything.

## Related pages

- [engine_features](https://whiskerjs.com/signals/engine-features/): JavaScript, Intl and WebAssembly features.
- [permissions](https://whiskerjs.com/signals/permissions/): Permission states, read without asking.
- [vendor_flavors](https://whiskerjs.com/signals/vendor-flavors/): Browser-specific objects, in the id.
