Ir al contenido
En producción, en modo shadow: FingerTrackID recomienda, su sistema decide.Estado →

Captura y contexto del evento

Esta página describe el SDK 0.3.0, el paquete que ofrece la consola (notas de la versión). Su API añade createActionContextHelper, que ejecuta por usted todo el ciclo de vida descrito más abajo; todo lo demás conserva la API de 0.2.x. Si su frontend todavía ejecuta una versión anterior, lea también versiones anteriores.

El SDK mantiene su estado en la memoria de la página y convierte una captura aceptada en un token opaco y de corta duración por cada acción de negocio. Su backend envía ese token con su evaluación. Para un inicio de sesión:

  1. Inicialice una vez por página, en código del navegador, y mantenga el cliente en un módulo que sobreviva a sus componentes de inicio de sesión. initializeSdk() no hace ninguna solicitud.
  2. Prepare en la primera intención. Al primer foco o pulsación del puntero en el formulario de inicio de sesión, llame a prefetch(): captura e inicia el contexto del evento, de modo que el token suele estar listo en el momento del envío. No al cargar la página: cada carga abriría una sesión y una captura que cuenta como uso (la unidad de uso), también para los visitantes que nunca inician sesión.
  3. Mantenga la frescura. La evidencia del navegador solo es utilizable dentro de la ventana de evidencia de su aplicación, 60 o 300 segundos desde que la captura llega a FingerTrackID. El helper deduce la ventana del propio expiresAt de los tokens y vuelve a capturar solo cuando es necesario: la evidencia expiraría antes de su margen, la sesión de la captura terminó, dos consultas seguidas fueron rechazadas o expiraron en una captura que ya había dado un token, o volvió una limitación transitoria (una vez por página). Una captura que nunca dio un token se reemplaza solo cuando su sesión terminó (una sola vez consecutiva), por ese único reintento, o 300 segundos después de enviarse, de modo que una caída que afecte solo a los contextos de evento cuesta como máximo una captura adicional cada 300 segundos (etapas y motivos). Su reloj cuenta el tiempo que el dispositivo pasó en suspensión.
  4. En el envío, tome el contexto de esta acción con take(), que espera como máximo waitMs y nunca aborta el SDK. Envíe el token y su expiresAt con la solicitud de negocio, o null junto con el motivo, para sus registros.
  5. Cierre solo cuando haya terminado. Llame a close() después de que su backend haya respondido a la solicitud que llevaba el token, al cerrar sesión o al cambiar de cuenta. Cerrar antes termina la sesión que emitió el token, y un token cuya evaluación aún no se ha almacenado deja de funcionar.

En producción, el 2026-10-15 (una aplicación, una muestra pequeña, no un SLA), getEventContext sobre una captura aceptada tardó 0,15 s en la mediana, mientras que una captura iniciada en el clic tardó 2,6 s (medido en producción): diseñe para que el token esté listo antes del envío, en lugar de obtenerlo en el envío.

Un módulo por página. Usa solo la API pública del SDK, nunca lanza excepciones y nunca aborta el SDK.

  • prepareNextAction() se ejecuta en el primer focusin o pointerdown en el formulario, y en cada uno posterior; para un botón de inicio de sesión, en su pointerenter, focus y pointerdown. Nunca la llame al cargar la página: cada carga abre una captura que cuenta como uso (la unidad de uso). Nunca espera y nunca reintenta por sí sola una captura fallida.
  • takeActionContext() se ejecuta una vez por acción de negocio, en su envío, y se resuelve dentro de SUBMIT_WAIT_MS con { context, failure }. Use el objeto que devuelve para esa acción y para cada reintento de ella, incluido null: no tome otro contexto para cambiar una solicitud ya enviada. Un token nunca se entrega a dos acciones.
  • Un clic que es la primera intención, o que sigue a una captura cuya evidencia caducó, espera hasta SUBMIT_WAIT_MS mientras el helper captura; muestre el progreso durante la espera. En dispositivos táctiles, pointerdown llega justo antes del clic, por lo que el primer toque a menudo espera.
  • La frescura proviene del expiresAt del token, y la antigüedad de una captura cuenta el tiempo que el dispositivo pasó en suspensión. El helper nunca entrega un token al que le queden menos de minRemainingMs (15000 ms por defecto; opción de createActionContextHelper). Manténgalo por encima del margen que su backend necesita para almacenar la evaluación (guía de integración).
  • Con el helper, no llame usted mismo a prepare(), collect() ni getEventContext(), y no llame directamente al close() del cliente: use closeSdk(). Un collect() propio se adopta, pero cuesta una captura propia.
  • Un contexto null nunca es silencioso: failure indica la etapa y el código (etapas y motivos). Cuéntelos de su lado; una integración en la que todo inicio de sesión llega al backend con null se ve, desde el lado de FingerTrackID, exactamente igual que una en la que el SDK nunca se ejecutó. El código es solo para sus registros: la solicitud de evaluación no tiene ningún campo para él.
  • Envíe expiresAt con el token para que su backend pueda descartar un token que expiró antes de poder usarse.
  • Elija SUBMIT_WAIT_MS a partir de sus propias mediciones (0 a 10000 ms). Con la precarga, el contexto dispone del tiempo que el usuario pasa en el formulario para resolverse. Una captura iniciada en el clic tardó 2,6 s en la mediana en producción (medido en producción), por encima de los 2500 ms de esta receta: preparar en la primera intención, para que una captura aceptada y reciente esté esperando en el clic, importa más que subir el tope.
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
}

