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 ciclo de vida
Sección titulada «El ciclo de vida»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:
- 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. - 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. - 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
expiresAtde 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. - En el envío, tome el contexto de esta acción con
take(), que espera como máximowaitMsy nunca aborta el SDK. Envíe el token y suexpiresAtcon la solicitud de negocio, onulljunto con el motivo, para sus registros. - 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.
La receta
Sección titulada «La receta»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 primerfocusinopointerdownen el formulario, y en cada uno posterior; para un botón de inicio de sesión, en supointerenter,focusypointerdown. 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 deSUBMIT_WAIT_MScon{ context, failure }. Use el objeto que devuelve para esa acción y para cada reintento de ella, incluidonull: 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_MSmientras el helper captura; muestre el progreso durante la espera. En dispositivos táctiles,pointerdownllega justo antes del clic, por lo que el primer toque a menudo espera. - La frescura proviene del
expiresAtdel 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 deminRemainingMs(15000 ms por defecto; opción decreateActionContextHelper). 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()nigetEventContext(), y no llame directamente alclose()del cliente: usecloseSdk(). Uncollect()propio se adopta, pero cuesta una captura propia. - Un contexto
nullnunca es silencioso:failureindica 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 connullse 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
expiresAtcon el token para que su backend pueda descartar un token que expiró antes de poder usarse. - Elija
SUBMIT_WAIT_MSa 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 = 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}Inicio de sesión con contraseña
Sección titulada «Inicio de sesión con contraseña»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.
POST de formulario clásico
Sección titulada «POST de formulario clásico»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 formularioGETpondrí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 deHTMLFormElement.prototypehace que siga funcionando incluso si un campo se llamasubmit.- 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 = 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})Aplicación de página única (fetch)
Sección titulada «Aplicación de página única (fetch)»- Llame a
prepareNextAction()desde los manejadores de foco y de pulsación del puntero del formulario. En React,onFocusyonPointerDownen 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. ElStrictModede 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
AbortSignalvinculado 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
fetchse 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 ventana de evidencia
Sección titulada «La ventana de evidencia»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(60000o300000), incluidas las evaluaciones enviadas conevent_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
expiresAtfunciona con cualquiera de las dos ventanas; solo su margen de captura necesita caber en la más corta.
Cómo se aplica la ventana:
getEventContextsolo puede devolver un token dentro de la ventana. Una vez terminada la ventana de la captura, devuelveUNAVAILABLEcon el motivoSERVER_UNAVAILABLE(oINCONCLUSIVE, si la captura solo se evaluó después de su ventana).- El
expiresAtdevuelto 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 agetEventContextsobre el mismo handle después deexpiresAtdevuelveUNAVAILABLEcon el motivoEVENT_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 con400 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 conevent_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 deexpiresAt.
Del navegador a la API
Sección titulada «Del navegador a la API»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
INCONCLUSIVEcontrust_score: null, nuncaCOMPLETED. Con contexto, esCOMPLETEDcuando 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_AUTOMATIONyBROWSER_SPOOFING, se evaluaron; en caso contrario, esINCONCLUSIVE. Si no se encuentra nada adverso, eltrust_scorees100, y la recomendación gestionada esMONITORen 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.
Límites de sesión
Sección titulada «Límites de sesió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 devuelveRATE_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 enclose(). 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()orenew(); nada se reabre en segundo plano. Los handles de la sesión anterior devuelvenCAPABILITY_EXPIRED(createEventygetEventContextsobre 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.
Tiempos y latencia
Sección titulada «Tiempos y latencia»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
getEventContexty nunca se reinician. Dentro de ellos, el SDK vuelve a preguntar 400 ms después de cada respuestaPROCESSING(o después de unRetry-Aftermás largo) y repite por sí solo una consulta con tiempo de espera agotado, perdida,429o503. Ninguna consulta empieza con menos de 1000 ms restantes. Cuando se agota el presupuesto, el evento conserva su última respuesta, que puede serPROCESSING. - 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 en producción (2026-10-15)
Sección titulada «Medido en producción (2026-10-15)»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.
Motivos de fallo
Sección titulada «Motivos de fallo»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 |
Etapas y motivos
Sección titulada «Etapas y motivos»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ó |
Cierre de sesión y cambio de cuenta
Sección titulada «Cierre de sesión y cambio de cuenta»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.
Versiones anteriores
Sección titulada «Versiones anteriores»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.
Si todavía usa el SDK 0.1.1
Sección titulada «Si todavía usa el SDK 0.1.1»Todo lo anterior se aplica, excepto el helper, con estas diferencias:
- Una captura por cliente. Un segundo
collect()después de uno aceptado devuelveUNAVAILABLEcon el motivoCONFLICT, y el cliente conserva su sesión. Para volver a capturar, hagaawait close()y llame ainitializeSdk()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()nicreateActionContextHelper(). El primercollect()abre la sesión. getEventContexttiene 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.
Qué captura el SDK
Sección titulada «Qué captura el SDK»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.
Soporte
Sección titulada «Soporte»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.