# Fingerprints: Mosaic API reference

> Fingerprints in Mosaic’s local REST API: 7 routes, each with its method, path, fields, what it returns and the errors it names.

Source: https://mosaicbrowser.com/docs/api/fingerprints

**Profiles** REST API

## Fingerprints

Generated from real-device distributions, and checked for coherence before they are ever used.

- [POSTGenerate a fingerprint](#generate-fingerprint)
- [POSTRegenerate a profile’s identity](#regenerate-fingerprint)
- [POSTCheck a profile’s identity for contradictions](#validate-fingerprint)
- [GETList the real devices a profile can claim](#list-devices)
- [POSTCheck a fingerprint that has not been saved](#audit-fingerprint)
- [POSTRun a detection audit](#audit-profile)
- [GETGet a fresh seed](#new-seed)

## [Generate a fingerprint](#generate-fingerprint)

POST`/api/v1/fingerprints/generate`

Draws a complete identity — a machine, a browser build, a locale — from a catalogue of devices people can actually buy. The hardware half comes out of **one** catalogue row rather than three independent tables: the GPU and its ANGLE renderer string, the `hardwareConcurrency` values that model ships with, and the displays it actually has. Drawing them separately is how an `Apple M3 Max` used to arrive beside a 13-inch MacBook Air panel — each value the most popular in its own column, the combination a machine nobody sells. The result is validated before it is returned and redrawn if it fails, so you never receive a Direct3D renderer on macOS. Pass a `seed` to reproduce an identity exactly, or `deviceId` to pin the machine and let everything else vary.

#### Body

| Field | Type | What it is |
| --- | --- | --- |
| `deviceId` | `string` | Draw this exact machine from the catalogue — see `GET /fingerprints/devices`. A phone id implies `os: "android"`, so it is enough on its own. An unknown id falls through to a normal draw rather than failing, so a stale id in a saved template cannot stop a profile being created. |
| `os` | `"windows" \| "macos" \| "linux" \| "android"` | Force a platform. Any desktop platform or `android` can be made on any computer, and the profile is drawn coherently for the platform it claims — a Windows profile gets a Windows GPU, screen and thread count. The host’s own desktop platform still matches best: the fonts installed, their metrics and how they rasterise come from the real machine, and no switch reaches them. Measured on a macOS host: claiming macOS scored 100/100 on IPHey, claiming Windows 80/100, and claiming Linux showed 1 font against 159. A foreign desktop is accepted with that caution in the editor, never refused. `android` draws a whole captured phone — screen, DPR, thread count, touch points and the entire WebGL record from one real device, never mixed with a table. |
| `brand` | `"Chrome"` | Browser brand, and there is one. `Edge` is a `400`: its User-Agent suffix and the client-hint version lists are built from the same switch, so the engine can produce a correct Edge User-Agent or a correct `Sec-CH-UA-Bitness`, never both. |
| `countryCode` | `string` | ISO country, used to pick a coherent locale and timezone. |
| `timezone` | `string` | IANA zone, overriding the country’s default. |
| `languages` | `string[]` | BCP-47 tags; also sets Accept-Language. |
| `seed` | `string` | Reproduce a specific identity. Omit for a new one. |

#### Returns

A complete `Fingerprint`.

#### Errors

- 400 — the options are mutually unsatisfiable and no coherent identity exists.

## [Regenerate a profile’s identity](#regenerate-fingerprint)

POST`/api/v1/profiles/:id/fingerprint/regenerate`

Replaces the profile’s fingerprint with a new one, preserving any fields the profile has locked. Refused while the profile is running.

#### Path parameters

| Field | Type | What it is |
| --- | --- | --- |
| `id`required | `string` | The record’s id. |

#### Body

`GenerateOptions`. Same options as the generator.

#### Returns

The updated `Profile`.

## [Check a profile’s identity for contradictions](#validate-fingerprint)

POST`/api/v1/profiles/:id/fingerprint/validate`

Answers two separate questions about the stored fingerprint. `problems` lists what is impossible on any machine — a GPU backend that does not exist on the chosen OS, a screen with no taskbar inset, a browser version that disagrees with its own User-Agent. `warnings` lists what is possible somewhere but not on *this* machine: a claimed platform the engine cannot back below JavaScript, a GPU vendor that contradicts `navigator.gpu`, a chip beside a core count it never shipped with. The generator never produces either; both come from hand-editing, and neither blocks a save.

#### Path parameters

| Field | Type | What it is |
| --- | --- | --- |
| `id`required | `string` | The record’s id. |

#### Returns

`{ problems: string[], warnings: [{ field: "os"|"gpu"|"screen"|"hardwareConcurrency"|"browserMajor", title, detail }] }`

## [List the real devices a profile can claim](#list-devices)

GET`/api/v1/fingerprints/devices`

The device catalogue, as choices. Each entry is one machine somebody can buy — its GPU as the driver names it, the `hardwareConcurrency` values that model ships with, the memory configurations it comes in, and the displays it actually has. Pass an `id` back as `deviceId` to `POST /fingerprints/generate` to draw that exact machine.

The **Android** rows are captured phones rather than entries assembled from tables: one row is one device, and its screen, DPR, thread count, touch points and whole WebGL record were read off the same handset. A phone id such as `sm-s918u` implies `os: "android"` on its own, so naming one is enough to draw a phone.

`backed` says whether *this* computer can stand behind the claim, and it turns on two things a page reads directly: the **OS**, because fonts and their metrics come from the real machine and no switch reaches them, and the **GPU vendor**, because `navigator.gpu` is not injected and reports the real adapter from a worker realm. The thread count is deliberately not one of them — `Emulation.setHardwareConcurrencyOverride` is applied by Chromium itself, so it holds in every realm. For a phone it is one question rather than three: whether this engine moves the whole mobile record below JavaScript. Machines that fail `backed` are still listed, because a profile may be destined for a different computer; they are marked rather than hidden, and choosing one raises a warning from the audit rather than an error.

#### Query parameters

| Field | Type | What it is |
| --- | --- | --- |
| `os` | `"windows" \| "macos" \| "linux" \| "android"` | Only this platform’s machines. |
| `backed` | `"true"` | Only machines this computer can stand behind. |

#### Returns

`{ total, devices: [{ id, label, chip, os, cores: number[], memory: number[], screens: string[], backed: boolean, gpu: GpuSpec }] }`

#### Errors

- 400 — `os` is not one of the four platforms.

## [Check a fingerprint that has not been saved](#audit-fingerprint)

POST`/api/v1/fingerprints/audit`

The same two checks as `/profiles/:id/fingerprint/validate`, against a fingerprint in the request body rather than a stored profile. This is what the editor calls while someone is still choosing: it lets a draft identity be checked before it belongs to anything. Send a whole fingerprint, as returned by `POST /fingerprints/generate` — a partial object is refused rather than half-checked, because a missing field would silently read as a coherent one.

#### Body

`Fingerprint`. A whole fingerprint object.

#### Returns

The same shape as `/profiles/:id/fingerprint/validate`.

#### Errors

- `400` if the body is not a whole fingerprint.

## [Run a detection audit](#audit-profile)

POST`/api/v1/profiles/:id/audit`

Drives the live browser through the probes a CreepJS-class detector uses: worker and iframe realm agreement, Function.prototype.toString integrity, property-descriptor shape, timezone against getTimezoneOffset, screen geometry, and automation traces such as navigator.webdriver, cdc_ symbols and window.chrome. Starts the profile headlessly if it is not already running, and stops it again afterwards.

#### Path parameters

| Field | Type | What it is |
| --- | --- | --- |
| `id`required | `string` | The record’s id. |

#### Returns

`{ passed, score, checks: [{ id, label, status: "pass"|"warn"|"fail", detail }], ranAt }`

## [Get a fresh seed](#new-seed)

GET`/api/v1/seed`

A random seed value suitable for the generator, when you want to record it before generating.

#### Returns

`{ seed: string }`

## Run the API on your own profiles

The REST API and the MCP server are part of Pro: $49 a month, with the first 14 days free. Free has every browser feature and no API key.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