La receta anterior sirve tanto para un formulario clásico como para una aplicación de página única. En ambos casos, el token va con la solicitud que su backend evalúa: el formulario de contraseña, o el formulario del código MFA cuando su inicio de sesión lo pide (llame a prepareNextAction() cuando ese formulario reciba el foco o una pulsación del puntero y a takeActionContext() en su envío, como para el formulario de contraseña). Nunca envíe valores del formulario al SDK.

Un formulario enviado descarga la página, y la descarga cancela las solicitudes del SDK. Retenga el envío hasta que el contexto se resuelva o se agote su tope, escríbalo en campos ocultos y, después, envíe el formulario.

  • Mantenga method="post": un formulario GET pondría el token en la URL.
  • submit() no envía el nombre ni el valor del botón pulsado; si su backend los lee, póngalos en un campo oculto. Llamarlo a través de HTMLFormElement.prototype hace que siga funcionando incluso si un campo se llama submit.
  • No llame a close() aquí: la página que responde no tiene cliente del SDK. La sesión termina por sí sola después de 30 minutos y ocupa una plaza de sesión hasta entonces.
<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
})
  • Llame a prepareNextAction() desde los manejadores de foco y de pulsación del puntero del formulario. En React, onFocus y onPointerDown en el elemento <form> reciben los de cada campo.
  • Mantenga el SDK fuera del estado del componente. Un componente que se desmonta en el envío (un cambio de ruta, un indicador de carga que reemplaza el formulario) no debe llevarse consigo el cliente ni su trabajo pendiente.
  • No llame a closeSdk() en la limpieza de un efecto. El StrictMode de React ejecuta las limpiezas una vez más en desarrollo, y una limpieza que cierra el cliente termina la sesión de un token aún no enviado.
  • No pase a las llamadas del SDK un AbortSignal vinculado al componente o a la ruta, y no aborte para dejar de esperar: takeActionContext() deja de esperar por sí sola y deja el SDK en ejecución.
  • Navegue después de que fetch se resuelva, nunca antes.

Los inicios de sesión que salen de la página (inicio de sesión con Google, otros proveedores OAuth) necesitan un paso más: inicio de sesión con Google, OAuth y otras redirecciones.

// 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
}

La evidencia del navegador es utilizable durante un tiempo limitado, contado desde el momento en que FingerTrackID recibe la captura enviada por collect(), no desde la carga de la página ni desde initializeSdk(). La duración la fija por aplicación la configuración gestionada de captura de producción cuando se aprovisiona la aplicación:

Aplicación Ventana de evidencia
Aprovisionada antes de que se introdujera la ventana de 300 segundos (octubre de 2026), incluidas las primeras aplicaciones piloto 60 segundos
Aprovisionada desde entonces 300 segundos

Una aplicación existente conserva su ventana hasta que FingerTrackID la cambie con usted; nada la cambia en silencio. Para ver la suya:

  • Toda respuesta de evaluación la lleva en data_freshness.maximum_required_age_ms (60000 o 300000), incluidas las evaluaciones enviadas con event_context_token: null.
  • Para un token concreto, el límite que cuenta es siempre su expiresAt, descrito más abajo.
  • En caso de duda, pida al soporte de FingerTrackID el valor de su aplicación. El código que respeta expiresAt funciona con cualquiera de las dos ventanas; solo su margen de captura necesita caber en la más corta.

