Antifrauder API v1 Open app

Checks · Device fingerprint

Device fingerprint

Detect spoofed devices, incognito sessions, automation and impossible-travel velocity from a real browser fingerprint. Because it needs to run in the visitor's browser, the device check is done with an embedded scanner — not a server-side JSON call.

There is no JSON device endpoint. Calling POST /api/v1/check/device returns 400 device_requires_browser — a fingerprint can only be collected from the browser via the embed below.
  • You add a one-tag scanner to the page you want to protect.
  • It collects a fingerprint and POSTs it to /embed/collect with your X-Site-Key.
  • We charge a device check, analyze it in-process, and return the scored visit to the browser.

Integrate

Create a scanner site key and add your page origins to its allowlist in the Antifrauder mini-app → Scanner. The scanner must be active, and the calling page's Origin must be allow-listed (else 403 origin_not_allowed).

HTML
<script src="https://cdn.antifrauder.app/scripts/embed.min.js"></script>
<script>
  // siteKey + secret come from the mini-app Scanner screen.
  Antifrauder.init({ siteKey: 'sk_live_…', secret: '<base64 key>' });

  // run a scan, e.g. on a login or checkout submit
  Antifrauder.scan({ externalId: 'order_1042' })
    .then(function (res) {
      if (res.intel.device.verdict === 'fraudulent') blockSubmit();
      sendToBackend(res.checkId);             // your server can also re-fetch via API
    })
    .catch(function (err) {
      console.warn('scan failed:', err.code); // insufficient_balance, origin_not_allowed, …
    });
</script>
embed.js loads scanner.js from the same CDN, runs the fingerprint, encrypts it with your scanner secret (AES-256-GCM) and POSTs only the ciphertext — the raw fingerprint never leaves the page in plaintext. scan() resolves with the analyzed visit.

Collect endpoint

The endpoint the embed calls. You normally don't call it by hand — but this is the exact contract. Note it lives at the root (not under /api/v1) and authenticates with X-Site-Key, not your API key.

POST /embed/collect

Headers

Header Required Description
X-Site-Key required Scanner site key (active). Bad/inactive → 401 invalid_site_key.
Content-Type required application/json
Origin required Sent by the browser; must be in the site key's allowlist.

Body parameters

Field Type Required Description
enc object required The fingerprint, AES-256-GCM encrypted with your scanner secret: { v, iv, data } (base64). The embed builds this for you; plaintext is rejected with 400 invalid_scan.
extraSignals object optional Extra context to fold into the analysis.
externalId string optional Your own correlation id (order, session, …).

Response

The consolidated intel plus a fraud section (the same shape as the other checks) — plus the transaction fields checkId, charged and balance.

200 — JSON
{
  "checkId": "665f1a9c8e4b2a0012ab34cd",
  "charged": 7,
  "balance": 4769,

  "intel": {
    "device": { "id": "a1b2c3d4e5f6", "visitorId": "a1b2c3d4e5f6a7b8c9d0",
                "confidence": "high", "verdict": "suspicious" },
    "identity": {
      "claimed": { "browser": "Safari 17", "os": "iOS 17" },
      "real":    { "browser": "Chrome 122", "os": "Windows 10" },
      "flags":   { "isOSMatched": false, "isBrowserMatched": false, "isTimezoneMatched": true }
    },
    "network": { "ip": { "address": "45.155.205.16" },
                 "location": { "city": "Amsterdam", "country": "NL" } },
    "velocity": { "device": { "5m": 1, "1h": 2, "24h": 5 }, "ip": { "5m": 1, "1h": 3, "24h": 9 } },
    "firstSeen": "2026-06-10T08:31:00.000Z",
    "signals": { "isBot": false, "isDeviceSpoofed": true, "isLocationSpoofed": false, "isIncognito": true },
    "meta": { "sources": { "ok": 1, "total": 1 } }
  },
  "fraud": {
    "score": 62, "level": "high",
    "negative": [
      { "code": "DV-R-26", "key": "real_os_vs_claimed_os_mismatch", "name": "Real OS vs Claimed OS Mismatch",
        "description": "The detected OS contradicts the one claimed by the User-Agent.", "points": 15 }
    ],
    "positive": []
  }
}

Result fields

Field Type Description
intel.device object id (stable device id), visitorId, confidence, and verdict (clean · suspicious · fraudulent).
intel.identity object claimed vs real device attributes and the per-attribute match flags (isOSMatched, isTimezoneMatched, …) — divergence drives the risk signals.
intel.network object ip, location and webrtc of the visitor.
intel.velocity · intel.firstSeen object · string Scan counts per device/IP over 5m/1h/24h; when first seen (null if new).
intel.signals object Boolean summary flags: isBot, isDeviceSpoofed, isLocationSpoofed, isIncognito, … .
fraud object { score, level, negative[], positive[] } — each signal with a stable code (DV-*), name, description, points. Device signals are all risk (negative).

Errors

All errors set the CORS header so the calling page can read them; the embed surfaces the code on scan().catch(err => err.code).

Status Code Meaning
400 invalid_scan Missing or malformed scan data.
401 invalid_site_key Unknown key or scanner not active.
403 origin_not_allowed Page origin not in the allowlist.
402 insufficient_balance Account balance too low for a device check.
429 rate_limited Per-site-key rate limit exceeded.
502 analysis_failed Engine error; the charge was refunded.