Migrating off FingerprintJS Pro is rarely about the integration code, which is a day of work. The hard part is continuity. Your fraud rules, allow-lists, and account-to-device history are all keyed on FingerprintJS visitor IDs, and a new engine will not reproduce those IDs. Cut over naively and you throw away the device history that makes your detection valuable, treating every returning user as brand new.
The safe path is a parallel run: operate both systems side by side, build a mapping between the old and new identifiers, let the new device history mature, and only then move decisions across. This guide walks through that migration end to end, from why the IDs differ to how to cut over without a blind spot.
Why the IDs will not match
A visitor ID is the output of a specific scoring and hashing pipeline. Two device intelligence engines observe overlapping signals but weight, normalize, and combine them differently, so the same physical device produces different IDs in each system. There is no conversion function; the IDs are not translations of each other.
This has direct consequences for anything keyed on the old ID:
- Account-to-device links that power new device login detection do not transfer automatically.
- Device-level allow-lists and block-lists need to be rebuilt against new IDs.
- Velocity and rate limiting by device counters start empty.
Accepting this up front is what makes the migration safe. The plan is not to convert IDs but to relearn the mapping through observation, which the parallel run provides. The fingerprintjs alternative overview covers why teams make this move in the first place.
Step 1: run both agents in parallel
The first phase changes nothing about your decisions. You load both the existing FingerprintJS agent and the new one on the same pages, capturing both IDs for every session without acting on the new one yet.
// Old and new agents side by side during the parallel run.
const [fpjs, prynt] = await Promise.all([
FingerprintJS.load().then(a => a.get()),
Prynt.load().then(c => c.get()),
]);
telemetry.record({
legacyId: fpjs.visitorId,
newDeviceId: prynt.deviceId,
newSignals: prynt.signals,
ts: Date.now(),
});
Every session now yields a pair: the old visitor ID and the new device ID observed on the same device at the same moment. Those pairs are the raw material for the mapping. This is also your accuracy check; you can compare how the two engines agree on returning-visitor recognition before trusting the new one, using the criteria in self-hosted vs saas fraud.
Step 2: build the ID mapping
With paired observations flowing, construct a mapping table linking legacy IDs to new device IDs. Because both engines have some instability, the mapping is many-to-many at first and firms up with repeated observations.
| Legacy visitor ID | New device ID | Observations | Confidence |
|---|---|---|---|
| fpjs_a1b2 | prynt_9x8y | 14 | High |
| fpjs_c3d4 | prynt_7w6v | 2 | Low |
| fpjs_c3d4 | prynt_5u4t | 1 | Low, likely noise |
Use the observation count to gate confidence: a legacy ID seen many times against the same new ID is a strong link, while a single co-occurrence may be coincidence or transient instability. This is the same identity-graph reasoning described in identity graph fraud rings, applied to migration rather than fraud. Once the mapping is confident, you can port your allow-lists and account-device history onto the new IDs.
Step 3: shadow your decisions
Before the new system controls anything, run it in shadow mode. Feed the new signals through your fraud rules and log what decision it would have made, without enforcing it. Compare against the live decisions from the old system.
Watch for:
- Divergences where the new stack would block a user the old one allowed, or vice versa. Investigate each class of disagreement.
- False-positive drift. Confirm the new suspect score is calibrated to your risk tolerance before it can decline real users.
- Coverage gaps. Make sure Smart Signals you relied on have equivalents, and that the reason codes map to your existing playbooks.
Shadow mode turns the cutover from a leap into a measured step. You are not guessing whether the new system behaves well; you have a log of exactly how it would have decided against real traffic.
Step 4: cut over and decommission
When the mapping is mature and shadow decisions agree with your standards, move enforcement to the new system. Do it gradually rather than all at once.
- Ramp by traffic slice. Enforce with the new stack for a small percentage of sessions, watch the metrics, and increase.
- Keep the old agent running during the ramp so you retain the mapping and a rollback path.
- Migrate the history so account-device links, allow-lists, and velocity counters are populated on new IDs before they drive decisions.
- Decommission the old agent once the new one holds full traffic and the metrics are stable.
Because you are moving to a self-hosted stack, the cutover also changes where data lives: device and IP data now stay inside your perimeter, which is a large part of the motivation covered in why self-host fraud detection and self-hosting for data residency.
What you gain and what to watch
The payoff of the migration is control: no per-identification billing, data ownership, and an MIT-licensed stack you can inspect and modify, as laid out in open source device fingerprinting. The risk to manage is the transition window, where a botched mapping or premature cutover loses history.
Keep the parallel run long enough for returning users to cycle back, gate the mapping on observation counts, and never enforce with the new system until shadow mode has proven its calibration. Done that way, migration is a continuity exercise, not a reset.
Frequently asked questions
Will my existing visitor IDs carry over when I migrate?
No, the IDs are computed by different engines, so they will not match one to one. The migration path is to run both systems in parallel, build a mapping between old and new IDs, and let history accumulate under the new IDs before cutting over.
How long should a parallel run last?
Long enough to see your returning users cycle back, which for most sites means a few weeks to a couple of months. The goal is to accumulate enough matched observations that the new device history is as useful as the old one before you switch decisions over.
Migrating from FingerprintJS is a continuity problem, not a coding problem. Run both agents in parallel, learn the ID mapping from real co-occurrence, shadow your decisions until the new stack matches your standards, then ramp the cutover while keeping a rollback path. Treat device history as the asset it is, and you land on a self-hosted stack with your detection intact. The compare/fingerprintjs page and the docs cover the integration specifics.
Run it yourself
Prynt is open-source, self-hostable device intelligence — visitor IDs, bot & fraud Smart Signals, and behavioral biometrics you own end to end.