Cómo se aplica la ventana:

  • getEventContext solo puede devolver un token dentro de la ventana. Una vez terminada la ventana de la captura, devuelve UNAVAILABLE con el motivo SERVER_UNAVAILABLE (o INCONCLUSIVE, si la captura solo se evaluó después de su ventana).
  • El expiresAt devuelto es el más temprano de: la emisión del token más la duración de la ventana, el final de la ventana de la captura, el final de la sesión del SDK y el final de la capacidad actual del SDK. Por lo tanto, nunca es posterior a la llegada de la captura a FingerTrackID más la duración de la ventana. Volver a llamar a getEventContext sobre el mismo handle después de expiresAt devuelve UNAVAILABLE con el motivo EVENT_CONTEXT_EXPIRED.
  • La evaluación que lleva el token debe almacenarse antes de expiresAt. Una evaluación nueva con un token expirado (o ya usado) se rechaza con 400 event-context-invalid: no es reintentable, no se almacena nada y los IDs de operación de esa solicitud quedan vinculados a ese token. En su lugar, evalúe el evento como una operación nueva con event_context_token: null (guía de integración).
  • Una evaluación ya almacenada no se ve afectada: un reintento con el mismo cuerpo devuelve el resultado almacenado (200) incluso después de expiresAt.

El navegador envía a su backend solo el token y su expiresAt (o null), en la solicitud de negocio que ya hace. Su backend pone el token en el campo event_context_token de POST /v1/assessments, o null si expiresAt deja demasiado poco tiempo para almacenar la evaluación (la ventana de evidencia).

  • El token es opaco y de un solo uso: se consume una vez por evento de negocio. No lo decodifique, no lo escriba en registros ni en analítica, no lo ponga en una URL ni lo use como su propio token de autorización.
  • Su backend nunca acepta del navegador referencias de persona y de cuenta ni occurred_at.
  • Cuando no hay token, su backend envía igualmente la evaluación con event_context_token: null (guía de integración).
  • Una solicitud ya enviada a FingerTrackID conserva su token original (o null) en cada reintento.
  • Mantenga vivo el cliente del SDK hasta que su backend haya respondido. Hasta que se almacene la evaluación que lleva el token, terminar la sesión que lo emitió (close()) invalida el token. Nunca amplíe el plazo original del contexto o de la solicitud para esperar a que termine.
  • Con el perfil gestionado de producción actual, una evaluación almacenada sin contexto es INCONCLUSIVE con trust_score: null, nunca COMPLETED. Con contexto, es COMPLETED cuando al menos el 30 % de los 13 indicadores se evaluaron con evidencia reciente (evidence_coverage ≥ 30, contados en conjunto los indicadores de navegador, de red y de historial del dispositivo) y los dos indicadores que cuentan para la puntuación, BOT_AUTOMATION y BROWSER_SPOOFING, se evaluaron; en caso contrario, es INCONCLUSIVE. Si no se encuentra nada adverso, el trust_score es 100, y la recomendación gestionada es MONITOR en todas las franjas: la puntuación informa, nunca decide. Una vez que su empresa activa sus propias reglas de riesgo, la puntuación y las franjas las siguen, y todo indicador que usted active pasa a ser obligatorio: un indicador activo sin evidencia reciente utilizable (por ejemplo, un indicador de red mientras la evidencia de red no está disponible) deja la evaluación sin puntuación (trust_score: null, MONITOR), incluso por encima del 30 % de cobertura. El contexto es lo que adjunta la evidencia del navegador (consulte cómo saber que llegó la evidencia del SDK). La llamada aún puede terminar en un Problem operativo (por ejemplo, 503). Valide la respuesta o el Problem reales con la política emitida de su aplicación.

Un cliente abre una sesión de navegador para su aplicación solo cuando necesita por primera vez autoridad del servidor: en prepare() o en el primer collect(). initializeSdk() por sí solo no abre ninguna sesión ni consume cuota. Los límites son por aplicación, iguales para todas las aplicaciones, y no requieren configuración:

Límite Valor
Sesiones nuevas 5 por segundo, con una ráfaga de 20
Sesiones vivas concurrentes Hasta 1000 por aplicación
Duración de la sesión 30 minutos desde la operación que la abre, nunca se amplía
Solicitudes por sesión Una solicitud de captura o de contexto del evento cada 250 ms, que el SDK respeta por sí solo
Contextos de evento por sesión 128

