This guide shows you how to integrate the Truora Validations native SDKs (Android and iOS) into a Capacitor application using a local plugin.
It is a concrete, end-to-end implementation of the hybrid-frameworks bridge pattern. If you use Ionic or any Capacitor-based stack (Vue, React, or Angular), follow this guide directly. If you use a different hybrid framework, the Kotlin and Swift bridge code here is reusable almost verbatim — only the plugin-registration glue changes.
The integration has two layers:
Web layer (TypeScript): a Capacitor plugin definition registered via registerPlugin() that gives you a typed interface for calling native methods and listening for events.
Native layer (Kotlin / Swift): platform-specific plugin classes that receive calls from the web layer, configure and launch the Truora SDK, and emit results back through Capacitor’s event system.
Requirements
Platform Requirements
Capacitor: 7.0.0 or higher
Node.js: 20.19.0+ or 22.12.0+
Android: API Level 24 (Android 7.0) or higher, Gradle 8.12+, AGP 8.9+, Java 21
iOS: 14.0 or higher, CocoaPods 1.10+, Xcode 15+
The Truora Android SDK supports API 21+ and the iOS SDK supports iOS 13+, but Capacitor 7 itself raises the floor. The versions above are a known-working configuration.
Permissions Required
Camera access for document and face capture
Internet access for API communication
Prerequisites & Authentication
Both SDKs retrieve their API key through a key provider interface rather than taking it as a plain argument. This avoids hardcoding credentials and lets you fetch a temporary key at runtime. The key must be of type SDK (temporary) — any other key type will result in an error.
How you obtain the key is up to you (backend service, secure storage, or env config during development). In this integration the web layer passes the key into startValidation(), and the native plugin holds it in memory for the SDK to consume.
For the full authentication model — the two-key system (Generator Key + SDK Key), how to mint a temporary SDK key, and shared concepts like userId, language, and UI customization — see the Shared logic guide. Everything there applies identically to this Capacitor integration.
Edit android/app/build.gradle and add the Truora SDK artifacts and the Kotlin plugin. Use the latest published version (the current release is shown below — check the Android SDK guide for the newest).
Edit android/app/src/main/AndroidManifest.xml. Add the permissions and override the FileProvider declaration so it does not conflict with the one the Truora SDK bundles:
Create android/app/src/main/java/com/example/myapp/TruoraValidationsPlugin.kt (adjust the package to match your applicationId). This is the Android half of the bridge: it implements TruoraAPIKeyGetter, builds the validation, and emits results back to the web layer.
MainActivity requirement: the Capacitor BridgeActivity already extends AppCompatActivity, which the plugin’s load() relies on to create the ValidationHandler. No extra MainActivity change is needed beyond the default Capacitor setup.
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '14.0'
use_frameworks!
install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
end
target 'App' do
capacitor_pods
# Truora Validations SDK
pod 'TruoraCamera'
pod 'TruoraValidationsSDK'
end
post_install do |installer|
assertDeploymentTarget(installer)
end
<key>NSCameraUsageDescription</key>
<string>Camera access is required for identity verification</string>
5.3 Create the iOS plugin
Create ios/App/App/TruoraValidationsPlugin.swift. This is the iOS half of the bridge: it implements TruoraAPIKeyGetter, builds and presents the validation, and notifies the web layer of results.
# Build the web assets
npm run build
# Sync with iOS
npx cap sync ios
# Install pods
cd ios/App && pod install
# Open in Xcode
npx cap open ios
Then build and run on a physical device from Xcode. Face and document validation require a real camera; the iOS Simulator does not have one.
Configuration Options
For the shared concepts behind these options — accountId/userId, language, and UI customization — see the Shared logic guide. The tables below list what this Capacitor plugin accepts.
Validation types
Set validationType in the config to one of the following:
Type
Description
face
Captures and validates the user’s face via liveness detection
document
Captures and validates an identity document
document_with_face
Runs document validation first, then face validation using the document photo as reference
invoice
Captures and validates an invoice or utility bill
Validation config parameters
These are the fields accepted inside the config object:
Parameter
Type
Default
Description
validationType
string
“face”
The type of validation to perform
autocapture
boolean
true
Automatically capture when the face/document is detected
waitForResults
boolean
false
Wait for the API response before closing the view and showing the result to the user
language
string
null
UI language: “es”, “en”, or “pt”
country
string
null
Country code for document/invoice (e.g. “CO”, “MX”)
documentType
string
null
Document type (e.g. “nationalId”, “passport”)
UI customization parameters
All fields are optional; anything you omit falls back to the default Truora styling:
Parameter
Type
Description
primaryColor
string
Primary brand color (hex, e.g. “#435AE0”)
onPrimaryColor
string
Text/icon color on primary surfaces
secondaryColor
string
Secondary brand color
onSecondaryColor
string
Text/icon color on secondary surfaces
surfaceColor
string
Background color of surfaces
onSurfaceColor
string
Text/icon color on surfaces
surfaceVariantColor
string
Variant background color
onSurfaceVariantColor
string
Text/icon color on variant surfaces
errorColor
string
Color for error states
logoUrl
string
URL to your brand logo image
Available countries and document types
Use the country Code for the country field, and one of the listed values for documentType:
The plugin communicates results through Capacitor events. Listen for:
validationResults — emitted when a validation completes, is canceled, or errors. The results field is a JSON string containing an array of result objects.
loadingStateChanged — emitted with { loading: false } when the validation flow finishes.
TruoraValidations.addListener('validationResults', (event) => {
const results = JSON.parse(event.results)
for (const item of results) {
switch (item.result) {
case 'completed':
console.log('Validation ID:', item.validationId)
console.log('Status:', item.validationStatus)
if (item.validationStatus === 'success') {
// User passed validation
} else if (item.validationStatus === 'failure') {
// User did not pass
} else {
// Status is "pending" — still processing
}
break
case 'canceled':
console.log('User canceled the validation')
break
case 'error':
console.error('Validation error:', item.validationStatus)
break
}
}
})
Important notes & constraints
One validation per builder
Both SDKs enforce a rule: only one validation type per builder instance. Calling withValidation twice on the same builder throws an error. The document_with_face flow is handled by running two sequential validations — document first, then face — each with its own builder. The plugin code in this guide already does this correctly.
Physical device required
Face and document validations require a real camera and will not work on emulators or simulators. This is intentional: our injection-attack security flags emulated environments.
API key security
Never hardcode your API key in client-side code for production. Use a temporary SDK-type key fetched from your backend at runtime. The plugin receives the key from your web layer and passes it to the native SDK. See Shared logic for the full key model.
Troubleshooting
Common issues and how to resolve them:
Issue
Solution
Camera not working on Android
Verify the CAMERA permission is in AndroidManifest.xml. The SDK handles the runtime permission prompt itself.
Build fails on iOS with missing modules
Run cd ios/App && pod install and ensure the deployment target is 14.0+ in both the Podfile and the Xcode project.
“API Key Invalid” error
Verify the key is correct, of type sdk (temporary), and not expired.
“UNIMPLEMENTED” error on iOS
The plugin is not registered. Confirm ViewController.swift calls bridge?.registerPluginInstance(TruoraValidationsPlugin()) and that Main.storyboard uses customClass="ViewController" / customModule="App".
“ValidationHandler not initialized” on Android
MainActivity must extend BridgeActivity (which extends AppCompatActivity); the plugin’s load() casts the activity to AppCompatActivity to create the handler.
“Cannot configure multiple validation types” on Android
Caused by calling withValidation() twice on one builder. Use the document_with_face pattern from this guide (separate sequential builders).
FileProvider conflict on Android
The Truora SDK bundles its own FileProvider. Add tools:replace="android:authorities" and tools:replace="android:resource" to your manifest’s <provider> / <meta-data> as shown in step 4.3.