Troubleshooting
Start from the symptom you see. Each section names the likely causes, how to tell them apart and the fix, and links to the page that holds the full rule. FingerTrackID never blocks your product: while you fix any of these, your login and every other business action keep their normal path (integration guidance).
Check the integration health first
Section titled “Check the integration health first”In the console, open Integração e saúde (Integration and health) for the application (the screen). It covers the last 24 recorded hours; its numbers are not live.
- Última coleta (last collection) shows a recent time instead of nenhuma coleta na janela (no collection in the window). No collection at all points to the browser side: see the Web SDK collects nothing.
- In Da coleta à avaliação (from collection to assessment), Coletas aceitas (accepted collections) grows with your logins and Correlacionadas (assessments that found their browser collection) stays close to Avaliações (assessments). Collections without correlated assessments point to the hand-off from the browser to your backend: see assessments arrive without browser evidence.
- The health Sem dados do SDK (no SDK data) means assessments arrived in the window and none carried the SDK’s context, so none received a score.
- Credenciais de servidor (server credentials) shows each credential’s
key_idand state, Origens das páginas (page origins) the website origins the SDK may collect from, and Limite de requisições (request limit) the rate in force.
For a single access, open it in Acessos (accesses): with browser evidence, Cobertura de evidência (evidence coverage) is above zero, the Automação (robô) indicator shows Detectado or Não detectado instead of Não avaliado, and the limitations do not include Coletor não carregado (collector not loaded). The full list of checks is in check that SDK evidence arrives.
The Web SDK collects nothing
Section titled “The Web SDK collects nothing”Every SDK call that fails returns UNAVAILABLE with a reason (or, with the helper, a
failure with its stage and reason); initializeSdk() and createEvent() throw instead.
Log them: the reason tells most causes apart
(failure reasons).
| What you see | Likely cause | Fix |
|---|---|---|
| No request reaches your collection domain, and the setup in Início is not done | The collection domain is not active yet. Without an active collection domain the SDK is not enabled for your application, and publishing DNS alone does not activate it | Follow the step Confirmar o domínio de coleta until it shows Concluído, and Configurar a coleta web until Concluída (collection domain) |
| The step waits on Depende de você, or a record shows Ainda não encontrado | A DNS record is missing, has another name, answers with more than one value, or sits behind a proxy; or your CAA policy does not allow the certificate | Publish the three records exactly as shown, DNS only, with nothing in front; allow pki.goog and letsencrypt.org in CAA; then choose Verificar agora (the records, how to check) |
| An automatic step shows Algo travou | A platform step did not move for 30 minutes | Contact support with the request reference shown in Início |
TRANSPORT_ERROR on a page whose origin is not listed in Origens das páginas |
The page origin is not one of the application’s allowed origins: the collection domain answers 403 origin-not-allowed without CORS approval, so the browser hides that answer |
Add the exact HTTPS origin in Integração e saúde › Origens das páginas (up to 10, all under the collection domain; the change takes effect at once) |
TRANSPORT_ERROR, and the browser console reports a CSP violation |
Your connect-src does not list the exact collection origin |
Add it to connect-src; the SDK needs no script-src entry (your site’s CSP) |
TRANSPORT_ERROR, and the OPTIONS request to the collection domain answers 400 |
Page instrumentation adds tracing headers to the SDK’s requests | Keep the collection origin out of trace propagation (tracing headers) |
TRANSPORT_ERROR only for some visitors |
A blocking browser extension, or the network | Nothing to change in your setup: continue with a null context, and the assessment of that access is inconclusive |
AUTHENTICATION_FAILED on an allowed origin |
The publicKey is not this application’s (pub_app_…), or the SDK version is not the one your application’s collection is set up for |
Use the public key issued in the dash; install the version the dash offers, and contact support before changing it (upgrading the SDK) |
INVALID_RESPONSE |
A proxy, CDN or WAF in front of the collection domain altered the response | Keep the collection CNAME DNS-only |
TRANSPORT_ERROR or INVALID_RESPONSE right at the session open, after a build change |
A minifier option that breaks the SDK, such as compress.unsafe_math (measured on SDK 0.2.0) |
Exclude the SDK package from that minification or turn the option off (installation) |
RATE_LIMITED |
For example, too many new sessions, or more than 1000 live sessions for the application | Prepare only on the first intent of an assessed form, never at page load, and call close() when done (session limits) |
initializeSdk() throws INVALID_CONFIGURATION |
Its configuration is not exactly publicKey and collectionOrigin, an HTTPS origin with no path |
Fix the two keys (initializeSdk) |
initializeSdk() throws UNSUPPORTED_RUNTIME |
The page is not HTTPS, or the browser lacks Fetch, cryptographic randomness or a monotonic clock | Continue with a null context |
The SDK is delivered only as the private package bundled into your frontend. FingerTrackID serves no SDK script, so a page that loads it from a URL or a public CDN collects nothing.
Assessments arrive without browser evidence
Section titled “Assessments arrive without browser evidence”The response is INCONCLUSIVE with trust_score: null, recommendation_basis
INSUFFICIENT_EVIDENCE and the fallback MONITOR; all 13 indicators are NOT_EVALUATED with
COLLECTOR_NOT_LOADED, and device_intelligence_snapshot_id and sdk_version are null. Under
the current managed production profile, an assessment stored without an event_context_token is
always like this (when the browser has no event context).
- Your backend sends
null. The browser sends the token and itsexpiresAtwith the business request it already makes; your backend copies the token intoevent_context_token. Check that the field is forwarded, and that most logins in your backend logs carry a token. A login without one must have a logged reason, such asWAIT_EXCEEDEDon a slow network, not one fixed code on every login. - The browser had no token. The SDK failed (see above), or
the context was taken too late: a submit with nothing prepared needs at least three round trips
before a token exists. Call
prefetch()on the first focus or pointer-down of the form (the lifecycle). - Logins through a redirect (social or OAuth providers) lose the context unless it is taken before the redirect or collected after the callback (login redirects).
- The client was closed too early. Calling
close()before your backend has answered terminates the session that issued the token, and a token whose assessment is not stored yet stops working. - The API answers
400 event-context-invalid. The token expired, was already used, or is from another scope (for example, collected for another application). Nothing is stored. Do not retry the same body: assess the event once more as a new operation withevent_context_token: null(event-context-invalid).
A token is single-use, one per business event, and the assessment that carries it must be stored
before its expiresAt. The evidence window is 60 or 300 seconds from when the collection reaches
FingerTrackID, depending on your application; every response shows it in
data_freshness.maximum_required_age_ms (the evidence window).
If event-context-invalid is frequent, collect closer to the action.
401 on POST /v1/assessments
Section titled “401 on POST /v1/assessments”The type tells three cases apart. timestamp-out-of-window and request-replayed only answer
a request whose signature was verified for a known credential: if you see them, your signature is
right.
authentication-failed
Section titled “authentication-failed”The uniform failure, not retryable: the response never says which part was wrong. Treat it as a configuration error and check, in order:
- The secret. The key is the decoded bytes of the secret,
decodeSecret(text, 'hex'), never the hexadecimal text. Use the secret delivered with thatkey_id: after Trocar segredo the new credential has a newkey_id, and the previous one is revoked at the end of an overlap of at most 24 hours. - The bytes. The body hash covers the exact bytes you send. Build the body with a single
JSON.stringifycall and send those bytes; re-serializing after signing gives401. - The canonical string. Exactly ten lines separated by LF, without a trailing LF, the ninth
empty; the path is literally
/v1/assessments(the canonical string). - The headers.
X-FTID-Signature: v1=<key_id>:<mac>, one signature, with a lowercase 64-character MAC;key_idandrequest_idare lowercase UUIDv7.X-Client-Id,X-Signature,X-TimestampandX-Nonceare refused with401. - The credential’s state. In Integração e saúde › Credenciais de servidor, the
credential must be active, not revoked or suspended. A credential still waiting for secure
installation is not usable. A client that kept retrying
429without waiting is blocked for at least 10 minutes, during which every request gets401.
Run the reference self-check on the Node.js that runs your backend: node ftid-hmac-v1-self-check.mjs
must print OK and the test vector’s MAC. If it does, the signer works, and the cause is the
secret, the key_id or the bytes you send
(reference signer).
timestamp-out-of-window
Section titled “timestamp-out-of-window”Retryable. X-FTID-Timestamp is the second at which you sign the attempt, not the event’s
occurred_at, and it must be within ±300 s of the server clock. Synchronize your backend clock
(NTP), then send a new attempt. An occurred_at out of ±300 s is a different error,
400 occurred-at-out-of-window.
request-replayed
Section titled “request-replayed”Retryable. A request_id was reused. It is the anti-replay nonce: generate a new one for every
attempt, including after 401, 403, 429 or 503, with crypto.randomUUIDv7(), never
crypto.randomUUID(), which makes version 4. Then fix the generator that repeated it.
403 forbidden
Section titled “403 forbidden”You are authenticated, but the credential lacks the route’s capability. A credential’s
capabilities are frozen in its version, so you cannot add one to it: create a server credential
with that permission in the console. The feedback routes need action:write and outcome:write
(forbidden).
429 rate-limited
Section titled “429 rate-limited”Your server client is above its rate: 5 requests per second with a burst of 10 on the standard
tier (limits). Wait for Retry-After (whole seconds, today 1), then send a new
attempt with the same body, a new request_id, the current timestamp and a new signature.
Reusing the old request_id gives 401 request-replayed, and retrying without waiting exhausts
the security budget and blocks the credential for at least 10 minutes.
To raise the rate, open Integração e saúde › Limite de requisições and choose
Pedir aumento de limite, as Administrador da empresa or Gestor de integração; the console
offers up to 10 per second, approved at once. For more, contact support at
contato@fingertrackid.com. Momentary saturation of the service is 503 assessment-unavailable
instead, retried the same way.
Many results are inconclusive
Section titled “Many results are inconclusive”INCONCLUSIVE is a statement about the evidence, not about the person: no limitation code is a
reason against the access.
- Without a context every assessment is
INCONCLUSIVE(above). - With a context, the assessment is
COMPLETEDonly when at least 30% of the 13 indicators were evaluated with fresh evidence (evidence_coverage≥ 30) and both indicators that count toward the score,BOT_AUTOMATIONandBROWSER_SPOOFING, were evaluated. - Your company’s active rules make every indicator you activate required: an active indicator
without usable fresh evidence leaves the assessment without a score (
trust_score: null,MONITOR), even above 30% coverage (risk rules).
Read each indicator’s not_evaluated_reason_code to see what was missing
(limitation codes). For example:
STALE_DEVICE_PROFILE: the browser evidence was older than your application’s evidence window (collect closer to the action), or a server-side evidence source was older than its maximum age, which recollecting does not fix.UNSUPPORTED_RUNTIMEon the browser privacy and private browsing indicators: the SDK ran inside an iframe or an in-app browser, or the browser engine could not be identified consistently.REQUIRED_EVIDENCE_MISSINGonDEVICE_MULTI_USER: expected on the first login of a new browser installation; later logins evaluate it.
Outbound webhooks
Section titled “Outbound webhooks”Outbound assessment webhooks are not enabled (production status):
there is no webhook to configure or debug. Your backend receives each assessment only in the
response to its POST /v1/assessments.
What to send to support
Section titled “What to send to support”Keep and send operation IDs, request_id, correlation_id (the X-Correlation-Id header of
every response), the assessment_id, public error codes, timestamps and versions. Never send
tokens, secrets, cookies, raw headers, personal data or full payloads.