SDK libraries for web, server, Android and iOS

Clients only collect signals and return one opaque attestation; your backend asks for the decision. Web, mobile and server SDKs are open source under Apache-2.0, and each family below shows where it installs from. The server SDKs wrap every backend endpoint, open sealed results and verify webhook signatures.

The PHP and .NET libraries install from the GitHub release for now, with the steps below. Java installs from Maven Central.

Browser

Web agent

@fingerprintchecker/agent loads the browser agent once per page and identifies the visitor with an intent token from your server.

Install

npm i @fingerprintchecker/agent

Example

import { load } from '@fingerprintchecker/agent';

const fp = await load({ publishableKey: 'pk_live_...' });
// intentToken comes from your server (POST /v2/intents)
const { eventId, sealedResult, attestation } = await fp.identify({ intentToken, linkedId: 'order-42' });
// Send eventId, sealedResult and attestation to your backend, which decides.

Source and README on GitHub →

Browser

React and Next.js

@fingerprintchecker/react is a provider and a useFpChecker function on top of the agent. It works in the Next.js App Router from any client component.

Install

npm i @fingerprintchecker/agent @fingerprintchecker/react

Example

// app/fp-provider.tsx
'use client';
import { FpCheckerProvider } from '@fingerprintchecker/react';

export function Providers({ children }: { children: React.ReactNode }) {
  return <FpCheckerProvider publishableKey={process.env.NEXT_PUBLIC_FPCHECKER_KEY!}>{children}</FpCheckerProvider>;
}

// In a client component, from an event handler:
// const { identify, isLoading, error } = useFpChecker();
// const result = await identify({ intentToken });

Source and README on GitHub →

Browser

Vue and Nuxt

@fingerprintchecker/vue is a plugin and a composable on top of the agent. It never loads the agent during server rendering.

Install

npm i @fingerprintchecker/agent @fingerprintchecker/vue

Example

import { createApp } from 'vue';
import { fpChecker, useFpChecker } from '@fingerprintchecker/vue';

createApp(App).use(fpChecker, { publishableKey: 'pk_live_...' }).mount('#app');

// In a component's setup:
// const { identify, data, error, isLoading } = useFpChecker();
// const result = await identify({ intentToken });

Source and README on GitHub →

Server

Node.js

@fingerprintchecker/node covers every backend endpoint, opens sealed results and verifies webhook signatures. No runtime dependencies.

Install

npm i @fingerprintchecker/node

Example

import { FpChecker, unsealResult } from '@fingerprintchecker/node';

const fp = new FpChecker({ secretKey: process.env.FPCHECKER_SECRET_KEY });

const verdict = await fp.checkEligibility({ attestation });
if (verdict.eligible !== true) throw new Error('refused: ' + (verdict.reason ?? verdict.error));

// Or open the sealed result locally, with every active key:
const result = unsealResult(sealedResult, [process.env.FPC_SEALED_KEY]);

Source and README on GitHub →

Server

Python

fingerprintchecker (import fpchecker) has one method per endpoint, opens sealed results and verifies webhook signatures. Python 3.9 and later.

Install

pip install fingerprintchecker

Example

import os
from fpchecker import FpChecker, unseal_result

fp = FpChecker(os.environ["FPCHECKER_SECRET_KEY"])

verdict = fp.check_eligibility({"attestation": attestation})
if verdict.get("eligible") is not True:
    raise PermissionError(verdict.get("reason") or verdict.get("error"))

# Or open the sealed result locally, with every active key:
result = unseal_result(sealed_result, [os.environ["FPC_SEALED_KEY"]])

Source and README on GitHub →

Server

Go

The Go module uses only the standard library. It covers every backend endpoint, sealed results and webhook signatures.

Install

go get github.com/AndrewYukon/FingerPrintChecker/server/[email protected]

Example

c, err := fpchecker.New(os.Getenv("FPC_SECRET_KEY"))
if err != nil {
	log.Fatal(err)
}

verdict, err := c.CheckEligibility(ctx, map[string]any{"attestation": attestation}) // json.RawMessage, decode into your own type
var apiErr *fpchecker.APIError
if errors.As(err, &apiErr) {
	log.Println(apiErr.Status, apiErr.Code)
}
log.Println(string(verdict)) // grant only when eligible is true

