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

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 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:

  1. 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.
  2. 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.
  3. 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 expiresAt dos 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.
  4. No envio, obtenha o contexto desta ação com take(), que espera no máximo waitMs e nunca aborta o SDK. Envie o token e o expiresAt dele com a requisição de negócio, ou null junto com o motivo, para os seus logs.
  5. 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.

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 primeiro focusin ou pointerdown no formulário, e em cada um dos seguintes; para um botão de login, no pointerenter, no focus e no pointerdown dele. 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 de SUBMIT_WAIT_MS com { context, failure }. Use o objeto que ele devolve para essa ação e para toda nova tentativa dela, inclusive null: 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_MS enquanto o helper coleta; mostre o progresso durante a espera. Em dispositivos de toque, o pointerdown vem logo antes do clique, então o primeiro toque muitas vezes espera.
  • A atualidade vem do expiresAt do 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 de minRemainingMs restantes (15000 ms por padrão; opção de createActionContextHelper). 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() nem getEventContext() você mesmo, e não chame o close() do cliente diretamente: use closeSdk(). Um collect() seu é adotado, mas custa uma coleta própria.
  • Um contexto null nunca é silencioso: failure informa 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 com null parece, 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 expiresAt com o token para que o seu backend possa descartar um token que expirou antes de poder ser usado.
  • Escolha SUBMIT_WAIT_MS a 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 = null
let 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
}

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.

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ário GET colocaria 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 por HTMLFormElement.prototype faz com que ele continue funcionando mesmo se um campo se chamar submit.
  • 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 = false
loginForm.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
})
  • Chame prepareNextAction() a partir dos handlers de foco e de pointer-down do formulário. No React, onFocus e onPointerDown no 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. O StrictMode do 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 AbortSignal ligado 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 fetch resolver, 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 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 (60000 ou 300000), inclusive avaliações enviadas com event_context_token: null.
  • Para um token específico, o limite que vale é sempre o expiresAt dele, descrito abaixo.
  • Em caso de dúvida, pergunte ao suporte do FingerTrackID o valor da sua aplicação. Código que respeita expiresAt funciona com qualquer uma das janelas; só a sua margem de coleta precisa caber na mais curta.

Como a janela se aplica:

  • getEventContext só pode devolver um token dentro da janela. Depois que a janela da coleta termina, ele devolve UNAVAILABLE com o motivo SERVER_UNAVAILABLE (ou INCONCLUSIVE, se a coleta só foi avaliada depois da janela dela).
  • O expiresAt devolvido é 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. Chamar getEventContext de novo no mesmo handle depois do expiresAt devolve UNAVAILABLE com o motivo EVENT_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 com 400 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 com event_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 do expiresAt.

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 é INCONCLUSIVE com trust_score: null, nunca COMPLETED. Com contexto, ela é COMPLETED quando 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_AUTOMATION e BROWSER_SPOOFING, foram avaliados; caso contrário, ela é INCONCLUSIVE. Sem nada adverso encontrado, o trust_score é 100, e a recomendação gerenciada é MONITOR em 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.

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 devolve RATE_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 no close(). 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() ou renew(); nada é reaberto em segundo plano. Handles da sessão antiga devolvem CAPABILITY_EXPIRED (createEvent e getEventContext sobre 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.

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 getEventContext dele e nunca são reiniciados. Dentro deles, o SDK pergunta de novo 400 ms depois de cada resposta PROCESSING (ou depois de um Retry-After mais longo) e repete sozinho uma consulta com timeout, perdida, com 429 ou com 503. Nenhuma consulta começa com menos de 1000 ms restantes. Quando o orçamento acaba, o evento mantém a última resposta, que pode ser PROCESSING.
  • 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 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.

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.

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

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

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.

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.

Tudo acima se aplica, exceto o helper, com estas diferenças:

  • Uma coleta por cliente. Um segundo collect() depois de um aceito devolve UNAVAILABLE com o motivo CONFLICT, e o cliente mantém a sessão dele. Para coletar de novo, faça await close() e chame initializeSdk() 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 sem createActionContextHelper(). O primeiro collect() abre a sessão.
  • getEventContext tem 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.

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.

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.