Pular para o conteúdo
No ar em produção, em modo shadow: o FingerTrackID recomenda, o seu sistema decide.Status →

Sua primeira avaliação

No ar · modo shadowAPI v1.1Node.js 24.16+

Seu backend pergunta ao FingerTrackID sobre uma ação, aqui um login, e recebe a resposta na mesma chamada. Em modo shadow nada muda no seu produto: você guarda a resposta ao lado da decisão que o seu sistema já toma.

Antes de começar, você precisa de:

  • o key_id de uma credencial de servidor, e o segredo dela já no seu gerenciador de segredos (cadastro da empresa);
  • Node.js 24.16 ou mais recente na série 24, ou 26.1 ou mais recente. Outra linguagem também funciona, pelas regras de assinatura.

Fixe a versão exata. self-check reproduz o vetor público de assinatura no Node.js que o executa, para você saber que a assinatura funciona antes de tocar em uma credencial. Ele não precisa de nenhuma.

O pacote não tem dependência em tempo de instalação nem script de instalação. Compare o integrity do seu lockfile com o do registro.

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

Crie um cliente quando o backend iniciar, lendo os dois valores do ambiente pelo nome. O segredo vem do seu gerenciador de segredos: nunca de um arquivo no repositório, de um argumento de comando ou de um assistente de IA.

Um valor ausente ou malformado lança FtidConfigurationError com um code fixo e nunca repete o valor. Gere um alerta para isso e mantenha o seu produto funcionando sem o 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 })
}

Depois das suas próprias verificações, chame assess com a etiqueta que você escolheu para este momento, um pseudônimo com chave da pessoa e o token que o Web SDK gerou para esta ação.

Enquanto o Web SDK não estiver no seu frontend, envie null no token: a chamada funciona, e a avaliação volta sem nota.

assess nunca lança exceção depois da inicialização. Cada caso volta como um resultado, e nenhum deles deve interromper o seu login:

result.kind O que o seu backend faz
ASSESSED Guarde-o; a sua própria política decide
INVALID_INPUT Corrija a chamada; nada foi enviado
PROBLEM Siga result.problem.category
UNAVAILABLE Mantenha a sua decisão; a requisição pode ter sido armazenada
UNEXPECTED_RESPONSE Mantenha a sua decisão; informe o 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 })
}

Três campos trazem a resposta:

  • assessment_status é COMPLETED somente quando havia evidência atual do navegador. Sem token, no perfil gerenciado de produção atual, ele é INCONCLUSIVE.
  • trust_score vai de 0 a 100, maior é melhor, e só existe em COMPLETED. null significa sem nota: nunca 0.
  • recommended_action é uma recomendação. Ela nunca bloqueia nem permite nada por si só.

Confira se external_event_id e tag são os que você enviou, e guarde a resposta como foi recebida. Todos os campos estão na página resposta.

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 envia uma avaliação em modo shadow, com a etiqueta integration_check e sem token do navegador, e imprime o resultado. O segredo vem só de FTID_HMAC_SECRET_HEX ou da entrada padrão, nunca de um argumento.

--no faz o npx executar só a cópia que o seu projeto instalou, nunca um pacote de mesmo nome vindo do registro.

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