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/agentExample
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.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/reactExample
// 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 });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/vueExample
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 });Server
Node.js
@fingerprintchecker/node covers every backend endpoint, opens sealed results and verifies webhook signatures. No runtime dependencies.
Install
npm i @fingerprintchecker/nodeExample
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]);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 fingerprintcheckerExample
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"]])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
}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);
}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 installExample
// 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;
}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 restoreExample
// 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;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}")
}
}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: FingerPrintCheckerExample
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)")
}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 getExample
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)');
}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 installExample
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})`);
}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.
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.
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.
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.
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.
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.
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.