API reference

JavaScript agent

Every export, option, result field and error of the Fingerly browser SDK, @fingerly/web-js.

This is the complete API of @fingerly/web-js. For a guided introduction, start with the JavaScript SDK.

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

const fingerly = await load({ apiKey: 'fly_pk_us_production_…' })
const result = await fingerly.identify({ tag: 'login' })

Exports

ExportKindDescription
loadFunctionCreates a client.
FingerlyClientClassThe client load returns.
TransportErrorClassThe error identify rejects with.
IdentifyOptions, IdentifyResult, IdentifyTrigger, Verdict, Verdicts, ConfidenceTypesDescribed below.

The script-tag build, dist/fingerly.global.js, exposes the same members on window.Fingerly.

load()

Signature
function load(options: FingerlyOptions): Promise<FingerlyClient>

Creates a client. Loading collects and sends nothing. A configuration mistake throws synchronously, so call it with await inside try.

Load options

  • apiKeystringrequired
    Your public key. Its prefix decides which regional API is used.
  • endpointsstring | string[]
    Ordered base URLs or root-relative paths to send requests to instead of the regional API, for a proxy. /api/v1/identify is appended to each. They are tried in order when one fails.
  • endpointstring
    A single base URL or path. Prefer endpoints. Passing both throws.
  • fallbackToDefaultEndpointbooleanDefault false
    Try the key's regional API after the custom endpoints fail.
  • consent'granted' | 'pending' | 'denied'Default 'granted'
    Whether the visitor has consented. Until it is granted, identify() and collect() collect and send nothing and reject with a ConsentError. See consent.
  • transportobject
    Replaces how requests are sent, for tests: an object whose submit(submission, signal) resolves with an identify response, and optionally submitSupplement(requestId, submission, signal). endpoint, endpoints and fallbackToDefaultEndpoint are then ignored. See testing.
  • sourcesarray
    The signal sources to run. Every browser source by default; pass [] in tests to collect nothing.
  • scheduleobject
    Collection tuning for collect() and for identify() with explicit tiers: budgetMs, defaultSourceTimeoutMs and concurrency.
    3 child fields
    • budgetMsnumberDefault 1200
      The total collection budget.
    • defaultSourceTimeoutMsnumberDefault 300
      The time any one source may take.
    • concurrencynumberDefault 6
      Sources collected at once.

Where requests go

Key prefixAPI
fly_pk_us_…https://us.api.fingerly.io
fly_pk_eu_…https://eu.api.fingerly.io (coming soon)

load throws when apiKey is empty, when both endpoint and endpoints are set, when endpoints is empty, or when an endpoint is neither an absolute http(s) URL nor a path starting with /, or contains a query or fragment.

identify()

Signature
fingerly.identify(options?: IdentifyOptions): Promise<IdentifyResult>

Collects signals, submits them and resolves with the server's verdict. Each call is a new identification with its own idempotency key; the SDK does not cache results.

IdentifyOptions

  • tagstring
    Your own reference for this identification. Echoed on the event, in webhooks and in the dashboard.
  • submitbooleanDefault true
    false collects and computes local verdicts without sending anything.
  • signalAbortSignal
    Cancels collection and submission.
  • tiersArray<'fast' | 'deferred'>
    Collect these tiers in one pass under schedule.budgetMs, with no separate deferred report. Omit for the default: a 300 ms initial tier, then a 1,200 ms deferred tier after the answer.

IdentifyResult

  • requestIdstring
    The identification's ID. Empty when not submitted.
  • visitorIdstring
    The stable visitor identifier. Empty only when not submitted or on a duplicate.
  • visitorIsNewboolean
    Whether your organization is seeing this visitor for the first time.
  • visitorConfidencenumber
    0 to 100: 100 for an exact match, 85 to 99 when recognised after the device changed, 0 for a new visitor ID.
  • identifiableboolean
    false when the browser gave too little to identify anyone. Still scored.
  • duplicateboolean
    true when the server had already answered this request. Keep the first response.
  • statestring
    enriched, or unavailable when the network lookup could not run. Empty when not submitted.
  • suspectScorenumber | null
    The weighted sum of the signals that fired. null when not scored, which is not 0. Not a percentage and not capped.
  • suspectLevelstring | null
    low, medium or high. null exactly when suspectScore is.
  • triggersIdentifyTrigger[]
    The signals that fired, heaviest first.
    4 child fields
    • signalstring
      The signal.
    • groupstring
      Its group.
    • weightnumber
      The weight it added.
    • confidencestring
      low, medium or high.
  • verdictsVerdicts
    Local verdicts from the initial tier. Advisory.
  • reportSignalReport
    The report that was sent.
  • deferredPromise<DeferredIdentifyResult>
    The deferred tier's outcome. Never rejects.

Verdicts

KeyMatches when
incognitoThe page is in a private browsing window.
shieldsAnti-fingerprinting protections are rewriting values. A privacy choice, not a reason to challenge anyone.
torThe browser is Tor Browser.
emulatorThe browser runs in an emulated device.
automationThe browser is driven by automation.
farmThe browser looks mass-provisioned or reset fresh.

Verdict

  • valueboolean
    Whether the verdict matched.
  • confidence'low' | 'medium' | 'high'
    How strong the evidence is.
  • reasonsstring[]
    Short, stable tokens for the evidence, strongest first.

DeferredIdentifyResult

`status`FieldsWhen
'submitted'report, combinedReport, verdictsThe deferred report was sent. verdicts covers both tiers.
'skipped'reason: 'not-submitted', 'explicit-tiers', 'duplicate'No deferred report applied.
'failed'error, and whatever was collectedCollection or sending failed. The identification itself is unaffected.

collect()

Signature
fingerly.collect(options?: IdentifyOptions): Promise<SignalReport>

Runs collection and returns the report without submitting anything. Uses tiers (both by default), signal and schedule.budgetMs.

setConsent()

Signature
fingerly.setConsent(state: 'granted' | 'pending' | 'denied'): void
fingerly.consent: 'granted' | 'pending' | 'denied'
fingerly.onConsentChange(listener: (state) => void): () => void

Changes the consent state. It applies to the next call and to any call already running: withdrawing consent stops collection and sending, and a running deferred tier resolves as skipped with the reason consent-withdrawn. onConsentChange calls its listener on every change and returns a function that unsubscribes. Any other value throws a TypeError.

ConsentError

Definition
declare class ConsentError extends Error {
  readonly name: 'ConsentError'
  readonly code: 'consent_required'
  readonly state: 'pending' | 'denied'
}

identify() and collect() reject with it when consent is not granted, or is withdrawn while they run.

TransportError

Definition
declare class TransportError extends Error {
  readonly name: 'TransportError'
  readonly status?: number      // the HTTP status, when the server answered
  readonly retryable: boolean   // whether trying again later may succeed
}
Situation`status``retryable`
The server refused the requestThe HTTP statustrue for 404, 405, 408, 425, 429 and 5xx
The network failed or an attempt timed outnonetrue
The request was abortednonefalse
A 2xx without a request ID, such as a captive portal pagenonefalse

identify rejects only after its own retries: up to three attempts, 5 seconds each, with jittered backoff and one idempotency key.

Runtime

  • ES2020, using fetch and AbortController. No workers, WebAssembly or eval are required.
  • Requests omit credentials and set no cookies. The SDK stores no identifier on the device.
  • About 18 KB gzipped for the script-tag build.