The two products share a shape: a client agent identifies the visitor and returns a request ID, and your server reads the result with a secret key. Most of a migration is renaming. This guide maps each part, then walks through switching over without a gap in protection.
At a glance
| Concept | FingerprintJS Pro | Fingerly |
|---|---|---|
| Load the agent | FingerprintJS.load({ apiKey }) | load({ apiKey }) |
| Identify | fp.get() | fingerly.identify() |
| Your reference | linkedId and tag | One tag string |
| Visitor | visitorId | visitorId, scoped to your organization |
| Match confidence | confidence.score, 0 to 1 | visitorConfidence, 0 to 100 |
| Risk | Smart Signals, Suspect Score | Signal groups, suspect_score and suspect_level |
| Read one result | Server API GET /events/{request_id} | GET /api/v1/events/{request_id} |
| Visitor history | Server API GET /visitors/{visitor_id} | GET /api/v1/events?visitor=…, 30 days |
| Webhooks | One per identification, with Smart Signals | identification.completed, and visitor.suspect with signals |
| Region | region load option | Part of the key |
| First-party requests | Custom subdomain or cloud proxy integrations | Proxy integrations |
Plan the switch
Step 1: Create Fingerly keys
Create a public key with your site's origins and a secret key, first in development, then in production. Unlike FingerprintJS Pro's optional request filtering, allowed origins are required: a public key with none refuses every browser request. See API keys.
Step 2: Run both agents side by side
Call Fingerly next to FingerprintJS Pro at the same moments and send both request IDs to your server. Keep deciding with FingerprintJS Pro, and log what Fingerly would have decided.
Step 3: Move server checks
When the two agree on the sessions you care about, switch your decisions to the Fingerly event. Keep reading FingerprintJS Pro results for a few days as a fallback.
Step 4: Move webhooks
Register Fingerly endpoints, verify their signatures, and point your consumers at them.
Step 5: Remove the old agent
Delete the FingerprintJS Pro agent, its proxy routes and its keys.
Replace the JavaScript agent
import * as FingerprintJS from '@fingerprintjs/fingerprintjs-pro'
const fp = await FingerprintJS.load({
apiKey: '<public api key>',
region: 'eu',
endpoint: ['https://metrics.example.com', FingerprintJS.defaultEndpoint],
})
const { requestId, visitorId } = await fp.get({ linkedId: user.id, tag: { action: 'login' } })
Load options
| FingerprintJS Pro | Fingerly | Notes |
|---|---|---|
apiKey | apiKey | A Fingerly public key, fly_pk_…. |
region | None | The region is part of the key, and the SDK routes by it. |
endpoint | endpoints | Paths or URLs for a proxy. /api/v1/identify is appended. |
FingerprintJS.defaultEndpoint in the list | fallbackToDefaultEndpoint: true | Try the regional API after your own endpoints fail. |
scriptUrlPattern | None | The SDK is bundled into your application. Nothing is downloaded at runtime, so there is no script to proxy. |
get() options and result
| FingerprintJS Pro | Fingerly | Notes |
|---|---|---|
linkedId | Part of tag | Fingerly has one reference field. Put the account or order in it, such as login or checkout:8412. |
tag (any JSON) | tag (a string) | Encode what you need as a string. Your server compares it before trusting the result. |
extendedResult | None | Network and device details are on the stored event your server reads. |
timeout | signal | Pass an AbortSignal. Collection is already capped at 300 ms, and each attempt at 5 seconds. |
requestId | requestId | Send it to your server with the action. |
visitorId | visitorId | Twenty letters and digits, scoped to your organization. |
confidence.score | visitorConfidence | 0 to 100 rather than 0 to 1. See visitor confidence. |
visitorFound | visitorIsNew | The opposite sense. |
sealedResult | None | Read the event on your server with a secret key instead. |
Fingerly also returns the score, the level and the signals in the same response, and local verdicts computed in the browser. Use them to adapt the interface; decide on your server.
Replace the React library
import { FpjsProvider, useVisitorData } from '@fingerprintjs/fingerprintjs-pro-react'
<FpjsProvider loadOptions={{ apiKey: '<public api key>' }}>
<App />
</FpjsProvider>
const { getData, isLoading } = useVisitorData({ extendedResult: true }, { immediate: false })
const { requestId } = await getData({ ignoreCache: true })
useIdentify({ immediate: true }) replaces immediate: true, and refresh() replaces getData({ ignoreCache: true }). Vue, Svelte, Angular and Next.js have the same shape: see the SDKs.
Mobile
On iOS and Android, replace the FingerprintJS Pro client and its visitor ID calls with Fingerly.load(apiKey:) and identify(tag:). The result has the same fields as on the web. See iOS and Android, or React Native and Flutter.
Replace server reads
import { FingerprintJsServerApiClient, Region } from '@fingerprintjs/fingerprintjs-pro-server-api'
const client = new FingerprintJsServerApiClient({ apiKey: process.env.FP_SECRET_API_KEY!, region: Region.EU })
const event = await client.getEvent(requestId)
const { visitorId, linkedId, timestamp } = event.products.identification.data
const isBot = event.products.botd?.data?.bot.result === 'bad'
const usesVpn = event.products.vpn?.data?.result === true
| FingerprintJS Pro | Fingerly event |
|---|---|
products.identification.data.visitorId | visitor_id |
products.identification.data.requestId | request_id |
products.identification.data.linkedId, tag | tag |
products.identification.data.timestamp (milliseconds) | occurred_at (RFC 3339) |
products.identification.data.ip | ip_address |
ipLocation country | country_code, country_name |
products.ipInfo network | asn, asn_name, anonymity_network |
browserDetails | agent, agent_version, os, device_kind |
products.suspectScore.data.result | suspect_score, with suspect_level from your threshold |
Each Smart Signal's result | A group in triggers, with its weight and confidence |
An event lists only the signal groups that fired. A group that is absent did not fire. Server libraries for Python, Go, Java, .NET, PHP, Ruby and Rust make the same calls.
Smart Signals and signal groups
| Smart Signal | Fingerly group | Notes |
|---|---|---|
Bot detection, bad | bot | Automation, and clients that say they are bots. |
Bot detection, good | None | Read visitor_kind: search_crawler or ai_bot. |
| VPN detection | vpn | Weighed by how it was recognised or by confidence. See weighting modes. |
| Tor | tor | |
| Proxy detection | datacenter_proxy, residential_proxy | Two groups, weighted separately. |
| IP blocklist | ip_blocklist | |
| Incognito mode | incognito_mode | |
| Browser tampering | browser_tampering | |
| Virtual machine | virtual_machine | |
| Privacy-focused settings | privacy_settings | Also fingerprint_suppressed when too little can be read to identify anyone. |
| Developer tools | developer_tools | |
| High-activity device | high_activity | |
| Geolocation spoofing | location_spoofing | |
| Remote control tools | remote_control | Browsers only. |
| Root apps (Android) | rooted_device | |
| Emulator (Android) | android_emulator | The iOS Simulator is ios_simulator. |
| Cloned app (Android) | cloned_app | |
| Jailbroken device (iOS) | jailbroken_device | |
| Frida detection | frida_detected | |
| MITM attack | mitm_attack | |
| Factory reset | device_farm | A signal that the device looks freshly reset or mass-provisioned, not a reset timestamp. |
| Velocity signals | None | Count per visitor_id in your own store. high_activity covers devices seen far more often than usual. |
| Raw device attributes | None | Fingerly does not expose what it collects from a device. |
Every group has a weight you can change, per organization or per key, and the score is their sum. See signals and risk weights.
Replace webhooks
| FingerprintJS Pro | Fingerly | |
|---|---|---|
| What is sent | One request per identification, with Smart Signals | identification.completed per identification; visitor.suspect with every signal when the level is high |
| Signature header | FPJS-Event-Signature: v1=… | X-Fingerly-Signature: sha256=…, with X-Fingerly-Timestamp |
| Signed content | The body | The timestamp, a full stop, and the body |
| Replay protection | None in the signature | Reject timestamps more than five minutes old |
| Deduplication | By requestId | By the envelope id, also in X-Fingerly-Event-ID |
import { isValidWebhookSignature } from '@fingerprintjs/fingerprintjs-pro-server-api'
const valid = isValidWebhookSignature({
header: req.get('fpjs-event-signature'),
data: rawBody,
secret: process.env.FP_WEBHOOK_SECRET!,
})
const visit = JSON.parse(rawBody.toString())
If your consumer needs every signal for every identification, read the event by data.request_id when identification.completed arrives. See webhooks.
Proxy integrations
FingerprintJS Pro's proxy integrations forward both the agent download and its requests. Fingerly's SDK is part of your bundle, so a Fingerly proxy only forwards two API routes. Use the Cloudflare Worker, createProxy for Node.js runtimes, or your own proxy. Choose a new path rather than reusing the old one, so both can run during the switch.
Identities do not carry over
- Store the Fingerly
visitor_idnext to the FingerprintJS Pro one on your accounts, orders and devices while both run. - Rules such as "known device for this account" start empty. Loosen new-device friction during the switch, or keep consulting the old IDs until customers have returned once.
- Counters per visitor, such as sign-ups per device, restart from zero. Keep old limits running on the old IDs until the window they cover has passed.
Other differences
- Environments. Keys belong to development, staging or production. Development and staging run the same detection and are free. See environments.
- Pricing. Prepaid, $0.003 per production identification, with no plans or minimums. See billing.
- Retention. Events are readable for 30 days. Keep your own copy for longer. See data retention.
- Regions. The United States region is available; the European Union region is coming soon. See regions.
- Consent. Every SDK takes a
consentoption and collects nothing until it isgranted. See consent tools.