Una sesión deja de contar para el límite de concurrencia cuando termina: cuando se termina (close(), o cuando el SDK la retira, más abajo) o cuando se agotan sus 30 minutos. Una sesión que nunca se cierra ocupa su plaza durante los 30 minutos completos, incluso después de que el visitante se haya ido. Cuando se alcanza un límite, se rechaza una sesión nueva y collect() devuelve status: 'UNAVAILABLE' con el motivo RATE_LIMITED, tanto por encima de la tasa de sesiones nuevas como en el límite de concurrencia; en el límite de concurrencia, el Retry-After que el SDK respeta es el tiempo hasta que expire la primera de las sesiones ocupadas, como máximo 30 minutos. Continúe la acción de negocio con un contexto null, como con cualquier otro fallo de captura, y nunca reintente en bucle. Consulte también los límites de la API.

Cómo usa las sesiones el SDK (desde 0.2.0):

  • Cada captura nueva después de una aceptada abre una sesión nueva, como máximo una por segundo por cliente (un collect() antes de ese plazo devuelve RATE_LIMITED, y la captura anterior sigue siendo utilizable). El cliente termina la sesión reemplazada una vez que ha expirado el último token que emitió (como máximo 300 segundos), de inmediato si no emitió ninguno, o en close(). Un cliente mantiene como máximo tres sesiones reemplazadas en espera; por encima de eso, termina la más antigua.
  • Después de 30 minutos, el cliente abre por sí solo la siguiente sesión en su próximo collect(), prepare() o renew(); nada se reabre en segundo plano. Los handles de la sesión anterior devuelven CAPABILITY_EXPIRED (createEvent y getEventContext sobre ellos no abren ninguna sesión), y el helper vuelve a capturar. Desde 0.3.0, esos 30 minutos cuentan el tiempo que el dispositivo pasó en suspensión.

Para mantenerse dentro de las 1000 sesiones concurrentes:

  • Prepare solo en las páginas que tienen una acción evaluada (inicio de sesión, retiro, negociación), nunca en cada vista de página, y solo en la primera intención (foco o pulsación del puntero) de su formulario: así, las sesiones se abren solo para los visitantes que inician la acción.
  • Llame a close() una vez que su backend haya respondido y la página haya terminado con el cliente, y al cerrar sesión o al cambiar de cuenta.

FingerTrackID no publica ningún SLA de latencia. Las primeras cifras medidas en producción están más abajo; provienen de una aplicación y de una muestra pequeña. Planifique con los límites que siguen, mida en su propia página (desde collect() hasta un contexto COMPLETED) y elija SUBMIT_WAIT_MS según lo que observe. Estos límites son cotas del protocolo, no la latencia esperada ni un SLA.

Cada solicitud tiene un plazo en el cliente más largo que el presupuesto del servidor para esa ruta, porque una solicitud del navegador también consume DNS, TLS, el preflight de CORS y el viaje de vuelta:

Paso SDK 0.2.0 y posteriores Presupuesto del servidor
Apertura de sesión (prepare() o el primer collect()) 3000 ms por intento; un intento con tiempo de espera agotado, perdido, 429 o 503 se repite una vez en la misma llamada cuando la espera para repetirlo cabe en un segundo 1000 ms
Captura 4000 ms por intento 2000 ms
Contexto del evento 1500 ms por solicitud, dentro de un presupuesto de 5000 ms por evento, como máximo 8 solicitudes de contexto del evento por evento 500 ms por solicitud
Renovación de la capacidad y terminación de la sesión 1500 ms por intento 1000 ms
  • Los 5000 ms de un evento empiezan con su primera llamada a getEventContext y nunca se reinician. Dentro de ellos, el SDK vuelve a preguntar 400 ms después de cada respuesta PROCESSING (o después de un Retry-After más largo) y repite por sí solo una consulta con tiempo de espera agotado, perdida, 429 o 503. Ninguna consulta empieza con menos de 1000 ms restantes. Cuando se agota el presupuesto, el evento conserva su última respuesta, que puede ser PROCESSING.
  • Una sesión envía una solicitud de captura o de contexto del evento a la vez, al menos 250 ms después de que la anterior se resolviera.
  • Un envío sin nada preparado necesita al menos tres viajes de ida y vuelta a su dominio de captura antes de que exista un token (apertura de sesión, captura, contexto del evento), más el procesamiento de la captura por FingerTrackID. Por eso la receta precarga en la primera intención.
  • La primera solicitud a cada ruta también puede necesitar un preflight de CORS. 0.2.0 permite que el navegador lo reutilice (el servidor permite almacenarlo en caché durante 600 segundos); 0.1.1 enviaba un preflight antes de cada solicitud.
  • Los encabezados de solicitud añadidos por la instrumentación de la página hacen fallar todas las solicitudes (encabezados de rastreo).

