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'

// consent: 'granted' only once you have consent (or another lawful basis)
const fingerly = await load({ apiKey: 'fly_pk_production_…', consent: 'granted' })
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/browser.global.js, exposes the same members on window.Fingerly. Serve it from your own site under a neutral name; the bundled import is preferred.

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 names the environment it submits to.
  • endpointsstring | string[]
    Ordered URLs or root-relative paths to send requests to instead of the Fingerly API, such as the path a proxy was issued, /k7x2qm4a, or a hosted subdomain's URL. Both calls, the identification and the deferred report, are posted to each exactly as given, with nothing appended. A bare origin, such as http://localhost:8099, is read as an API and gets /api/v1/ingest. They are tried in order when one fails.
  • endpointstring
    Deprecated. Means the same as endpoints. Passing both throws.
  • fallbackToDefaultEndpointbooleanDefault false
    Try the Fingerly API after the custom endpoints fail.
  • consent'granted' | 'pending' | 'denied'Default 'pending'
    Whether the visitor has consented. Until it is granted, identify() and collect() collect and send nothing and reject with a ConsentError. Pass granted once you have consent or another lawful basis, or call setConsent() later. 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

Both calls, the identification and the deferred report after it, are one POST to https://api.fingerly.io/api/v1/ingest unless endpoints names somewhere else, in which case they go to that URL or path exactly.

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, which includes the default state, pending, or is withdrawn while they run. The API's own 422 consent_required, for a submission without "consent": "granted", carries the same code.

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.