// Or open the sealed result locally, with every active key:
res, err := fpchecker.UnsealResult(sealedResult, []string{activeKey, previousKey})
if err == nil {
	log.Println(res.EventID, res.ProjectID) // check ProjectID, age and EventID before use
}

Source and README on GitHub →

Server

Java

The Java library on Maven Central (Java 11 and later) has one method per backend endpoint and returns Jackson JSON trees.

Install

implementation("com.fpchecker:fpchecker-server-java:0.1.0")

Example

static final Pattern EVENT_ID = Pattern.compile("^ev_[0-9A-HJKMNP-TV-Z]{20}$");
static final Set<String> used = ConcurrentHashMap.newKeySet(); // use a shared store with expiry in production

// The browser sends an eventId. Do not trust it: read /v1/events/<id> with your secret key.
static boolean verifyEvent(FpChecker fp, String eventId) {
    if (eventId == null || !EVENT_ID.matcher(eventId).matches()) return false;
    JsonNode event;
    try {
        event = fp.getEvent(eventId);
    } catch (FpCheckerApiException e) {
        return false; // unknown event or an outage: do not accept
    }
    if (!eventId.equals(event.path("eventId").asText(null))) return false;
    try {
        Instant createdAt = Instant.parse(event.path("createdAt").asText(""));
        if (Math.abs(Duration.between(createdAt, Instant.now()).toMillis()) > Duration.ofMinutes(5).toMillis()) return false;
    } catch (DateTimeParseException e) {
        return false; // an unreadable createdAt fails closed
    }
    return markEventUsed(eventId); // each eventId counts once
}

static boolean markEventUsed(String eventId) {
    return used.add(eventId);
}

Source and README on GitHub →

Server

PHP

fpchecker/fpchecker-php has one method per backend endpoint and returns plain arrays. PHP 8.2 and later with curl, json and openssl.

Install

# 1. Download fpchecker-php-0.1.0.zip from https://github.com/AndrewYukon/FingerPrintChecker/releases/tag/server/php/v0.1.0 into vendor-packages/
# 2. In composer.json:
{
    "repositories": [
        { "type": "artifact", "url": "vendor-packages/" }
    ],
    "require": {
        "fpchecker/fpchecker-php": "0.1.0"
    }
}
# 3. Then run:
composer install

Example

// The browser sends an eventId. Do not trust it: read /v1/events/<id> with your secret key.
function verifyEvent(\FpChecker\FpChecker $fp, string $eventId): ?array
{
    if (preg_match('/\Aev_[0-9A-HJKMNP-TV-Z]{20}\z/', $eventId) !== 1) {
        return null;
    }
    try {
        $event = $fp->getEvent($eventId);
    } catch (\FpChecker\FpCheckerApiError $e) {
        return null; // unknown event or an outage: do not accept
    }
    if (($event['eventId'] ?? null) !== $eventId) {
        return null;
    }
    // createdAt looks like 2026-10-27T12:00:00Z or 2026-10-27T12:00:00.123Z
    $createdAt = null;
    foreach (['!Y-m-d\TH:i:sp', '!Y-m-d\TH:i:s.vp'] as $format) {
        $createdAt ??= \DateTimeImmutable::createFromFormat($format, is_string($event['createdAt'] ?? null) ? $event['createdAt'] : '') ?: null;
    }
    if ($createdAt === null || abs(time() - $createdAt->getTimestamp()) > 300) {
        return null; // too old, or an unreadable createdAt: fail closed
    }
    return markEventUsed($eventId) ? $event : null; // each eventId counts once
}

function markEventUsed(string $eventId): bool
{
    // Your store: true only the first time (a database unique key, Redis SET NX).
    return true;
}

Source and README on GitHub →

Server

.NET

FingerPrintChecker.Server has one async method per backend endpoint and returns JsonElement. .NET 8, no runtime dependency beyond the base class library.

Install

