Skip to main content
Este guia mostra a API pública do LivenessFacetecSDK e o fluxo completo de uma verificação de vivacidade.

API pública

A fachada do SDK fica em com.vcc.vendor.facetec.sdk.LivenessFacetecSDK e tem apenas três funções:
object LivenessFacetecSDK {
    suspend fun init(
        applicationContext: Context,
        config: LivenessFacetecSDKConfig
    ): Result<LivenessFacetecSDKInitData>

    fun startLivenessCheck(
        activity: Activity,
        sessionId: String,
        sessionToken: String,
        facetecSessionToken: String
    ): Flow<LivenessFacetecSDKLivenessState>

	fun stop()
}
  • init é idempotente — Chamadas subsequentes não reinicializam o FaceTec, mas sempre coletam o fingerprint novamente.
  • startLivenessCheck requer uma Activity chamadora porque o FaceTec abre sua própria UI a partir dela.
  • stop para a execução do SDK e libera recursos do sistema.

Configuração

data class LivenessFacetecSDKConfig(
    val customization: LivenessFacetecSDKCustomization? = null,
)
ParâmetroTipoObrigatórioDescrição
customizationLivenessFacetecSDKCustomization?NãoPersonalização da UI do FaceTec. Veja Customização

Campos de LivenessFacetecSDKInitData

init() retorna Result<LivenessFacetecSDKInitData> com os dados de device fingerprint coletados pelo FingerprintJS Pro:
data class LivenessFacetecSDKInitData(
    val visitorId: String?,  // Visitor ID do FingerprintJS Pro; null se falhar
    val requestId: String?   // Request ID do FingerprintJS Pro; null se falhar
)
CampoTipoDescrição
visitorIdString?Visitor ID gerado pelo FingerprintJS Pro. Encaminhe ao backend para criar a sessão.
requestIdString?Request ID da coleta do FingerprintJS Pro. Encaminhe ao backend junto com visitorId.
Ambos os campos são nullable. Trate o caso null — strings vazias são aceitas pelo endpoint de criação de sessão, mas o sinal de device fingerprint estará ausente.

Criação de sessão — responsabilidade do consumer app

O SDK não cria sessões nem se comunica diretamente com o backend de autenticação. Após init(), o consumer app deve:
  1. Encaminhar visitorId e requestId retornados por init() ao seu backend.
  2. O backend cria a sessão e devolve sessionId, sessionToken e facetecSessionToken.
  3. Passar os três tokens para startLivenessCheck().
O SDK não tem acesso às credenciais do backend do consumer nem conhece o contexto de negócio necessário para criar uma sessão válida. Manter a criação de sessão no consumer app garante flexibilidade (autenticação, multi-tenant) sem acoplar o SDK a uma topologia específica de backend.

Estados do Flow

startLivenessCheck retorna um Flow<LivenessFacetecSDKLivenessState> que emite um único caminho Loading → Success ou Loading → Error antes de completar:
sealed class LivenessFacetecSDKLivenessState {
    object Loading : LivenessFacetecSDKLivenessState()
    data class Success(val data: LivenessFacetecSDKResultData) : LivenessFacetecSDKLivenessState()
    data class Error(val error: LivenessFacetecSDKError) : LivenessFacetecSDKLivenessState()
}
O SDK suporta retry controlado pelo backend. Quando o backend retorna canRetry: true na resposta de uma captura, o SDK mantém a UI do FaceTec aberta e realiza uma nova tentativa automaticamente — sem nenhuma ação do consumer. O Flow não emite estados intermediários durante as tentativas; Success ou Error são emitidos apenas ao final da última tentativa. O número de tentativas é definido exclusivamente pelo backend.

Fluxo de uso

1

Inicializar o SDK

Chame LivenessFacetecSDK.init uma única vez (preferencialmente em uma Application ou na tela inicial):
import com.vcc.vendor.facetec.sdk.LivenessFacetecSDK
import com.vcc.vendor.facetec.sdk.models.LivenessFacetecSDKConfig

lifecycleScope.launch {
    LivenessFacetecSDK.init(
        applicationContext = applicationContext,
        config = LivenessFacetecSDKConfig()
    ).onSuccess { initData ->
        // Encaminhe initData.visitorId e initData.requestId ao backend
        // para criar a sessão antes de chamar startLivenessCheck
    }.onFailure { e ->
        Log.e("LivenessFacetecSDK", "Falha ao inicializar: ${e.message}")
    }
}
init() é idempotente — Chamadas subsequentes não reinicializam o FaceTec, mas sempre coletam o fingerprint novamente. É seguro chamá-lo em múltiplas Activities ou no onCreate() da Application.
2

Criar sessão no backend

Com visitorId e requestId em mãos, chame seu backend para obter os três tokens necessários para a captura:
val session = mySessionApi.createSession(
    body = SessionRequest(
        visitorId = initData.visitorId.orEmpty(),
        eventId   = initData.requestId.orEmpty()
    )
)
// session: { sessionId, sessionToken, facetecSessionToken }
3

