The Truora Validations SDK allows you to integrate identity verification features directly into your native mobile applications. The SDK handles the complexity of capturing the user’s identity documents, facial recognition, and liveness detection to verify their identity against backend records.
Requirements
Platform Requirements
Android: 5.1 (API Level 21) or higher
The SDK must be launched from an AppCompatActivity
Permissions Required
Camera access (for document and face capture)
Internet access (for API communication)
Prerequisites & Authentication
The Key Provider Interface
To prevent hardcoding sensitive logic, you must implement the TruoraAPIKeyGetter interface in your code. This allows you to retrieve the API key from a secure location (like a compiled secret, a secure storage enclave, or an obfuscated string). This interface is how the sdk will get access to the api key for the validations, the api key must be of type sdk (temporary), any other key type will result in an error. Below we show three examples of how to implement the interface, but it can work however you want
import com.truora.interfaces.TruoraAPIKeyGetter;
// You would need to import necessary Android classes like Context and relevant security classes.
public class EncryptedStorageApiKeyProvider implements TruoraAPIKeyGetter {
// NOTE: In a real app, 'context' would be used to access EncryptedSharedPreferences.
// The key must be retrieved and cached synchronously or fetched asynchronously before this method is called.
private final String cachedApiKey;
public EncryptedStorageApiKeyProvider(String preFetchedKey) {
// Assuming the key is pre-fetched asynchronously and passed to the constructor.
this.cachedApiKey = preFetchedKey;
}
@Override
public String getApiKeyFromSecureStorage() {
// Check if the key was successfully retrieved and cached
if (cachedApiKey == null || cachedApiKey.isEmpty()) {
throw new RuntimeException("API key not found in secure storage or retrieval failed.");
}
return this.cachedApiKey;
}
}
import com.truora.interfaces.TruoraAPIKeyGetter
import android.content.Context
// You would need androidx.security.crypto for EncryptedSharedPreferences
class EncryptedStorageApiKeyProvider(private val context: Context, private val preFetchedKey: String?) : TruoraAPIKeyGetter {
// NOTE: For synchronous retrieval, this class assumes the API key was already
// fetched asynchronously and passed in the constructor, or it uses a blocking method.
override fun getApiKeyFromSecureStorage(): String {
// Use Kotlin's safety features to check for null/empty
return preFetchedKey.takeIf { !it.isNullOrEmpty() }
?: throw RuntimeException("API key not found in secure storage or retrieval failed.")
}
// Example conceptual function for synchronous secure retrieval if necessary
// private fun retrieveKeyFromStorageSync(context: Context, key: String): String? { /* ... */ }
}
Option 2: BuildConfig value
In this option we get the key from a generated class defined in your module’s build.gradle file.
import com.truora.interfaces.TruoraAPIKeyGetter;
/**
* Retrieves the API key directly from the BuildConfig file.
*/
public class BuildConfigApiKeyProvider implements TruoraAPIKeyGetter {
@Override
public String getApiKeyFromSecureStorage() {
// Option 2: From environment config (less secure, for development)
String apiKey = BuildConfig.TRUORA_API_KEY;
if (apiKey == null || apiKey.isEmpty()) {
// Throw an error or return an empty string if the key is missing/null
return "";
}
return apiKey;
}
}
import com.truora.interfaces.TruoraAPIKeyGetter
// Assuming BuildConfig.TRUORA_API_KEY is available
/**
* Retrieves the API key directly from the BuildConfig file.
*/
class BuildConfigApiKeyProvider : TruoraAPIKeyGetter {
override fun getApiKeyFromSecureStorage(): String {
// Option 2: From environment config (less secure, for development)
// Uses Kotlin's 'takeIf' and Elvis operator (?:) for a concise null/empty check
return BuildConfig.TRUORA_API_KEY.takeIf { !it.isNullOrEmpty() } ?: ""
}
}
Option 3: Retrieve from a backend
Recommended for production builds these are examples for backend retrieval:
import com.truora.interfaces.TruoraAPIKeyGetter;
public class BackendServiceApiKeyProvider implements TruoraAPIKeyGetter {
// Key must be declared volatile to ensure visibility across threads (network fetch vs. SDK call).
private volatile String cachedApiKey = null;
/**
* Conceptual method: Key should be fetched in the Application class or an initialization phase
* before the SDK is instantiated.
*/
public void fetchApiKeyAsynchronously(String userToken) {
// Implementation detail: Use a library like Retrofit or OkHttp to call your API.
// ... (Network call logic on a background thread) ...
// On successful response:
// this.cachedApiKey = fetchedKey;
}
@Override
public String getApiKeyFromSecureStorage() {
// Option 3: Return the cached key.
if (cachedApiKey == null || cachedApiKey.isEmpty()) {
// Indicates failure to pre-fetch the key, halting SDK initialization.
throw new RuntimeException("API key has not been retrieved from backend service or fetch failed.");
}
return cachedApiKey;
}
}
import com.truora.interfaces.TruoraAPIKeyGetter
class BackendServiceApiKeyProvider : TruoraAPIKeyGetter {
// Key must be declared volatile to ensure visibility across threads/Coroutines.
@Volatile private var cachedApiKey: String? = null
/**
* Conceptual method: This function would use Coroutines to safely fetch the key
* in a background thread and update 'cachedApiKey'.
*/
fun fetchApiKeyAsynchronously(userToken: String) {
// Implementation detail: Use Retrofit/Ktor within a Coroutine scope.
// After successful fetch: cachedApiKey = fetchedKey
}
override fun getApiKeyFromSecureStorage(): String {
// Option 3: Return the key that was retrieved and cached asynchronously beforehand
// Uses the Elvis operator to throw a RuntimeException if the key is not ready
return cachedApiKey ?: throw RuntimeException("API key has not been retrieved from backend service.")
}
}
Installation
The Truora SDK is modular and published on Maven. You don’t need to include every module in your project, only the validation modules that you need.
For example, if you are just interested in the face validation module add the following to your app-level build.gradle.kts (or build.gradle):
Before implementing validations, let’s understand the available configuration options.
Using custom validation config and UI config is optional when building the validation:
If no uiConfig is provided then the validation view will show the standard Truora branding, logo and colors
If no validationConfig is provided then default values will be applied like:
No waiting for validation results before finishing the validation process for the user
autocapture of face/doc when it’s detected will be on
More indepth information on the validation configuration options, such as similarity threshold, can be found in the official validations api documentation
The userId parameter is crucial for initializing the Truora SDK. It provides a unique identifier for the user undergoing the validation process.
Purpose: The userId allows Truora to associate validation attempts and reference data (like previously captured reference faces) with a specific user in your system. It is mandatory for linking the validation results to your user base.
Best Practice: This ID should be an immutable, non-sensitive identifier from your database (e.g., a UUID or internal user ID) that uniquely identifies the person. Do not use sensitive PII like an email address or national ID number as the userId.
Language
The language parameter allows you to define the localization of the text and instructions displayed within the validation view.
Customization: By setting the language, you ensure a user-friendly experience by presenting instructions and feedback in the user’s preferred language.
Available Options: The SDK supports several languages. You should provide the language code as a two-letter string (e.g., “es” for Spanish, “en” for English, “pt” for Portuguese). If no language is provided, the SDK will typically default to English or attempt to use the device’s system language, though providing an explicit language is recommended.
// Initialize SDK with an AppCompatActivity context, most often is the same caller "this"
// Do this before the onResume of the lifecycle of the component
ValidationHandler validationHandler = ValidationHandler.create(this);
After initializing once the handler its time to configure the SDK
// Build SDK by passing an object with the Key Provider interface
TruoraSDK.Validations.Builder(this,"user_unique_id") //Unique user id
.withLanguage(TruoraLanguage.SPANISH) //Sets language to Spanish
.withUIConfig({ /* ... */ })
.withValidation({ /* ... */ })
.build(validationHandler);
// Initialize SDK with an AppCompatActivity context, most often is the same caller "this"
// Do this before the onResume of the lifecycle of the component
val validationHandler = ValidationHandler.create(this)
After initializing once the handler its time to configure the SDK
// Build SDK by passing an object with the Key Provider interface
TruoraSDK.Validations.Builder(this,"user_unique_id") // Unique user id
.withLanguage(TruoraLanguage.SPANISH) //Sets language to Spanish
.withUIConfig{ /* ... */ }
.withValidation{ /* ... */ }
.build(validationHandler)
Face configuration
To create a face validation you just need to add it when building the validation object withValidation, you can just return the validation config inside the lambda function if you do not want to use any custom configuration
To specify that the validation type you want to use is Face, then explicitly type the lambda function as accepting a Face config validation type
Java Example
This is only the face configuration example in Java, the entire integration example is in the following section.
Automatically capture when face is properly detected
similarityThreshold
double
0.8
Minimum similarity score required (0.0 - 1.0)
timeout
int
60
Maximum time in seconds for the validation
waitForResults
bool
false
Wait for API response before closing the view
referenceFace
ReferenceFace
null
Optional reference image for comparison
Document configuration
To create a face validation you just need to added it when building the validation object withValidation, you can just return the validation config inside the lambda function if you do not want to use any custom configuration
To specify that the validation type you want to use is Document, then explicitly type the lambda function as accepting a Document config validation type
Java Example
This is only the document configuration example in Java, the entire integration example is in the following section.
// 1. Configure using Lambdas
val builder = TruoraSDK.Validations.Builder(this, "user_unique_id")
.withValidation { config ->
config
.useAutocapture(true)
.setTimeout(120) // 2 minutes timeout
.waitForResults(false)
.setCountry(Country.CO) // Country code (e.g., co, mx, pe)
.setDocumentType(DocumentType.NATIONAL_ID) // Type of document
.build()
}
// 2. Bind
builder.build(validationHandler)
Configuration Parameters:
Parameter
Type
Default
Description
country
Country
null
Country code for document validation (if not set, user selects)
documentType
DocumentType
null
Type of document (if not set, user selects)
useAutocapture
bool
true
Automatically capture when document is detected. Not supported for passport — the SDK will throw a TruoraException if autocapture is enabled with DocumentType.PASSPORT
data class ValidationResult(
val id: String?, // Unique ID for this validation
val status: String?, // Status of the validation
val type: String?, // Type of the validation
val detail: ValidationDetailResponse?
)
Validation Statuses
Status
Description
When it occurs
ValidationStatus.success
Validation passed
User successfully passed the validation
ValidationStatus.failure
Validation failed
User did not pass the validation criteria
ValidationStatus.pending
Awaiting processing
Validation is being process
TruoraValidationError Types
When the SDK returns failed, the error can be one of three types:
Exception Type
Description
TruoraValidationError.SDK
Internal SDK error (configuration, permissions, user actions)
TruoraValidationError.ValidationApi
Error from the Truora Validation API
TruoraValidationError.Network
Network connectivity error
Common SDK Error Types (SDKErrorType):
Error Type
Code
Description
cameraPermissionError
20011
Camera permission was denied
invalidApiKey
20017
API key is invalid or expired
invalidConfiguration
20024
SDK configuration is invalid
networkError
20025
Network connection failed
uploadFailed
20026
Failed to upload captured media
Handling Example Java
This is only the results handling example in Java, the entire integration example is in the following section.
private fun startFaceValidation() {
isValidating = true
statusText.text = "Starting document validation..."
startButton.isEnabled = false
progressBar.visibility = View.VISIBLE
try {
// Building the SDK configuration using chained calls and the 'apply' scope function
TruoraSDK.Validations
.Builder(this, "android-test-user")
.apply {
// Optional: Customize UI. Using trailing lambda syntax.
withUIConfig { uiBuilder ->
uiBuilder.setPrimaryColor("#435AE0")
uiBuilder.setSurfaceColor("#FFFFFF")
uiBuilder.setErrorColor("#FF5454")
uiBuilder.setLogo("https://your-cdn.com/logo.png")
uiBuilder.build()
}
// Configure doc validation. Using trailing lambda syntax.
withValidation { config ->
config.useAutocapture(true)
.waitForResults(true)
config.build()
}
}
.build(validationHandler)
// Using a Kotlin method reference (::) for the callback
validationHandler.start(::handleValidationResult)
} catch (e: TruoraException) {
Log.e(TAG, "Failed to initialize SDK", e)
statusText.text = "Initialization failed: ${e.message}"
resetUI()
} catch (e: IllegalArgumentException) {
// Combined catch block for multiple exceptions
Log.e(TAG, "Failed to initialize SDK", e)
statusText.text = "Initialization failed: ${e.message}"
resetUI()
}
}
Troubleshooting
Common Issues
Camera not working on Android
Solution: Verify that camera permissions are added to AndroidManifest.xml and that the user has granted permission at runtime.
“API Key Invalid” error
Solution: Verify your API key is correct and has the proper grants (generator or sdk type). Check that it hasn’t expired.
Validation timeout
Solution: Increase the timeout value in the config, or ensure the device has a stable internet connection.