# 1. Download FingerPrintChecker.Server.0.1.0.nupkg from https://github.com/AndrewYukon/FingerPrintChecker/releases/tag/server/dotnet/v0.1.0
mkdir -p local-packages && cp FingerPrintChecker.Server.0.1.0.nupkg local-packages/
dotnet nuget add source "$(pwd)/local-packages" --name fpchecker-local
# 2. In your .csproj:
<PackageReference Include="FingerPrintChecker.Server" Version="0.1.0" />
# 3. Then run:
dotnet restore

Example

// The browser sends an eventId. Do not trust it: read /v1/events/<id> with your secret key.
const string EventIdAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";

public static async Task<JsonElement?> VerifyEventAsync(FpChecker fp, string eventId)
{
    // ev_ followed by 20 Crockford base32 characters
    if (eventId.Length != 23 || !eventId.StartsWith("ev_", StringComparison.Ordinal)
        || !eventId.Skip(3).All(ch => EventIdAlphabet.Contains(ch)))
    {
        return null;
    }

    JsonElement ev;
    try
    {
        ev = await fp.GetEventAsync(eventId);
    }
    catch (FpCheckerApiException)
    {
        return null; // unknown event or an outage: do not accept
    }

    if (!ev.TryGetProperty("eventId", out var id) || id.GetString() != eventId) return null;

    if (!ev.TryGetProperty("createdAt", out var c)
        || c.ValueKind != JsonValueKind.String
        || !DateTimeOffset.TryParse(c.GetString(), CultureInfo.InvariantCulture,
               DateTimeStyles.RoundtripKind | DateTimeStyles.AssumeUniversal, out var createdAt)
        || Math.Abs((DateTimeOffset.UtcNow - createdAt).TotalSeconds) > 300)
    {
        return null; // too old, or an unreadable createdAt: fail closed
    }

    return MarkEventUsed(eventId) ? ev : null; // each eventId counts once
}

// Your store: true only the first time (a database unique key, Redis SET NX).
static bool MarkEventUsed(string eventId) => true;

Source and README on GitHub →

Mobile

Android

A Kotlin library on Maven Central for Android 6.0 and later, with an optional Play Integrity proof. Returns the sealed result; from 0.7.0 also runs device checks such as root, hooking and location spoofing.

Install

implementation("com.fpchecker:fpchecker-android:0.7.0")

Example

val checker = FpChecker(context, FpCheckerConfig(publishableKey = "pk_..."))

lifecycleScope.launch {
    when (val r = checker.identify(intentToken, FpEventOptions(linkedId = "order_42"))) {
        is FpIdentifyResult.Success -> sendToBackend(r.attestation, r.sealedResult) // your backend decides
        is FpIdentifyResult.Failure -> log("identify failed: ${r.reason}")
    }
}

Source and README on GitHub →

Mobile

iOS and macOS

A Swift package for iOS 14 and later and macOS 11 and later, with App Attest and DeviceCheck proofs. Returns the sealed result; from tag 0.7.0 also runs device checks such as jailbreak, hooking and location spoofing.

Install

.package(url: "https://github.com/AndrewYukon/FingerPrintChecker", from: "0.7.0") // product: FingerPrintChecker

Example

import FingerPrintChecker

let checker = FingerPrintChecker(config: FPConfig(publishableKey: "pk_..."))

switch await checker.identify(intentToken: token, linkedId: "order_42") {
case .success(let r): try await sendToBackend(r.attestation, r.sealedResult) // your backend decides
case .failure(let reason, let httpStatus): print("identify failed: \(reason) \(httpStatus)")
}

Source and README on GitHub →

Mobile

Flutter

A Flutter plugin over the native Android and iOS SDKs. It is installed from Git only. Android minSdk 23, iOS 14 and later.

Install

# pubspec.yaml
dependencies:
  fingerprintchecker:
    git:
      url: https://github.com/AndrewYukon/FingerPrintChecker
      path: flutter
      ref: flutter/v0.1.1
# Then run:
flutter pub get

Example

import 'package:fingerprintchecker/fingerprintchecker.dart';

final fp = FingerPrintChecker(const FpConfig(publishableKey: 'pk_...'));

// Your backend creates the intent with the secret key (POST /v2/intents). Never put the secret key in the app.
final intentToken = await fetchIntentFromYourBackend();