Medido el 2026-10-15 en producción, en una aplicación:

Medición Muestra Mediana p95 Máximo
getEventContext sobre una captura aceptada, hasta el token 10 0,15 s 0,17 s —
Una captura iniciada en el clic, desde la apertura de sesión hasta el final de la evaluación de la captura 13 2,6 s 3,9 s 4,4 s

Es una muestra pequeña de una sola aplicación: léala como indicativa, no como un SLA, y mida su propia página, su red y los dispositivos de sus usuarios. La segunda fila es aproximadamente lo que un envío sin nada preparado espera antes de que su token pueda existir; preparar en la primera intención deja solo la primera fila en el envío.

Qué lanza una excepción y qué devuelve un motivo

Sección titulada «Qué lanza una excepción y qué devuelve un motivo»
Llamada Lanza En otro caso, devuelve
initializeSdk(configuration) Un Error cuyo message es INVALID_CONFIGURATION, UNSUPPORTED_RUNTIME o CONFIGURATION_CONFLICT (y CLOCK_INVALID en un navegador cuyo reloj monotónico falla) El cliente, de forma síncrona
createEvent(observation) Un Error cuyo message es CLOSED, INVALID_HANDLE o CAPABILITY_EXPIRED (y CLOCK_INVALID o UNSUPPORTED_RUNTIME en un navegador defectuoso) El handle del evento, de forma síncrona
prepare({ signal }?) Nunca; la promesa nunca se rechaza READY, o UNAVAILABLE con un reason
collect({ signal }?) Nunca ACCEPTED con su observation, o UNAVAILABLE con un reason y la observation pendiente (o null)
retryCollection(observation, { signal }?) Nunca Lo mismo que collect(); una observación ya aceptada responde ACCEPTED sin solicitud
getEventContext(event, { signal }?) Nunca COMPLETED con eventContextToken y expiresAt; PROCESSING cuando su presupuesto terminó antes; INCONCLUSIVE o FAILED con un limitationCode; o UNAVAILABLE con un reason
renew({ signal }?) Nunca RENEWED, NOT_DUE, o UNAVAILABLE con un reason
close() Nunca TERMINATED, UNCERTAIN o LOCAL_ONLY
createActionContextHelper(client, { minRemainingMs, waitMs }?) Un Error cuyo message es INVALID_INPUT (opciones distintas de esos dos enteros, minRemainingMs 1000–30000 y waitMs 0–10000), INVALID_HANDLE (no es un cliente de esta copia del SDK), CLOSED o CONFIGURATION_CONFLICT (el cliente ya tiene un helper con otras opciones) El helper, de forma síncrona; las mismas opciones devuelven el mismo helper
helper.prefetch() Nunca Nada; nunca espera
helper.take({ waitMs }?) Nunca; la promesa nunca se rechaza { context: { token, expiresAt }, failure: null } o { context: null, failure: { stage, reason } }
helper.close() Nunca El resultado del close() del cliente

UNAVAILABLE lleva retryAfterMs después de un 429 o 503 del servidor y cuando el propio backoff del SDK rechaza una solicitud. El SDK renueva por sí solo su capacidad de corta duración dentro de estas llamadas; renew() solo lo hace por adelantado y nunca amplía la sesión de 30 minutos. close() devuelve TERMINATED cuando todas las sesiones que tenía confirmaron su terminación; UNCERTAIN cuando alguna no lo hizo, cuando una apertura de sesión seguía en curso o cuando no tenía ninguna sesión después de intentar abrir una; y LOCAL_ONLY cuando nunca intentó abrir una.

Todo resultado UNAVAILABLE lleva un reason (tipo SdkFailureReason), y createEvent lanza un Error cuyo message es uno de los mismos códigos. En todos los casos, continúe la acción de negocio con un contexto null.