Iniciar a verificação

startLivenessCheck() lança IllegalStateException diretamente (fora do Flow) se chamado antes de init() completar com sucesso. Use .catch { } no collector ou garanta que init() retornou Result.success(...) antes de habilitar o botão de captura.
import com.vcc.vendor.facetec.sdk.LivenessFacetecSDK
import com.vcc.vendor.facetec.sdk.LivenessFacetecSDKLivenessState
import com.vcc.vendor.facetec.sdk.models.LivenessFacetecSDKError
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch

lifecycleScope.launch {
    LivenessFacetecSDK.startLivenessCheck(
        activity            = this@MainActivity,
        sessionId           = session.sessionId,
        sessionToken        = session.sessionToken,
        facetecSessionToken = session.facetecSessionToken
    )
        .catch { e ->
            Log.e("LivenessFacetecSDK", "Erro inesperado: ${e.message}")
        }
        .collect { state ->
            when (state) {
                is LivenessFacetecSDKLivenessState.Loading -> mostrarProgresso()
                is LivenessFacetecSDKLivenessState.Success -> tratarSucesso(state.data)
                is LivenessFacetecSDKLivenessState.Error -> tratarErro(state.error)
            }
        }
}
4

Tratar o resultado

No caso de sucesso, use sessionId e captureId para reconciliar a captura no backend. Verifique livenessCheck == true para confirmar aprovação biométrica. No caso de erro, mapeie cada subclasse de LivenessFacetecSDKError para uma mensagem ou ação adequada — veja Solução de problemas.

Campos de LivenessFacetecSDKResultData

Quando a verificação termina com sucesso, o SDK emite Success(data: LivenessFacetecSDKResultData):
CampoTipoDescrição
sessionIdStringID da sessão criada no backend da Plataforma ID
captureIdStringID único da captura submetida
statusStringStatus final da captura
processedAtStringTimestamp ISO-8601 do processamento
resultString?Resultado do Liveness
scanResultBlobString?Blob bruto retornado pelo FaceTec
transactionIdString?ID da transação no provedor
retryScreenInt?Indicador interno de retry
replayCheckBooleanResultado da verificação anti-replay
sessionTokenCheckBooleanValidação do token de sessão
auditTrailCheckBooleanValidação da trilha de auditoria
livenessCheckBooleanResultado da verificação de vivacidade
Os quatro campos *Check são as garantias de segurança consolidadas pelo backend. Em uma captura legítima, todos devem ser true.
Success não implica necessariamente liveness aprovado. Verifique sempre livenessCheck == true para confirmar aprovação biométrica.

Exemplos de retorno

Sucesso — liveness aprovado

O fluxo terminou e a verificação biométrica passou. status vem "completed", result é "live" e todos os *Check são true.
LivenessFacetecSDKLivenessState.Success(
    data = LivenessFacetecSDKResultData(
        sessionId = "8f3a9c10-2b7d-4e1f-9c2a-5d6e7f8a9b01",
        captureId = "1c4e5f60-3a2b-4d8e-9f10-7b6c5d4e3f21",
        status = "completed",
        processedAt = "2026-04-28T14:32:18.421Z",
        result = "live",
        scanResultBlob = "...",
        transactionId = "txn_abc123def456",
        retryScreen = null,
        replayCheck = true,
        sessionTokenCheck = true,
        auditTrailCheck = true,
        livenessCheck = true
    )
)

Sucesso — liveness reprovado

O fluxo terminou tecnicamente, mas o resultado biométrico foi negativo. status vem "failed", result é "not_live" e livenessCheck é false. O backend pode também marcar auditTrailCheck como false quando a trilha de auditoria não atende aos requisitos.
LivenessFacetecSDKLivenessState.Success(
    data = LivenessFacetecSDKResultData(
        sessionId = "8f3a9c10-2b7d-4e1f-9c2a-5d6e7f8a9b01",
        captureId = "9d8c7b6a-5e4f-3d2c-1b0a-9f8e7d6c5b4a",
        status = "failed",
        processedAt = "2026-04-28T14:35:02.108Z",
        result = "not_live",
        scanResultBlob = "...",
        transactionId = "txn_xyz789ghi012",
        retryScreen = 2,
        replayCheck = true,
        sessionTokenCheck = true,
        auditTrailCheck = false,
        livenessCheck = false
    )
)
Esse caso não é erro técnico — o SDK funcionou corretamente, mas a prova de vida foi reprovada. Trate como negativa de verificação no seu app (mostrar mensagem ao usuário e permitir nova tentativa), sem reportar como falha do SDK.

Erro do fluxo

