Guides

Migrate from FingerprintJS Pro

Move a FingerprintJS Pro integration to Fingerly: map get() to identify(), Smart Signals to signal groups, and their webhooks to Fingerly webhooks, without downtime.

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

ConceptFingerprintJS ProFingerly
Load the agentFingerprintJS.load({ apiKey })load({ apiKey })
Identifyfp.get()fingerly.identify()
Your referencelinkedId and tagOne tag string
VisitorvisitorIdvisitorId, scoped to your organization
Match confidenceconfidence.score, 0 to 1visitorConfidence, 0 to 100
RiskSmart Signals, Suspect ScoreSignal groups, suspect_score and suspect_level
Read one resultServer API GET /events/{request_id}GET /api/v1/events/{request_id}
Visitor historyServer API GET /visitors/{visitor_id}GET /api/v1/events?visitor=…, 30 days
WebhooksOne per identification, with Smart Signalsidentification.completed, and visitor.suspect with signals
Regionregion load optionPart of the key
First-party requestsCustom subdomain or cloud proxy integrationsProxy integrations

Plan the switch

  1. 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.

  2. 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.

  3. 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.

  4. Step 4: Move webhooks

    Register Fingerly endpoints, verify their signatures, and point your consumers at them.

  5. 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 ProFingerlyNotes
apiKeyapiKeyA Fingerly public key, fly_pk_….
regionNoneThe region is part of the key, and the SDK routes by it.
endpointendpointsPaths or URLs for a proxy. /api/v1/identify is appended.
FingerprintJS.defaultEndpoint in the listfallbackToDefaultEndpoint: trueTry the regional API after your own endpoints fail.
scriptUrlPatternNoneThe SDK is bundled into your application. Nothing is downloaded at runtime, so there is no script to proxy.

get() options and result

FingerprintJS ProFingerlyNotes
linkedIdPart of tagFingerly 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.
extendedResultNoneNetwork and device details are on the stored event your server reads.
timeoutsignalPass an AbortSignal. Collection is already capped at 300 ms, and each attempt at 5 seconds.
requestIdrequestIdSend it to your server with the action.
visitorIdvisitorIdTwenty letters and digits, scoped to your organization.
confidence.scorevisitorConfidence0 to 100 rather than 0 to 1. See visitor confidence.
visitorFoundvisitorIsNewThe opposite sense.
sealedResultNoneRead 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 ProFingerly event
products.identification.data.visitorIdvisitor_id
products.identification.data.requestIdrequest_id
products.identification.data.linkedId, tagtag
products.identification.data.timestamp (milliseconds)occurred_at (RFC 3339)
products.identification.data.ipip_address
ipLocation countrycountry_code, country_name
products.ipInfo networkasn, asn_name, anonymity_network
browserDetailsagent, agent_version, os, device_kind
products.suspectScore.data.resultsuspect_score, with suspect_level from your threshold
Each Smart Signal's resultA 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 SignalFingerly groupNotes
Bot detection, badbotAutomation, and clients that say they are bots.
Bot detection, goodNoneRead visitor_kind: search_crawler or ai_bot.
VPN detectionvpnWeighed by how it was recognised or by confidence. See weighting modes.
Tortor
Proxy detectiondatacenter_proxy, residential_proxyTwo groups, weighted separately.
IP blocklistip_blocklist
Incognito modeincognito_mode
Browser tamperingbrowser_tampering
Virtual machinevirtual_machine
Privacy-focused settingsprivacy_settingsAlso fingerprint_suppressed when too little can be read to identify anyone.
Developer toolsdeveloper_tools
High-activity devicehigh_activity
Geolocation spoofinglocation_spoofing
Remote control toolsremote_controlBrowsers only.
Root apps (Android)rooted_device
Emulator (Android)android_emulatorThe iOS Simulator is ios_simulator.
Cloned app (Android)cloned_app
Jailbroken device (iOS)jailbroken_device
Frida detectionfrida_detected
MITM attackmitm_attack
Factory resetdevice_farmA signal that the device looks freshly reset or mass-provisioned, not a reset timestamp.
Velocity signalsNoneCount per visitor_id in your own store. high_activity covers devices seen far more often than usual.
Raw device attributesNoneFingerly 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 ProFingerly
What is sentOne request per identification, with Smart Signalsidentification.completed per identification; visitor.suspect with every signal when the level is high
Signature headerFPJS-Event-Signature: v1=…X-Fingerly-Signature: sha256=…, with X-Fingerly-Timestamp
Signed contentThe bodyThe timestamp, a full stop, and the body
Replay protectionNone in the signatureReject timestamps more than five minutes old
DeduplicationBy requestIdBy 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_id next 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 consent option and collects nothing until it is granted. See consent tools.