final r = await fp.identify(intentToken, linkedId: 'order_42', tags: {'plan': 'free'});
switch (r) {
  case FpSuccess():
    // Your backend verifies the event and decides.
    await sendToBackend(eventId: r.eventId, attestation: r.attestation);
  case FpFailure(:final reason, :final httpStatus):
    debugPrint('identify failed: $reason ($httpStatus)');
}

Source and README on GitHub →

Mobile

React Native

@fingerprintchecker/react-native is a module over the native Android and iOS SDKs. React Native 0.76 and later. On Expo it needs a development build or prebuild, not Expo Go.

Install

npm install @fingerprintchecker/react-native
cd ios && pod install

Example

import { FingerPrintChecker } from '@fingerprintchecker/react-native';

const fp = new FingerPrintChecker({ publishableKey: 'pk_...' });

// Your backend creates the intent with the secret key (POST /v2/intents). Never put the secret key in the app.
const intentToken = await fetchIntentFromYourBackend();

const r = await fp.identify(intentToken, { linkedId: 'order_42', tags: { plan: 'free' } });
if (r.ok) {
  // Your backend verifies the event and decides.
  await sendToBackend({ eventId: r.eventId, attestation: r.attestation });
} else {
  console.warn(`identify failed: ${r.reason} (${r.httpStatus})`);
}

Source and README on GitHub →

How the pieces fit

Your server creates an intent with a server SDK, the web agent or a mobile SDK identifies the device with it, and your server sends the result back to a server SDK for the decision. The secret key never leaves your server.

Read the developer guide →

Browser script tag

Without a bundler, download the agent from https://fpchecker.com/static/fp-agent.min.js (version 0.8.0 or later for identifyV2; 0.9.0 or later for linkedId, tags and eventId) and serve it from your own domain. The web steps are in the developer guide.

Web integration steps →

Where each SDK installs from

The developer guide shows the exact install line and an example for each. Versions change with every release, so they are not repeated here.

  • Web agent, React and Vue wrappers: npm (@fingerprintchecker/agent, @fingerprintchecker/react, @fingerprintchecker/vue).
  • Android: Maven Central.
  • iOS and macOS: Swift Package Manager, from the GitHub repository.
  • Flutter: a git tag of the GitHub repository in your pubspec.yaml.
  • React Native: npm (@fingerprintchecker/react-native).
  • Node.js server SDK: npm (@fingerprintchecker/node).
  • Python server SDK: PyPI (fingerprintchecker).
  • Go server SDK: a Go module, fetched with go get at a version tag.
  • Java server SDK: Maven Central.
  • PHP and .NET server SDKs: the GitHub release.

Maven Central →

CLI and MCP server

The fpchecker command line and a local MCP server for Claude Desktop, Cursor and Claude Code run with npx -y @fingerprintchecker/cli. They use the Management API and are read-only unless you turn writes on.

Build with AI →

Command line

A command-line client for tools, tests and scripts has no library. It has no platform attestation, so it is always scored as low trust. Examples are in the repository.

CLI examples on GitHub →

Native apps must use the SDK

Do not call the identify endpoint from an app yourself. Requests must carry the exact bytes the SDK builds, and the platform proofs are bound to them.

Read why in the developer guide →

Frequently asked questions

Which SDK do I need for a web sign-up?

The web agent (or the React or Vue wrapper) in the page, and one server SDK on your backend. The page identifies the visitor; the backend decides.

Are there SDKs for Flutter, React Native or Angular?

Yes for Flutter and React Native. The Flutter plugin installs from a git tag of the open-source repository, and the React Native module is on npm. Both sit on top of the native Android and iOS SDKs. Angular has no wrapper of its own: the web agent is a plain script and works in any framework, and every backend endpoint is plain HTTP.

Are the packages on npm and PyPI?

Yes. The web agent, React and Vue wrappers, React Native module and Node.js server SDK are on npm, and the Python server SDK is on PyPI. Android and the Java server SDK are on Maven Central, Go is a Go module, iOS is a Swift package, Flutter installs from a git tag, and PHP and .NET install from the GitHub release.

Try it on your own sign-up flow

Every new account gets 2,000 free checks. No card required.