reason Causa probable Qué hacer
TRANSPORT_ERROR La solicitud no se completó, o el navegador ocultó la respuesta: fallo de red, un connect-src de la CSP sin el origen de captura, una extensión que bloquea, un origen de página que no es uno de los orígenes permitidos de la aplicación, un preflight de CORS rechazado porque la instrumentación de la página añadió encabezados de solicitud, o el límite de sesiones nuevas de toda la plataforma (100 por segundo, compartidas por todas las aplicaciones) Compruebe el origen en Integração e saúde › Origens das páginas, su connect-src y su configuración de rastreo; en caso contrario, trátelo como transitorio
AUTHENTICATION_FAILED 401/403 de su dominio de captura en un origen correctamente permitido: una publicKey que no es la de esta aplicación, o una versión del SDK para la que la captura de su aplicación no está configurada Compruebe la clave pública emitida en la consola; instale la versión del SDK que ofrece la consola y póngase en contacto con el soporte antes de cambiarla
RATE_LIMITED Por encima de la tasa de sesiones nuevas de la aplicación o de sus 1000 sesiones concurrentes, más de 128 contextos de evento en una sesión, un collect() a menos de un segundo de la apertura de sesión anterior, o el propio backoff del SDK después de un Retry-After No reintente en bucle; continúe con null; libere sesiones con close()
SERVER_UNAVAILABLE 503 (u otro estado inesperado), incluido un evento cuya captura ya agotó su ventana de evidencia Continúe con null; capture más cerca de la acción
TIMEOUT Una solicitud superó su plazo en el cliente (después del reintento dentro de la llamada), o se agotó el presupuesto de 5000 ms del evento Continúe con null; si es frecuente, prepare el contexto antes
CAPABILITY_EXPIRED Un handle de una sesión que el cliente ya no usa. Desde 0.2.0, un collect() nuevo después de uno aceptado abre una sesión nueva y retira la observación anterior: createEvent sobre ella lanza este código y se rechaza una consulta nueva para un evento creado a partir de ella, mientras que un token que ya devolvió sigue siendo utilizable hasta su expiresAt y una consulta ya iniciada termina. El final de los 30 minutos de la sesión tiene el mismo efecto Use la captura más reciente. Con el helper, take() lo informa, como EVENT o CONTEXT, solo cuando esa llamada no puede iniciar la captura de reemplazo: ya inició su única captura, hay un backoff de captura en curso, o el límite de reemplazos causados por un fallo la retiene (300 segundos desde la anterior). Mientras la última captura del helper haya fallado y la actual ya no sea reciente, se informa en su lugar el motivo de COLLECT (etapas y motivos). Un collect() propio junto al helper lo hace más probable
EVENT_CONTEXT_EXPIRED getEventContext llamado de nuevo para un evento cuyo token superó expiresAt Use null para esa acción; una acción nueva crea un evento nuevo
CONFLICT Un conflicto 409 con el estado de la sesión o del evento. Con 0.1.1, también un segundo collect() en el mismo cliente Continúe con null; con 0.1.1, consulte más abajo
ABORTED Se abortó el signal que usted pasó Esperado después de su propia cancelación; nunca pase una señal vinculada al cierre de la página
CLOSED El cliente se cerró Inicialice un cliente nuevo para las acciones posteriores
INVALID_HANDLE Un handle de observación o de evento que este cliente no conoce (de otro cliente, o de antes de close()), o una observación que no fue aceptada Pase los handles que devolvió este cliente
INVALID_INPUT Un argumento de opciones distinto de { signal } con un AbortSignal Corrija la llamada
INVALID_RESPONSE Una respuesta que no superó las comprobaciones del SDK, por ejemplo alterada por un proxy, una CDN o un WAF delante del dominio de captura Mantenga el CNAME de captura como solo DNS, sin nada delante
CLOCK_INVALID El reloj o el temporizador monotónico del navegador se comportó de forma no válida Continúe con null
BODY_TOO_LARGE Una solicitud del SDK por encima de su límite de tamaño Comuníquelo al soporte con la versión del SDK
UNSUPPORTED_RUNTIME Al navegador le falta una funcionalidad requerida, como la aleatoriedad criptográfica Continúe con null

Cuando take() no tiene contexto, failure.stage indica dónde se detuvo la acción y failure.reason, por qué. Los motivos son los motivos de fallo anteriores más estos. Cada take() inicia como máximo una captura y dos consultas.

