Coleta e contexto do evento
Esta página descreve o SDK 0.3.0, o pacote que o console oferece
(notas de versão). A API dele acrescenta
createActionContextHelper, que executa por você todo o ciclo de vida abaixo; todo o resto mantém
a API da 0.2.x. Se o seu frontend ainda roda uma versão anterior, leia também
versões anteriores.
O ciclo de vida
Seção intitulada “O ciclo de vida”O SDK mantém o estado dele na memória da página e transforma uma coleta aceita em um token opaco e de vida curta por ação de negócio. Seu backend envia esse token com a avaliação dele. Para um login:
- Inicialize uma vez por página, em código do navegador, e mantenha o cliente em um módulo que
sobreviva aos seus componentes de login.
initializeSdk()não faz nenhuma requisição. - Prepare na primeira intenção. No primeiro foco ou pointer-down no formulário de login, chame
prefetch(): ele coleta e inicia o contexto do evento, de modo que o token normalmente já está pronto no envio. Não no carregamento da página: cada carregamento abriria uma sessão e uma coleta que conta como uso (a unidade de uso), também para visitantes que nunca fazem login. - Mantenha-o atual. A evidência do navegador só pode ser usada dentro da
janela de evidência da sua aplicação, 60 ou 300 segundos a partir de quando
a coleta chega ao FingerTrackID. O helper descobre a janela pelo próprio
expiresAtdos tokens e só coleta de novo quando precisa: a evidência expiraria antes da sua margem, a sessão da coleta terminou, duas consultas seguidas foram recusadas ou expiraram em uma coleta que já deu um token, ou uma limitação transitória voltou (uma vez por página). Uma coleta que nunca deu um token só é substituída quando a sessão dela terminou (uma única vez seguida), para aquela única nova tentativa, ou 300 segundos depois de ter sido enviada, de modo que uma indisponibilidade só dos contextos de evento custa no máximo uma coleta extra a cada 300 segundos (etapas e motivos). O relógio dele conta o tempo que o dispositivo passou em suspensão. - No envio, obtenha o contexto desta ação com
take(), que espera no máximowaitMse nunca aborta o SDK. Envie o token e oexpiresAtdele com a requisição de negócio, ounulljunto com o motivo, para os seus logs. - Feche só quando tiver terminado. Chame
close()depois que o seu backend tiver respondido à requisição que levou o token, no logout ou na troca de conta. Fechar antes encerra a sessão que emitiu o token, e um token cuja avaliação ainda não foi armazenada deixa de funcionar.
Em produção, em 2026-10-15 (uma aplicação, uma amostra pequena, não um SLA), getEventContext em
uma coleta aceita levou 0,15 s na mediana, enquanto uma coleta iniciada no clique levou
2,6 s (medido em produção): projete para que o token esteja
pronto antes do envio, em vez de buscá-lo no envio.
A receita
Seção intitulada “A receita”Um módulo por página. Ele usa só a API pública do SDK, nunca lança exceção e nunca aborta o SDK.
prepareNextAction()roda no primeirofocusinoupointerdownno formulário, e em cada um dos seguintes; para um botão de login, nopointerenter, nofocuse nopointerdowndele. Nunca o chame no carregamento da página: cada carregamento abre uma coleta que conta como uso (a unidade de uso). Ele nunca espera e nunca repete por conta própria uma coleta que falhou.takeActionContext()roda uma vez por ação de negócio, no envio dela, e resolve dentro deSUBMIT_WAIT_MScom{ context, failure }. Use o objeto que ele devolve para essa ação e para toda nova tentativa dela, inclusivenull: não obtenha outro contexto para mudar uma requisição já enviada. Um token nunca é entregue a duas ações.- Um clique que é a primeira intenção, ou que vem depois de uma coleta cuja evidência expirou, espera
até
SUBMIT_WAIT_MSenquanto o helper coleta; mostre o progresso durante a espera. Em dispositivos de toque, opointerdownvem logo antes do clique, então o primeiro toque muitas vezes espera. - A atualidade vem do
expiresAtdo token, e a idade de uma coleta conta o tempo que o dispositivo passou em suspensão. O helper nunca entrega um token com menos deminRemainingMsrestantes (15000 ms por padrão; opção decreateActionContextHelper). Mantenha-o acima da margem de que o seu backend precisa para armazenar a avaliação (orientações de integração). - Com o helper, não chame
prepare(),collect()nemgetEventContext()você mesmo, e não chame oclose()do cliente diretamente: usecloseSdk(). Umcollect()seu é adotado, mas custa uma coleta própria. - Um contexto
nullnunca é silencioso:failureinforma a etapa e o código (etapas e motivos). Conte esses casos do seu lado; uma integração em que todo login chega ao backend comnullparece, do lado do FingerTrackID, exatamente igual a uma em que o SDK nunca rodou. O código serve só para os seus logs: a requisição de avaliação não tem campo para ele. - Envie
expiresAtcom o token para que o seu backend possa descartar um token que expirou antes de poder ser usado. - Escolha
SUBMIT_WAIT_MSa partir das suas próprias medições (0 a 10000 ms). Com o prefetch, o contexto tem, para se resolver, o tempo que o usuário passa no formulário. Uma coleta iniciada no clique levou 2,6 s na mediana em produção (medido em produção), acima dos 2500 ms desta receita: preparar na primeira intenção, para que uma coleta aceita e atual esteja esperando no clique, importa mais do que aumentar o teto.
import { createActionContextHelper, initializeSdk } from '@ftid/sdk-web'
const configuration = { publicKey: 'REPLACE_WITH_ISSUED_PUBLIC_APPLICATION_KEY', collectionOrigin: 'https://collect.customer.example',}// Your choice, not an SDK value: the longest a submit waits for its context.const SUBMIT_WAIT_MS = 2_500
let helper = nulllet unavailable = null // Why there is no helper (initializeSdk threw), never a token.
function actionContexts() { if (helper || unavailable) return helper try { helper = createActionContextHelper(initializeSdk(configuration), { waitMs: SUBMIT_WAIT_MS }) } catch (error) { // initializeSdk and createActionContextHelper throw only fixed codes. const reason = /^[A-Z_]{1,64}$/.test(error?.message) ? error.message : 'UNEXPECTED' const failure = Object.freeze({ stage: 'COLLECT', reason }) unavailable = Object.freeze({ context: null, failure }) } return helper}
// First focus or pointer-down on the form, and every later one: cheap, never waits.// Not at page load.function prepareNextAction() { actionContexts()?.prefetch()}
// At submit: this action's { context, failure } within SUBMIT_WAIT_MS.async function takeActionContext() { return (await actionContexts()?.take()) ?? unavailable}
// After the backend answered and this page is done with the client, on logout or account switch.function closeSdk() { const closing = helper?.close() helper = null unavailable = null return closing}Login com senha
Seção intitulada “Login com senha”A receita acima serve tanto para um formulário clássico quanto para uma aplicação de página única.
Nos dois casos, o token vai com a requisição que o seu backend avalia: o formulário de senha, ou o
formulário do código de MFA quando o seu login pede um (chame prepareNextAction() quando esse
formulário receber foco ou um pointer-down e takeActionContext() no envio dele, como no
formulário de senha). Nunca envie valores do formulário ao SDK.
POST de formulário clássico
Seção intitulada “POST de formulário clássico”Um formulário enviado descarrega a página, e o descarregamento cancela as requisições do SDK. Segure o envio até que o contexto esteja resolvido ou o seu teto acabe, escreva-o em campos ocultos e depois envie.
- Mantenha
method="post": um formulárioGETcolocaria o token na URL. submit()não envia o nome e o valor do botão clicado; se o seu backend os lê, coloque-os em um campo oculto. Chamá-lo porHTMLFormElement.prototypefaz com que ele continue funcionando mesmo se um campo se chamarsubmit.- Não chame
close()aqui: a página que responde não tem cliente do SDK. A sessão termina sozinha depois de 30 minutos e ocupa uma vaga de sessão até lá.
<form id="login" method="post" action="/login"> <!-- Your existing fields. --> <input type="hidden" name="ftid_event_context_token" /> <input type="hidden" name="ftid_event_context_expires_at" /> <input type="hidden" name="ftid_context_failure" /> <button type="submit">Sign in</button></form>// prepareNextAction and takeActionContext come from the recipe above.const loginForm = document.getElementById('login')loginForm.addEventListener('focusin', () => prepareNextAction())loginForm.addEventListener('pointerdown', () => prepareNextAction())if (loginForm.contains(document.activeElement)) prepareNextAction() // An autofocused field.
let submitting = falseloginForm.addEventListener('submit', async (event) => { event.preventDefault() // Hold the navigation until the context is settled or the ceiling ends. if (submitting) return submitting = true const { context, failure } = await takeActionContext() const fields = loginForm.elements fields.ftid_event_context_token.value = context?.token ?? '' fields.ftid_event_context_expires_at.value = context?.expiresAt ?? '' fields.ftid_context_failure.value = failure ? `${failure.stage}:${failure.reason}` : '' // Submits without firing this listener again; the page navigates only now. HTMLFormElement.prototype.submit.call(loginForm)})// A page restored from the back/forward cache must be able to submit again.window.addEventListener('pageshow', (event) => { if (event.persisted) submitting = false})Aplicação de página única (fetch)
Seção intitulada “Aplicação de página única (fetch)”- Chame
prepareNextAction()a partir dos handlers de foco e de pointer-down do formulário. No React,onFocuseonPointerDownno elemento<form>recebem os de todos os campos. - Mantenha o SDK fora do estado do componente. Um componente que é desmontado no envio (uma troca de rota, um spinner que substitui o formulário) não pode levar consigo o cliente nem o trabalho pendente dele.
- Não chame
closeSdk()no cleanup de um efeito. OStrictModedo React executa os cleanups uma vez a mais em desenvolvimento, e um cleanup que fecha o cliente encerra a sessão de um token ainda não enviado. - Não passe às chamadas do SDK um
AbortSignalligado ao componente ou à rota, e não aborte para parar de esperar:takeActionContext()para de esperar sozinho e deixa o SDK rodando. - Navegue depois que o
fetchresolver, nunca antes.
Logins que saem da página (login com Google, outros provedores OAuth) precisam de mais um passo: login com Google, OAuth e outros redirecionamentos.
// takeActionContext and closeSdk come from the recipe above, kept in a module that outlives the// login component. csrfToken stands for your existing CSRF protection.async function submitLogin(credentials) { const { context, failure } = await takeActionContext() const response = await fetch('/api/login', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ ...credentials, eventContextToken: context?.token ?? null, eventContextExpiresAt: context?.expiresAt ?? null, contextFailure: failure, }), }) // The backend has answered: a successful login is done with the client. A failed attempt keeps // it, and the next attempt prepares its own context. if (response.ok) void closeSdk() return response}A janela de evidência
Seção intitulada “A janela de evidência”A evidência do navegador pode ser usada por um tempo limitado, contado a partir do momento em que o
FingerTrackID recebe a coleta enviada por collect(), não a partir do carregamento da página nem de
initializeSdk(). A duração é fixada por aplicação pela configuração gerenciada de coleta de
produção quando a aplicação é provisionada:
| Aplicação | Janela de evidência |
|---|---|
| Provisionada antes de a janela de 300 segundos ser introduzida (outubro de 2026), inclusive as primeiras aplicações do piloto | 60 segundos |
| Provisionada desde então | 300 segundos |
Uma aplicação existente mantém a janela dela até que o FingerTrackID a altere junto com você; nada a altera em silêncio. Para ver a sua:
- Toda resposta de avaliação a traz em
data_freshness.maximum_required_age_ms(60000ou300000), inclusive avaliações enviadas comevent_context_token: null. - Para um token específico, o limite que vale é sempre o
expiresAtdele, descrito abaixo. - Em caso de dúvida, pergunte ao suporte do FingerTrackID o valor da sua aplicação. Código que
respeita
expiresAtfunciona com qualquer uma das janelas; só a sua margem de coleta precisa caber na mais curta.
Como a janela se aplica:
getEventContextsó pode devolver um token dentro da janela. Depois que a janela da coleta termina, ele devolveUNAVAILABLEcom o motivoSERVER_UNAVAILABLE(ouINCONCLUSIVE, se a coleta só foi avaliada depois da janela dela).- O
expiresAtdevolvido é o mais cedo entre: a duração da janela depois da emissão do token, o fim da janela da coleta, o fim da sessão do SDK e o fim da capacidade vigente do SDK. Portanto, ele nunca é posterior à duração da janela contada a partir de quando a coleta chegou ao FingerTrackID. ChamargetEventContextde novo no mesmo handle depois doexpiresAtdevolveUNAVAILABLEcom o motivoEVENT_CONTEXT_EXPIRED. - A avaliação que leva o token precisa ser armazenada antes do
expiresAt. Uma avaliação nova com um token expirado (ou já usado) é recusada com400 event-context-invalid: não admite nova tentativa, nada é armazenado, e os IDs de operação dessa requisição ficam vinculados a esse token. Em vez disso, avalie o evento como uma nova operação comevent_context_token: null(orientações de integração). - Uma avaliação que já foi armazenada não é afetada: uma nova tentativa com o mesmo corpo devolve o
resultado armazenado (
200) mesmo depois doexpiresAt.
Do navegador à API
Seção intitulada “Do navegador à API”O navegador envia ao seu backend só o token e o expiresAt dele (ou null), na requisição de
negócio que ele já faz. Seu backend coloca o token no campo event_context_token
do POST /v1/assessments, ou null se o expiresAt deixar tempo
insuficiente para armazenar a avaliação (a janela de evidência).
- O token é opaco e de uso único: ele é consumido uma vez por evento de negócio. Não o decodifique, não o grave em logs nem em analytics, não o coloque em uma URL e não o use como seu próprio token de autorização.
- Seu backend nunca aceita do navegador referências de pessoa e de conta nem
occurred_at. - Quando não há token, seu backend ainda assim envia a avaliação com
event_context_token: null(orientações de integração). - Uma requisição já enviada ao FingerTrackID mantém o token original (ou
null) em toda nova tentativa. - Mantenha o cliente do SDK vivo até que o seu backend tenha respondido. Até que a avaliação que
leva o token seja armazenada, encerrar a sessão que o emitiu (
close()) invalida o token. Nunca estenda o prazo original do contexto ou da requisição para esperar a conclusão. - No perfil gerenciado de produção atual, uma avaliação armazenada sem contexto é
INCONCLUSIVEcomtrust_score: null, nuncaCOMPLETED. Com contexto, ela éCOMPLETEDquando pelo menos 30% dos 13 indicadores foram avaliados com evidência atual (evidence_coverage≥ 30, contada sobre os indicadores de navegador, de rede e de histórico do dispositivo em conjunto) e os dois indicadores que contam para a nota,BOT_AUTOMATIONeBROWSER_SPOOFING, foram avaliados; caso contrário, ela éINCONCLUSIVE. Sem nada adverso encontrado, otrust_scoreé100, e a recomendação gerenciada éMONITORem todas as faixas: a nota informa, nunca decide. Assim que a sua empresa ativa as próprias regras de risco, a nota e as faixas passam a segui-las, e todo indicador que você ativa se torna obrigatório: um indicador ativo sem evidência atual utilizável (por exemplo, um indicador de rede enquanto a evidência de rede está indisponível) deixa a avaliação sem nota (trust_score: null,MONITOR), mesmo acima de 30% de cobertura. É o contexto que anexa a evidência do navegador (veja como saber se a evidência do SDK chegou). A chamada ainda pode terminar em um Problem operacional (por exemplo,503). Valide a resposta ou o Problem que você de fato receber sob a política emitida para a sua aplicação.
Limites de sessão
Seção intitulada “Limites de sessão”Um cliente abre uma sessão de navegador para a sua aplicação só quando precisa pela primeira vez
de autoridade do servidor: em prepare() ou no primeiro collect(). initializeSdk() sozinho não
abre sessão nem consome cota. Os limites são por aplicação, iguais para todas as aplicações, e não
precisam de configuração:
| Limite | Valor |
|---|---|
| Novas sessões | 5 por segundo, com rajada de 20 |
| Sessões ativas simultâneas | Até 1000 por aplicação |
| Duração da sessão | 30 minutos a partir da operação que a abre, nunca estendida |
| Requisições por sessão | Uma requisição de coleta ou de contexto do evento a cada 250 ms, que o SDK respeita sozinho |
| Contextos de evento por sessão | 128 |
Uma sessão deixa de contar para o limite de simultâneas quando termina: quando é encerrada
(close(), ou quando o SDK a aposenta, como descrito abaixo) ou quando os 30 minutos dela se esgotam. Uma sessão que
nunca é fechada ocupa a vaga dela pelos 30 minutos inteiros, mesmo depois que o visitante saiu.
Quando um limite é atingido, uma nova sessão é recusada e collect() devolve
status: 'UNAVAILABLE' com o motivo RATE_LIMITED, tanto acima da taxa de novas sessões quanto
no limite de simultâneas; no limite de simultâneas, o Retry-After que o SDK respeita é o tempo até
a primeira das sessões ocupadas expirar, no máximo 30 minutos.
Continue a ação de negócio com um contexto null, como em qualquer outra falha de coleta, e nunca
faça nova tentativa em loop. Veja também os limites da API.
Como o SDK usa as sessões (desde a 0.2.0):
- Cada nova coleta depois de uma aceita abre uma nova sessão, no máximo uma por segundo por
cliente (um
collect()antes disso devolveRATE_LIMITED, e a coleta anterior continua utilizável). A sessão substituída é encerrada pelo cliente assim que o último token que ela emitiu expira (no máximo 300 segundos), imediatamente se ela não emitiu nenhum, ou noclose(). Um cliente mantém no máximo três sessões substituídas esperando; acima disso, ele encerra a mais antiga. - Depois de 30 minutos, o cliente abre sozinho a sessão seguinte no próximo
collect(),prepare()ourenew(); nada é reaberto em segundo plano. Handles da sessão antiga devolvemCAPABILITY_EXPIRED(createEventegetEventContextsobre eles não abrem sessão), e o helper coleta de novo. Desde a 0.3.0, esses 30 minutos contam o tempo que o dispositivo passou em suspensão.
Para ficar dentro das 1000 sessões simultâneas:
- Prepare só em páginas que têm uma ação avaliada (login, saque, negociação), nunca em toda visualização de página, e só na primeira intenção (foco ou pointer-down) no formulário dela: assim, as sessões só são abertas para visitantes que iniciam a ação.
- Chame
close()assim que o seu backend tiver respondido e a página tiver terminado de usar o cliente, e no logout ou na troca de conta.
Tempo e latência
Seção intitulada “Tempo e latência”O FingerTrackID não publica SLA de latência. Os primeiros números medidos em produção estão
abaixo; eles vêm de uma aplicação e de uma amostra pequena.
Planeje com os limites a seguir, meça na sua própria página (de collect() até um contexto
COMPLETED) e escolha SUBMIT_WAIT_MS com base no que observar. Esses limites são limites do
protocolo, não latência esperada nem um SLA.
Cada requisição tem um prazo no cliente maior que o orçamento do servidor para aquela rota, porque uma requisição do navegador também gasta tempo com DNS, TLS, o preflight de CORS e a volta:
| Passo | SDK 0.2.0 e posteriores | Orçamento do servidor |
|---|---|---|
Abertura de sessão (prepare() ou o primeiro collect()) |
3000 ms por tentativa; uma tentativa com timeout, perdida, com 429 ou com 503 é repetida uma vez na mesma chamada quando a espera por ela cabe em um segundo |
1000 ms |
| Coleta | 4000 ms por tentativa | 2000 ms |
| Contexto do evento | 1500 ms por requisição, dentro de um orçamento de 5000 ms por evento, no máximo 8 requisições de contexto do evento por evento | 500 ms por requisição |
| Renovação de capacidade e encerramento de sessão | 1500 ms por tentativa | 1000 ms |
- Os 5000 ms de um evento começam na primeira chamada
getEventContextdele e nunca são reiniciados. Dentro deles, o SDK pergunta de novo 400 ms depois de cada respostaPROCESSING(ou depois de umRetry-Aftermais longo) e repete sozinho uma consulta com timeout, perdida, com429ou com503. Nenhuma consulta começa com menos de 1000 ms restantes. Quando o orçamento acaba, o evento mantém a última resposta, que pode serPROCESSING. - Uma sessão envia uma requisição de coleta ou de contexto do evento por vez, pelo menos 250 ms depois que a anterior terminou.
- Um envio sem nada preparado precisa de pelo menos três idas e voltas ao seu domínio de coleta antes de existir um token (abertura de sessão, coleta, contexto do evento), mais o processamento da coleta pelo FingerTrackID. É por isso que a receita faz o prefetch na primeira intenção.
- A primeira requisição a cada rota também pode precisar de um preflight de CORS. A 0.2.0 deixa o navegador reutilizá-lo (o servidor permite guardá-lo em cache por 600 segundos); a 0.1.1 enviava um preflight antes de cada requisição.
- Cabeçalhos de requisição adicionados por instrumentação da página fazem toda requisição falhar (cabeçalhos de rastreamento).
Medido em produção (2026-10-15)
Seção intitulada “Medido em produção (2026-10-15)”Medido em 2026-10-15 em produção, em uma aplicação:
| Medição | Amostra | Mediana | p95 | Máximo |
|---|---|---|---|---|
getEventContext em uma coleta aceita, até o token |
10 | 0,15 s | 0,17 s | — |
| Uma coleta iniciada no clique, da abertura de sessão até o fim da avaliação da coleta | 13 | 2,6 s | 3,9 s | 4,4 s |
É uma amostra pequena de uma única aplicação: leia-a como indicativa, não como um SLA, e meça a sua própria página, a sua rede e os dispositivos dos seus usuários. A segunda linha é aproximadamente o que um envio sem nada preparado espera antes que o token dele possa existir; preparar na primeira intenção deixa só a primeira linha no envio.
O que lança exceção e o que devolve um motivo
Seção intitulada “O que lança exceção e o que devolve um motivo”| Chamada | Lança | Caso contrário, devolve |
|---|---|---|
initializeSdk(configuration) |
Um Error cuja message é INVALID_CONFIGURATION, UNSUPPORTED_RUNTIME ou CONFIGURATION_CONFLICT (e CLOCK_INVALID em um navegador cujo relógio monotônico se comporta mal) |
O cliente, de forma síncrona |
createEvent(observation) |
Um Error cuja message é CLOSED, INVALID_HANDLE ou CAPABILITY_EXPIRED (e CLOCK_INVALID ou UNSUPPORTED_RUNTIME em um navegador com defeito) |
O handle do evento, de forma síncrona |
prepare({ signal }?) |
Nunca; a promise nunca é rejeitada | READY, ou UNAVAILABLE com um reason |
collect({ signal }?) |
Nunca | ACCEPTED com a observation dele, ou UNAVAILABLE com um reason e a observation pendente (ou null) |
retryCollection(observation, { signal }?) |
Nunca | O mesmo que collect(); uma observação já aceita responde ACCEPTED sem requisição |
getEventContext(event, { signal }?) |
Nunca | COMPLETED com eventContextToken e expiresAt; PROCESSING quando o orçamento dele acabou antes; INCONCLUSIVE ou FAILED com um limitationCode; ou UNAVAILABLE com um reason |
renew({ signal }?) |
Nunca | RENEWED, NOT_DUE ou UNAVAILABLE com um reason |
close() |
Nunca | TERMINATED, UNCERTAIN ou LOCAL_ONLY |
createActionContextHelper(client, { minRemainingMs, waitMs }?) |
Um Error cuja message é INVALID_INPUT (opções diferentes desses dois inteiros, minRemainingMs 1000–30000 e waitMs 0–10000), INVALID_HANDLE (não é um cliente desta cópia do SDK), CLOSED ou CONFIGURATION_CONFLICT (o cliente já tem um helper com outras opções) |
O helper, de forma síncrona; as mesmas opções devolvem o mesmo helper |
helper.prefetch() |
Nunca | Nada; ele nunca espera |
helper.take({ waitMs }?) |
Nunca; a promise nunca é rejeitada | { context: { token, expiresAt }, failure: null } ou { context: null, failure: { stage, reason } } |
helper.close() |
Nunca | O resultado do close() do cliente |
UNAVAILABLE traz retryAfterMs depois de um 429 ou 503 do servidor e quando o próprio
backoff do SDK recusa uma requisição. O SDK renova sozinho a capacidade de vida curta dele dentro
dessas chamadas; renew() só faz isso antecipadamente e nunca estende a sessão de 30 minutos.
close() devolve TERMINATED quando toda sessão que ele mantinha confirmou o encerramento;
UNCERTAIN quando alguma não confirmou, quando uma abertura de sessão ainda estava em andamento, ou
quando ele não mantinha nenhuma sessão depois de tentar abrir uma; e LOCAL_ONLY quando ele nunca
tentou abrir uma.
Motivos de falha
Seção intitulada “Motivos de falha”Todo resultado UNAVAILABLE traz um reason (tipo SdkFailureReason), e createEvent lança
um Error cuja message é um desses mesmos códigos. Em todos os casos, continue a ação de negócio
com um contexto null.
reason |
Causa provável | O que fazer |
|---|---|---|
TRANSPORT_ERROR |
A requisição não foi concluída, ou o navegador escondeu a resposta: falha de rede, um connect-src da CSP sem a origem de coleta, uma extensão que bloqueia, uma origem de página que não está entre as origens permitidas da aplicação, um preflight de CORS recusado porque a instrumentação da página adicionou cabeçalhos de requisição, ou o limite de novas sessões da plataforma inteira (100 por segundo, compartilhado por todas as aplicações) |
Confira a origem em Integração e saúde › Origens das páginas, o seu connect-src e a sua configuração de rastreamento; caso contrário, trate como transitório |
AUTHENTICATION_FAILED |
401/403 do seu domínio de coleta em uma origem corretamente permitida: uma publicKey que não é desta aplicação, ou uma versão do SDK para a qual a coleta da sua aplicação não está configurada |
Confira a chave pública emitida no console; instale a versão do SDK que o console oferece e fale com o suporte antes de mudá-la |
RATE_LIMITED |
Acima da taxa de novas sessões da aplicação ou das 1000 sessões simultâneas dela, mais de 128 contextos de evento em uma sessão, um collect() a menos de um segundo da abertura de sessão anterior, ou o próprio backoff do SDK depois de um Retry-After |
Não faça nova tentativa em loop; continue com null; libere sessões com close() |
SERVER_UNAVAILABLE |
503 (ou outro status inesperado), inclusive um evento cuja janela de evidência da coleta terminou |
Continue com null; colete mais perto da ação |
TIMEOUT |
Uma requisição passou do prazo no cliente (depois da nova tentativa dentro da chamada), ou o orçamento de 5000 ms do evento acabou | Continue com null; se for frequente, prepare o contexto mais cedo |
CAPABILITY_EXPIRED |
Um handle de uma sessão que o cliente não usa mais. Desde a 0.2.0, um novo collect() depois de um aceito abre uma nova sessão e aposenta a observação anterior: createEvent sobre ela lança este código e uma nova consulta para um evento criado a partir dela é recusada, enquanto um token que ela já devolveu continua utilizável até o expiresAt dele e uma consulta já iniciada é concluída. O fim dos 30 minutos da sessão tem o mesmo efeito |
Use a coleta mais recente. Com o helper, take() o informa, como EVENT ou CONTEXT, só quando aquele take não pode iniciar a coleta substituta: ele já iniciou a sua única coleta, um backoff de coleta está correndo, ou o limite de substituições causadas por falha a retém (300 segundos desde a anterior). Enquanto a última coleta do helper tiver falhado e a coleta em uso estiver desatualizada, é informado o motivo de COLLECT em vez disso (etapas e motivos). Um collect() seu ao lado do helper torna isso mais provável |
EVENT_CONTEXT_EXPIRED |
getEventContext chamado de novo para um evento cujo token passou do expiresAt |
Use null para essa ação; uma nova ação cria um novo evento |
CONFLICT |
Um conflito 409 com o estado da sessão ou do evento. Com a 0.1.1, também um segundo collect() no mesmo cliente |
Continue com null; com a 0.1.1, veja abaixo |
ABORTED |
O signal que você passou foi abortado |
Esperado depois do seu próprio cancelamento; nunca passe um signal ligado ao encerramento da página |
CLOSED |
O cliente foi fechado | Inicialize um novo cliente para as ações seguintes |
INVALID_HANDLE |
Um handle de observação ou de evento que este cliente não conhece (de outro cliente, ou de antes do close()), ou uma observação que não foi aceita |
Passe os handles que este cliente devolveu |
INVALID_INPUT |
Um argumento de opções diferente de { signal } com um AbortSignal |
Corrija a chamada |
INVALID_RESPONSE |
Uma resposta que falhou nas verificações do SDK, por exemplo alterada por um proxy, CDN ou WAF na frente do domínio de coleta | Mantenha o CNAME de coleta como DNS-only, sem nada na frente |
CLOCK_INVALID |
O relógio ou o temporizador monotônico do navegador se comportou de forma inválida | Continue com null |
BODY_TOO_LARGE |
Uma requisição do SDK acima do limite de tamanho dela | Informe ao suporte, com a versão do SDK |
UNSUPPORTED_RUNTIME |
O navegador não tem um recurso exigido, como aleatoriedade criptográfica | Continue com null |
Etapas e motivos
Seção intitulada “Etapas e motivos”Quando take() não tem contexto, failure.stage diz onde a ação parou e failure.reason, por
quê. Os motivos são os motivos de falha acima mais estes. Cada take() inicia
no máximo uma coleta e duas consultas.
stage |
reason |
O que o helper faz em seguida |
|---|---|---|
COLLECT |
SERVER_UNAVAILABLE, RATE_LIMITED, TIMEOUT, TRANSPORT_ERROR, INVALID_RESPONSE, AUTHENTICATION_FAILED… |
A coleta do helper falhou (AUTHENTICATION_FAILED quando a abertura de sessão é recusada, por exemplo porque a coleta da sua aplicação não está configurada para esta versão do SDK). prefetch() nunca a repete. Cada take() a repete uma vez, depois de um backoff de 1, 2, 4… segundos (no máximo 300, nunca abaixo do Retry-After); a nova tentativa reenvia a mesma observação, então não é uma nova coleta que conta como uso. Até que uma coleta seja aceita, um take() cuja coleta em uso está desatualizada continua informando este motivo, inclusive durante o backoff, mesmo quando a consulta dele na coleta desatualizada falha com EVENT ou CONTEXT; um token utilizável vindo dessa consulta ainda prevalece |
COLLECT |
INVALID_INPUT, BODY_TOO_LARGE, UNSUPPORTED_RUNTIME |
A coleta nunca pode ter sucesso nesta página: o helper não envia mais requisições e continua respondendo com este motivo |
COLLECT |
INVALID_CONFIGURATION, UNSUPPORTED_RUNTIME, CONFIGURATION_CONFLICT, UNEXPECTED |
initializeSdk() ou createActionContextHelper() lançou exceção; a receita mantém esse código até o closeSdk() |
EVENT |
CAPABILITY_EXPIRED, INVALID_HANDLE |
A sessão da coleta terminou: o helper descarta a coleta e a próxima intenção coleta de novo, sob o limite de substituições causadas por falha (próxima linha) |
CONTEXT |
AUTHENTICATION_FAILED, CAPABILITY_EXPIRED, INVALID_HANDLE |
O helper descarta a coleta. Sob o limite de substituições causadas por falha (esta, dois eventos com falha seguidos, a nova tentativa de uma limitação transitória, ou uma coleta que nunca deu um token, tenha a coleta dado um token antes ou não), cada uma é enviada no mínimo 300 segundos depois do envio da anterior. A primeira pode vir na próxima intenção (prefetch() ou take()); se a coleta que ela traz também falhar, só um take() a substitui. Um token não suspende o limite. Uma coleta substituída pela idade ou pelos 128 tokens dela, ou uma que a sua página fez com collect(), deixa o prefetch() substituir de novo; os 300 segundos continuam valendo |
CONTEXT |
SERVER_UNAVAILABLE, RATE_LIMITED |
A próxima consulta espera 1, 2, 4… segundos (no máximo 60, nunca abaixo do Retry-After). Dois desses eventos seguidos, EVENT_CONTEXT_EXPIRED incluído, em uma coleta que já deu um token fazem a próxima intenção coletar de novo, sob o limite acima; até que isso seja possível, o helper continua consultando a coleta e entrega o token dela se os eventos se recuperarem. Uma coleta que nunca deu um token só é substituída 300 segundos depois de ter sido enviada |
CONTEXT |
EVENT_CONTEXT_EXPIRED |
A consulta respondeu expirado, o que conta para os dois seguidos acima, sem espera; ou o token veio com menos de minRemainingMs restantes, então ele nunca é entregue |
CONTEXT |
TIMEOUT, TRANSPORT_ERROR, INVALID_RESPONSE |
A próxima consulta na mesma coleta espera, como acima. Estes nunca contam para os dois seguidos e nunca iniciam uma nova coleta |
CONTEXT |
PROCESSING |
A coleta ainda estava em processamento quando o orçamento da consulta acabou. A contagem de eventos seguidos recomeça, e na próxima decisão ela é consultada de novo |
CONTEXT |
INCONCLUSIVE:<limitation code>, FAILED:<limitation code> |
A limitação técnica do servidor. DETECTOR_TIMEOUT, DETECTOR_ERROR, PROVIDER_UNAVAILABLE e REQUIRED_EVIDENCE_MISSING iniciam uma nova coleta por código por página, sob o limite acima; qualquer outro código continua sendo a resposta, sem requisição, até que a coleta possa ser substituída |
WAIT |
WAIT_EXCEEDED |
waitMs acabou antes; o trabalho ainda em andamento continua e serve à próxima ação |
WAIT |
CLOSED, INVALID_INPUT, CLOCK_INVALID |
O cliente foi fechado, take() recebeu uma opção inválida, ou o relógio do navegador se comportou mal |
Logout e troca de conta
Seção intitulada “Logout e troca de conta”No logout ou quando a conta muda, chame o closeSdk() da receita (ele fecha o cliente e descarta
os handles dele) antes da primeira ação da próxima conta. close() devolve TERMINATED,
UNCERTAIN ou LOCAL_ONLY; fechar no navegador não substitui o logout do seu backend. Nunca envie
o contexto de um usuário sob a conta de outro usuário. Não atrase o logout nem a troca de conta por
causa de uma avaliação opcional. Se algum dos dois interromper um contexto não efetivado, preserve o
erro/fallback normal e a requisição lógica existente.
Versões anteriores
Seção intitulada “Versões anteriores”Código de página escrito para o SDK 0.2.x
Seção intitulada “Código de página escrito para o SDK 0.2.x”Código de página escrito para a 0.2.x roda sem mudanças na 0.3.0: todo export e todo tipo da 0.2.x é igual. A coleta da sua aplicação aceita uma versão do SDK por vez, então fale com o suporte do FingerTrackID antes de mudá-la (atualização). Levar esse código para a receita acima é recomendado, mas pode ficar para um deploy seu posterior.
Se você ainda roda o SDK 0.1.1
Seção intitulada “Se você ainda roda o SDK 0.1.1”Tudo acima se aplica, exceto o helper, com estas diferenças:
- Uma coleta por cliente. Um segundo
collect()depois de um aceito devolveUNAVAILABLEcom o motivoCONFLICT, e o cliente mantém a sessão dele. Para coletar de novo, façaawait close()e chameinitializeSdk()para um novo cliente, mas só quando nenhum token do cliente antigo ainda estiver pendente, isto é, enviado ou prestes a ser enviado e ainda não armazenado por uma avaliação: fechar encerra a sessão dele, e esse token deixa de funcionar. Enquanto a coleta aceita estiver atual, uma nova ação pode, em vez disso, criar um novo evento a partir dela. - Sem
prepare()e semcreateActionContextHelper(). O primeirocollect()abre a sessão. getEventContexttem um único orçamento de 2000 ms por evento e no máximo três requisições de contexto do evento.- Toda requisição do SDK é precedida por um preflight de CORS, e a regra dos cabeçalhos de rastreamento se aplica da mesma forma.
O SDK 0.1.0 também nunca abre uma segunda sessão: depois dos 30 minutos dele, collect() devolve
CAPABILITY_EXPIRED até que você faça await close() e chame initializeSdk() de novo. Instale o
pacote que o console oferece.
O que o SDK coleta
Seção intitulada “O que o SDK coleta”A lista completa, para a sua política de privacidade, está em
dados coletados e tratamento. Em resumo: um perfil do
navegador limitado pela política de coleta da sua aplicação: dados do navegador e do user-agent
(inclusive client hints de baixa entropia), tela, fuso horário, idioma, marcadores de automação como
navigator.webdriver, verificações de privacidade e de consistência e, sob a política gerenciada
de coleta atual, características de renderização de canvas, WebGL, áudio offline e fontes
instaladas. As características de renderização só saem do navegador como valores derivados: hashes
de tamanho fixo, uma família de GPU, booleanos, uma contagem e uma máscara de presença sobre uma
lista fixa de fontes. Pixels, amostras de áudio, strings de fabricante e de renderizador do WebGL e
nomes de fontes nunca saem do navegador.
O SDK não coleta client hints de alta entropia nem comportamento de ponteiro e de digitação, e o
código dele não lê nem grava cookies, localStorage ou sessionStorage. Para distinguir uma
janela privada de uma normal, ele usa o armazenamento do navegador por pouco tempo: em navegadores
baseados em Chromium, ele grava blocos preenchidos com zeros em um banco de dados IndexedDB
temporário chamado __ftid_pp_… e o apaga ao fim da verificação (um banco deixado por uma página
fechada no meio da verificação é apagado por uma verificação posterior), e lê a estimativa de cota
de armazenamento; no Safari e no Firefox, ele abre uma vez o sistema de arquivos privado da origem,
o que pode criar a raiz vazia dessa origem. Nada disso armazena dados sobre o usuário.
O navegador gerencia um cookie assinado, HttpOnly e Secure, do seu domínio de coleta, para
continuidade; o script nunca o lê. Se o cookie estiver ausente ou for rejeitado, a continuidade
entre visitas diminui, mas a coleta ainda assim termina normalmente.
Suporte
Seção intitulada “Suporte”Para o suporte, guarde os IDs de operação, request_id, correlation_id, códigos de erro públicos,
timestamps e versões. Nunca envie tokens, segredos, cookies, cabeçalhos brutos, dados pessoais nem
payloads completos.