Guides

Migrate from another provider

Move an existing device identification integration to Fingerly: map the client call, server reads, signals and webhooks, then cut over without a gap.

Most device identification 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

What you use todayIn Fingerly
Load the client agent with a public keyload({ apiKey })
Identify the visitor at a moment that mattersfingerly.identify()
A linked ID and a tag or custom dataOne tag string
A visitor IDvisitorId, scoped to your organization
A match confidence, often 0 to 1visitorConfidence, 0 to 100
Risk signals and a risk scoreSignal groups, suspect_score and suspect_level
Read one result on your serverGET /api/v1/events/{request_id}
A visitor's historyGET /api/v1/events?visitor=…, 30 days
A webhook per identificationidentification.completed, and visitor.suspect with signals
A region optionNone. One API, one origin
A custom subdomain or a cloud proxyProxy integrations, or a hosted subdomain as a fallback

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. Allowed origins are required, not optional: 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 your current agent at the same moments and send both request IDs to your server. Keep deciding with your current provider, 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 your current provider's 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 old agent, its proxy routes and its keys.

Replace the JavaScript agent

fingerly.ts
import { load } from '@fingerly/web-js'

const fingerly = await load({
  apiKey: 'fly_pk_production_…',
  endpoints: '/k7x2qm4a',
  fallbackToDefaultEndpoint: true,
  consent: 'granted', // only once you have consent (or another lawful basis)
})

const { requestId, visitorId } = await fingerly.identify({ tag: 'login' })

Load options

What you set todayFingerlyNotes
A public API keyapiKeyA Fingerly public key, fly_pk_….
A regionNoneFingerly is one API at one origin. There is nothing to choose.
Custom endpointsendpointsThe path the dashboard issued for a proxy, such as /k7x2qm4a, or a URL. Posted to exactly as given, with nothing appended.
A fallback to the provider's own endpointfallbackToDefaultEndpoint: trueTry the Fingerly API after your own endpoints fail.
A script URL to proxyNoneThe SDK is bundled into your application. Nothing is downloaded at runtime, so there is no script to proxy.
NothingconsentDefaults to pending: nothing is collected until it is granted. Pass granted once you have consent or another lawful basis, or call setConsent() from your consent tool. See privacy and consent.

Identify options and result

What you use todayFingerlyNotes
A linked IDPart of tagFingerly has one reference field. Put the account or order in it, such as login or checkout:8412.
A tag of any JSONtag (a string)Encode what you need as a string. Your server compares it before trusting the result.
An extended resultNoneNetwork and device details are on the stored event your server reads.
A timeoutsignalPass an AbortSignal. Collection is already capped at 300 ms, and each attempt at 5 seconds.
The request IDrequestIdSend it to your server with the action.
The visitor IDvisitorIdTwenty letters and digits, scoped to your organization.
A confidence from 0 to 1visitorConfidence0 to 100. See visitor confidence.
Whether the visitor was found beforevisitorIsNewUsually the opposite sense.
An encrypted or sealed resultNoneRead 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 a React library

App.tsx
import { FingerlyProvider, useIdentify } from '@fingerly/react'

<FingerlyProvider apiKey="fly_pk_production_…" consent="granted">
  <App />
</FingerlyProvider>

const { identify, isLoading } = useIdentify({ tag: 'login' })
const { requestId } = await identify({ force: true })

useIdentify({ immediate: true }) identifies on mount, and refresh() identifies again, bypassing any cached result. Vue, Svelte, Angular and Next.js have the same shape: see the SDKs.

Mobile

On iOS and Android, replace your current client and its visitor ID calls with Fingerly.load(apiKey:consent:) and identify(tag:), passing a granted consent state once you have consent. The result has the same fields as on the web. See iOS and Android, or React Native and Flutter. The mobile SDKs are coming soon and not yet available to install.

Replace server reads

verify.ts
import { load } from '@fingerly/node'

const fingerly = load({ secretKey: process.env.FINGERLY_SECRET_KEY! })
const fired = (event: { triggers: Array<{ signal: string }> }, group: string) =>
  event.triggers.some((trigger) => trigger.signal === group)

const event = await fingerly.events.get(requestId)
const { visitor_id, tag, occurred_at } = event
const isBot = fired(event, 'bot')
const usesVpn = fired(event, 'vpn')
What you read todayFingerly event
The visitor IDvisitor_id
The request IDrequest_id
The linked ID and tagtag
The timestamp, often in millisecondsoccurred_at (RFC 3339)
The IP addressip_address
The IP location's countrycountry_code, country_name
The IP networkasn, asn_name, anonymity_network
Browser and device detailsagent, agent_version, os, device_kind
A risk scoresuspect_score, with suspect_level from your threshold
Each risk 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.

Risk signals and signal groups

Common risk signalFingerly groupNotes
Bad botbotAutomation, and clients that say they are bots.
Good botNoneRead visitor_kind: search_crawler or ai_bot.
VPNvpnWeighed by how it was recognised or by confidence. See weighting modes.
Tortor
Proxydatacenter_proxy, residential_proxyTwo groups, weighted separately.
IP blocklistip_blocklistNot active yet.
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
Location spoofinglocation_spoofing
Remote control toolsremote_controlBrowsers only. Not active yet.
Rooted device (Android)rooted_device
Emulator (Android)android_emulatorThe iOS Simulator is ios_simulator.
Cloned app (Android)cloned_app
Jailbroken device (iOS)jailbroken_device
Instrumentation frameworkfrida_detected
Man-in-the-middlemitm_attack
Factory resetdevice_farmA signal that the device looks freshly reset or mass-provisioned, not a reset timestamp.
Velocity countsNoneCount 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

Common todayFingerly
What is sentOne request per identification, with its signalsidentification.completed per identification; visitor.suspect with every signal when the level is high
Signature headerA provider-specific headerX-Fingerly-Signature: sha256=…, with X-Fingerly-Timestamp
Signed contentOften the body aloneThe timestamp, a full stop, and the body
Replay protectionOften none in the signatureReject timestamps more than five minutes old
DeduplicationOften by request IDBy the envelope id, also in X-Fingerly-Event-ID
Fingerly
import { verifyWebhook } from '@fingerly/node'

const valid = verifyWebhook({
  secret: process.env.FINGERLY_WEBHOOK_SECRET!,
  payload: rawBody,
  timestamp: req.get('x-fingerly-timestamp'),
  signature: req.get('x-fingerly-signature'),
})
const { type, data } = 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

Many providers' proxy integrations forward both an agent download and its requests. Fingerly's SDK is part of your bundle, so a Fingerly proxy forwards one path, the random one the dashboard issues, to one API URL. Use the Cloudflare Worker, a server SDK's proxy, a ready-made template for Vercel, Netlify, AWS CloudFront, Google Cloud Run, nginx, Caddy, Traefik or a container, or your own proxy. A custom subdomain maps to a hosted subdomain, with the same weakness against DNS-level blockers. The new path is different from the old one, so both can run during the switch.

Identities do not carry over

  • Store the Fingerly visitor_id next to your current provider's 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, from $0.0025 per production identification, falling with monthly volume, with no plans or minimums. See billing.
  • Retention. Events are readable for 30 days. Keep your own copy for longer. See data retention.
  • Consent. Every SDK takes a consent option, defaults it to pending, and collects nothing until it is granted. If your current agent collects as soon as it is called, pass granted or wire up your consent tool when you switch. See consent tools.