Skip to content
Live in production, in shadow mode: FingerTrackID recommends, your system decides.Status →

Your first assessment

Live · shadow modeAPI v1.1Node.js 24.16+

Your backend asks FingerTrackID about one action, here a login, and gets the answer in the same call. In shadow mode nothing changes in your product: you store the answer next to the decision your system already makes.

Before you start, you need:

  • the key_id of a server credential, and its secret already in your secret manager (onboarding);
  • Node.js 24.16 or newer on the 24 line, or 26.1 or newer. Another language works too, through the signing rules.

Pin the exact version. self-check reproduces the public signature vector on the Node.js that runs it, so you know signing works before you touch a credential. It needs none.

The package has no install-time dependency and no install script. Compare the integrity in your lockfile with the registry’s.

Terminal
npm install --save-exact @fingertrackid/server@0.1.2
npx --no fingertrackid-server self-check
Compare with your lockfile
npm view @fingertrackid/server@0.1.2 dist.integrity

Create one client when the backend starts, reading both values by name from the environment. The secret comes from your secret manager: never from a file in the repository, a command argument or an AI assistant.

A missing or malformed value throws FtidConfigurationError with a fixed code and never echoes the value. Alert on it and keep your product running without FingerTrackID.

ftid.ts
import { createFtidServerClient, type FtidServerClient } from '@fingertrackid/server'
export let ftid: FtidServerClient | null = null
try {
ftid = createFtidServerClient({
keyId: process.env.FTID_KEY_ID ?? '',
secretHex: process.env.FTID_HMAC_SECRET_HEX ?? '',
})
} catch (error) {
// Alert on it, and keep the product running without FingerTrackID.
logger.error('ftid.configuration_error', { code: (error as { code?: string }).code })
}

After your own checks, call assess with the tag you chose for this moment, a keyed pseudonym of the person and the token the Web SDK produced for this action.

Until the Web SDK is in your frontend, send null for the token: the call works, and the assessment comes back without a score.

assess never throws after startup. Every outcome is a result, and none of them should stop your login:

result.kind What your backend does
ASSESSED Store it; your own policy decides
INVALID_INPUT Fix the call; nothing was sent
PROBLEM Follow result.problem.category
UNAVAILABLE Keep your decision; the request may have been stored
UNEXPECTED_RESPONSE Keep your decision; report the correlationId
login.ts
// After your own checks. `ftid` is null after a configuration error: the login goes on.
const result = await ftid?.assess({
tag: 'login',
subjectRef: pseudonymOf(user), // a keyed pseudonym, never an email or tax ID
eventContextToken: body.ftidToken ?? null,
eventContextExpiresAt: body.ftidTokenExpiresAt ?? null,
})
if (result?.kind === 'ASSESSED') {
await storeBesideYourDecision(result.assessment)
} else if (result !== undefined) {
// Log identifiers and verdicts only: never the secret, the token or the body.
logger.warn('ftid.assess', { kind: result.kind, severity: result.severity })
}

Three fields carry the answer:

  • assessment_status is COMPLETED only when fresh browser evidence was present. Without a token, under the current managed production profile, it is INCONCLUSIVE.
  • trust_score goes from 0 to 100, higher is better, and exists only in COMPLETED. null means no score: never 0.
  • recommended_action is a recommendation. It never blocks or allows anything by itself.

Check that external_event_id and tag are the ones you sent, and store the response as received. Every field is on the response page.

201 · INCONCLUSIVE
{
"schema_version": "event-assessment.v1.1",
"assessment_id": "01996f2a-0000-7000-8000-000000000401",
"assessment_version": 1,
"supersedes_assessment_id": null,
"business_event_id": "01996f2a-0000-7000-8000-000000000402",
"external_event_id": "login_20260904_000002",
"tag": "login",
"assessment_status": "INCONCLUSIVE",
"retryable": false,
"trust_score": null,
"device_intelligence_snapshot_id": null,
"device_intelligence_snapshot_version": null,
"device_id": null,
"device_match_confidence": null,
"evidence_coverage": 0,
"data_freshness": {
"evidence_cutoff_at": "2026-09-04T12:00:00.105Z",
"oldest_required_observed_at": null,
"maximum_required_age_ms": 300000,
"all_required_fresh": false
},
"signals": [
{
"signal_code": "ANTI_FINGERPRINTING",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BOT_AUTOMATION",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BROWSER_PRIVACY_BRAVE",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BROWSER_PRIVACY_SAFARI",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BROWSER_PRIVACY_SAMSUNG_INTERNET",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BROWSER_SPOOFING",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "DEVICE_MULTI_USER",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "EMULATED_ENVIRONMENT",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "NETWORK_HOSTING",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "NETWORK_PROXY",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "NETWORK_TOR",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "NETWORK_VPN",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "PRIVATE_BROWSING",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
}
],
"limitation_codes": [
"COLLECTOR_NOT_LOADED",
"REQUIRED_EVIDENCE_MISSING"
],
"reason_codes": [
"REQUIRED_EVIDENCE_MISSING"
],
"recommended_action": "MONITOR",
"recommendation_basis": "INSUFFICIENT_EVIDENCE",
"policy_version": "production-detectors-01996f29-4d2c-7b18-9e6a-3f0c8d1b5a72-login",
"fallback_policy_version": "production-observation-fallback-v2",
"ruleset_version": "d95-detectors.v1",
"signal_catalog_version": "web-detectors-v3",
"feature_schema_version": "configured-assessment-features.v1",
"resolver_version": "first-party-installation-v1",
"sdk_version": null,
"provider_versions": [],
"evidence_cutoff_at": "2026-09-04T12:00:00.105Z",
"evaluated_at": "2026-09-04T12:00:00.143Z",
"expires_at": "2026-09-04T12:01:00.143Z",
"correlation_id": "01996f2a-1111-7000-8000-000000000405"
}

check sends one assessment in shadow mode, tagged integration_check and without a browser token, and prints the result. The secret comes only from FTID_HMAC_SECRET_HEX or standard input, never from an argument.

--no makes npx run only the copy your project installed, never a package of the same name from the registry.

Terminal
your-secret-manager read ftid-hmac-secret | FTID_KEY_ID=<key_id> npx --no fingertrackid-server check