Quando algo no fluxo técnico falha, o SDK emite Error(error: LivenessFacetecSDKError). As subclasses possíveis cobrem rede, captura, submissão, cancelamento e exceções não mapeadas:
// Sem conectividade ou timeout HTTP
LivenessFacetecSDKLivenessState.Error(
    error = LivenessFacetecSDKError.NetworkError("Connection timed out")
)

// Falha durante a captura biométrica
LivenessFacetecSDKLivenessState.Error(
    error = LivenessFacetecSDKError.CaptureError("FaceTec session timed out")
)

// Falha ao submeter a captura para o backend
LivenessFacetecSDKLivenessState.Error(
    error = LivenessFacetecSDKError.SubmitError("HTTP 500")
)

// Usuário fechou a UI do FaceTec
LivenessFacetecSDKLivenessState.Error(
    error = LivenessFacetecSDKError.Cancelled()
)
A hierarquia completa de erros, com causa provável e ação recomendada para cada subclasse, está em Solução de problemas.

Exemplo completo

Esse exemplo é uma adaptação do app de demonstração que acompanha o SDK:
package com.example.myapp

import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.vcc.vendor.facetec.sdk.LivenessFacetecSDK
import com.vcc.vendor.facetec.sdk.LivenessFacetecSDKLivenessState
import com.vcc.vendor.facetec.sdk.models.LivenessFacetecSDKConfig
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val btnInit = findViewById<Button>(R.id.btn_init)
        val btnStart = findViewById<Button>(R.id.btn_start)
        val progress = findViewById<ProgressBar>(R.id.progress)
        val tvResult = findViewById<TextView>(R.id.tv_result)

        btnInit.setOnClickListener {
            btnStart.isEnabled = false
            tvResult.text = ""

            lifecycleScope.launch {
                progress.visibility = View.VISIBLE
                LivenessFacetecSDK.init(
                    applicationContext = applicationContext,
                    config = LivenessFacetecSDKConfig()
                ).onFailure { e ->
                    Log.e("MainActivity", e.message.orEmpty())
                    progress.visibility = View.GONE
                    tvResult.text = "Falha ao iniciar o SDK"
                }.onSuccess { initData ->
                    // Criar sessão no backend com visitorId e requestId
                    val session = mySessionApi.createSession(
                        body = SessionRequest(
                            visitorId = initData.visitorId.orEmpty(),
                            eventId   = initData.requestId.orEmpty()
                        )
                    )
                    progress.visibility = View.GONE
                    btnInit.isEnabled = false
                    btnStart.tag = session
                    btnStart.isEnabled = true
                    tvResult.text = "SDK pronto"
                }
            }
        }

        btnStart.setOnClickListener {
            val session = btnStart.tag as? SessionResponse ?: return@setOnClickListener
            btnStart.isEnabled = false
            tvResult.text = ""

            lifecycleScope.launch {
                LivenessFacetecSDK.startLivenessCheck(
                    activity            = this@MainActivity,
                    sessionId           = session.sessionId,
                    sessionToken        = session.sessionToken,
                    facetecSessionToken = session.facetecSessionToken
                )
                    .catch { e ->
                        Log.e("MainActivity", e.message.orEmpty())
                        tvResult.text = "Erro: ${e.message}"
                        btnStart.isEnabled = true
                        progress.visibility = View.GONE
                    }
                    .collect { state ->
                        when (state) {
                            is LivenessFacetecSDKLivenessState.Loading -> {
                                progress.visibility = View.VISIBLE
                                tvResult.text = "Processando…"
                            }
                            is LivenessFacetecSDKLivenessState.Success -> {
                                progress.visibility = View.GONE
                                tvResult.text = buildString {
                                    appendLine("Sucesso!")
                                    appendLine("Session: ${state.data.sessionId}")
                                    appendLine("Capture: ${state.data.captureId}")
                                    appendLine("Status: ${state.data.status}")
                                    appendLine("Liveness: ${state.data.livenessCheck}")
                                }
                                btnStart.isEnabled = true
                            }
                            is LivenessFacetecSDKLivenessState.Error -> {
                                Log.e("MainActivity", state.error.message)
                                progress.visibility = View.GONE
                                tvResult.text = "Erro: ${state.error.message}"
                                btnStart.isEnabled = true
                            }
                        }
                    }
            }
        }
    }
}

Parando o SDK

LivenessFacetecSDK.stop() libera os recursos internos do SDK e reseta seu estado. Uma chamada subsequente a init() se comporta como primeiro uso.
LivenessFacetecSDK.stop()
SituaçãoAção
Teardown da Activity (onDestroy)Chame stop() para liberar recursos
Entre capturasOpcional — init() é idempotente
Se stop() for chamado durante uma captura em andamento, ela é cancelada imediatamente e o Flow emite Error(Cancelled). Chamadas antes de init() ou duas vezes seguidas não causam crash.

Próximos passos

Customização

Personalize cores, textos e animações da UI do FaceTec

Solução de problemas

Hierarquia de erros e dicas de diagnóstico