stage reason Lo que hace el helper a continuación
COLLECT SERVER_UNAVAILABLE, RATE_LIMITED, TIMEOUT, TRANSPORT_ERROR, INVALID_RESPONSE, AUTHENTICATION_FAILED… La captura del helper falló (AUTHENTICATION_FAILED cuando se rechaza la apertura de sesión, por ejemplo porque la captura de su aplicación no está configurada para esta versión del SDK). prefetch() nunca la reintenta. Cada take() la reintenta una vez, después de un backoff de 1, 2, 4… segundos (como máximo 300, nunca por debajo de Retry-After); el reintento reenvía la misma observación, por lo que no es una captura nueva que cuente como uso. Hasta que se acepte una captura, un take() cuya captura actual ya no es reciente sigue informando este motivo, también durante el backoff, incluso cuando su consulta sobre esa captura que ya no es reciente falla con EVENT o CONTEXT; un token utilizable de esa consulta sigue prevaleciendo
COLLECT INVALID_INPUT, BODY_TOO_LARGE, UNSUPPORTED_RUNTIME La captura nunca puede tener éxito en esta página: el helper no envía más solicitudes y sigue respondiendo con este motivo
COLLECT INVALID_CONFIGURATION, UNSUPPORTED_RUNTIME, CONFIGURATION_CONFLICT, UNEXPECTED initializeSdk() o createActionContextHelper() lanzó una excepción; la receta conserva ese código hasta closeSdk()
EVENT CAPABILITY_EXPIRED, INVALID_HANDLE La sesión de la captura terminó: el helper descarta la captura y la siguiente intención vuelve a capturar, sujeta al límite de reemplazos causados por un fallo (fila siguiente)
CONTEXT AUTHENTICATION_FAILED, CAPABILITY_EXPIRED, INVALID_HANDLE El helper descarta la captura. Con el límite de reemplazos causados por un fallo (este, dos eventos fallidos seguidos, el reintento de una limitación transitoria o una captura que nunca dio un token, tanto si la captura había dado un token antes como si no), cada uno se envía no antes de 300 segundos después de que se enviara el anterior. El primero puede llegar en la siguiente intención (prefetch() o take()); si la captura que trae también falla, solo un take() la reemplaza. Un token no levanta el límite. Una captura reemplazada por su antigüedad o por sus 128 tokens, o una que su página hizo con collect(), permite que prefetch() vuelva a reemplazar; los 300 segundos siguen aplicándose
CONTEXT SERVER_UNAVAILABLE, RATE_LIMITED La siguiente consulta espera 1, 2, 4… segundos (como máximo 60, nunca por debajo de Retry-After). Dos de estos eventos seguidos, EVENT_CONTEXT_EXPIRED incluido, en una captura que ya dio un token hacen que la siguiente intención vuelva a capturar, sujeta al límite anterior; hasta que pueda hacerlo, el helper sigue consultando la captura y entrega su token si los eventos se recuperan. Una captura que nunca dio un token se reemplaza solo 300 segundos después de enviarse
CONTEXT EVENT_CONTEXT_EXPIRED La consulta respondió que expiró, lo que cuenta para los dos seguidos de arriba, sin espera; o el token llegó con menos de minRemainingMs restantes, por lo que nunca se entrega
CONTEXT TIMEOUT, TRANSPORT_ERROR, INVALID_RESPONSE La siguiente consulta sobre la misma captura espera, como arriba. Estos nunca cuentan para los dos seguidos y nunca inician una captura nueva
CONTEXT PROCESSING La captura seguía en procesamiento cuando se agotó el presupuesto de la consulta. El recuento de eventos seguidos vuelve a empezar, y la siguiente decisión la consulta de nuevo
CONTEXT INCONCLUSIVE:<limitation code>, FAILED:<limitation code> La limitación técnica del servidor. DETECTOR_TIMEOUT, DETECTOR_ERROR, PROVIDER_UNAVAILABLE y REQUIRED_EVIDENCE_MISSING inician una captura nueva por código y por página, sujeta al límite anterior; cualquier otro código sigue siendo la respuesta, sin solicitud, hasta que la captura pueda reemplazarse
WAIT WAIT_EXCEEDED waitMs terminó antes; el trabajo que sigue en curso continúa y sirve a la siguiente acción
WAIT CLOSED, INVALID_INPUT, CLOCK_INVALID El cliente se cerró, take() recibió una opción no válida o el reloj del navegador falló

Al cerrar sesión o cuando cambia la cuenta, llame a closeSdk() de la receta (cierra el cliente y descarta sus handles) antes de la primera acción de la siguiente cuenta. close() devuelve TERMINATED, UNCERTAIN o LOCAL_ONLY; cerrar en el navegador no sustituye el cierre de sesión de su backend. Nunca envíe el contexto de un usuario bajo la cuenta de otro usuario. No retrase el cierre de sesión ni el cambio de cuenta por una evaluación opcional. Si cualquiera de los dos interrumpe un contexto no confirmado, conserve el error/fallback normal y la solicitud lógica existente.

Código de página escrito para el SDK 0.2.x

Sección titulada «Código de página escrito para el SDK 0.2.x»

El código de página escrito para 0.2.x se ejecuta sin cambios en 0.3.0: todas las exportaciones y tipos de 0.2.x son los mismos. La captura de su aplicación acepta una versión del SDK a la vez, así que póngase en contacto con el soporte de FingerTrackID antes de cambiarla (actualización). Se recomienda pasar ese código a la receta anterior, pero puede hacerse en un despliegue posterior suyo.

Todo lo anterior se aplica, excepto el helper, con estas diferencias:

  • Una captura por cliente. Un segundo collect() después de uno aceptado devuelve UNAVAILABLE con el motivo CONFLICT, y el cliente conserva su sesión. Para volver a capturar, haga await close() y llame a initializeSdk() para un cliente nuevo, pero solo cuando ningún token del cliente anterior siga pendiente, es decir, enviado o a punto de enviarse y aún no almacenado por una evaluación: cerrar termina su sesión y ese token deja de funcionar. Mientras la captura aceptada sea reciente, una acción nueva puede, en cambio, crear un evento nuevo a partir de ella.
  • Sin prepare() ni createActionContextHelper(). El primer collect() abre la sesión.
  • getEventContext tiene un único presupuesto de 2000 ms por evento y como máximo tres solicitudes de contexto del evento.
  • Cada solicitud del SDK va precedida de un preflight de CORS, y la regla de los encabezados de rastreo se aplica de la misma manera.

El SDK 0.1.0 tampoco abre nunca una segunda sesión: después de sus 30 minutos, collect() devuelve CAPABILITY_EXPIRED hasta que usted haga await close() y vuelva a llamar a initializeSdk(). Instale el paquete que ofrece la consola.

La lista completa, para su política de privacidad, está en datos recopilados y tratamiento. En resumen: un perfil del navegador limitado por la política de captura de su aplicación: datos del navegador y del user-agent (incluidas las client hints de baja entropía), pantalla, zona horaria, idioma, marcadores de automatización como navigator.webdriver, comprobaciones de privacidad y de coherencia y, con la política de captura gestionada actual, características de renderizado de canvas, WebGL, audio offline y fuentes instaladas. Las características de renderizado salen del navegador solo como valores derivados: hashes de longitud fija, una familia de GPU, booleanos, un recuento y una máscara de presencia sobre una lista fija de fuentes. Los píxeles, las muestras de audio, las cadenas de proveedor y de renderizador de WebGL y los nombres de fuentes nunca salen del navegador.

El SDK no captura client hints de alta entropía ni el comportamiento del puntero y del teclado, y su código no lee ni escribe cookies, localStorage ni sessionStorage. Para distinguir una ventana privada de una normal, usa brevemente el almacenamiento del navegador: en los navegadores basados en Chromium, escribe bloques rellenos de ceros en una base de datos IndexedDB temporal llamada __ftid_pp_… y la elimina al final de la comprobación (una base de datos que deja una página cerrada a mitad de la comprobación la elimina una comprobación posterior), y lee la estimación de la cuota de almacenamiento; en Safari y Firefox, abre una vez el sistema de archivos privado del origen, lo que puede crear la raíz vacía de ese origen. Nada de esto almacena datos sobre el usuario.

El navegador gestiona una cookie firmada HttpOnly, Secure de su dominio de captura para la continuidad; el script nunca la lee. Si la cookie falta o se rechaza, la continuidad entre visitas se reduce, pero la captura se completa igualmente con normalidad.

Para el soporte, conserve los IDs de operación, request_id, correlation_id, los códigos de error públicos, las marcas de tiempo y las versiones. Nunca envíe tokens, secretos, cookies, encabezados sin procesar, datos personales ni payloads completos.