Examples
Example - With Navigation Tracking
Complete multi-step KYC flow example
Scenario
Multi-step KYC flow with navigation tracking:
- Phone entry
- OTP verification
- PAN entry
- Document upload
Architecture
App (Jetpack Navigation)
↓
NavGraph with KYC steps
↓
Each screen emits context
↓
SDK tracks progress
↓
Backend evaluates triggers
↓
Assistant offers help when neededImplementation
1. Application Setup
class KycApplication : Application() {
override fun onCreate() {
super.onCreate()
// Register schemas before init
AI.registerScreenSchemas(listOf(
ScreenSchema(
screenId = "phone_entry",
title = "Enter Phone Number",
components = listOf(
ComponentSpec(
id = "phone_field",
type = "text_input",
label = "Phone Number",
validationRules = listOf(
ValidationRule(
ruleId = "phone_format",
pattern = "^[6-9]\\d{9}$",
errorMessage = "Invalid phone number"
)
)
)
)
),
ScreenSchema(
screenId = "otp_verify",
title = "Verify OTP",
components = listOf(
ComponentSpec(
id = "otp_field",
type = "text_input",
label = "6-digit OTP",
validationRules = listOf(
ValidationRule(
ruleId = "otp_format",
pattern = "^\\d{6}$",
errorMessage = "Invalid OTP"
)
)
)
)
),
ScreenSchema(
screenId = "pan_entry",
title = "Enter PAN Details",
components = listOf(
ComponentSpec(
id = "pan_field",
type = "text_input",
label = "PAN Number",
validationRules = listOf(
ValidationRule(
ruleId = "pan_format",
pattern = "^[A-Z]{5}[0-9]{4}[A-Z]$",
errorMessage = "Invalid PAN format"
)
)
)
)
)
))
// Initialize SDK
AI.init(
application = this,
apiKey = "your-api-key",
userId = "runtime-user-id",
policy = RuntimePolicy(
backendBaseUrl = "https://kycis.zynnex.in/v1",
clientId = "kyc-app-v1",
mappingVersion = "v1",
triggerStartMode = TriggerStartMode.CONFIRM_UI,
passiveEvalEnabled = true,
passiveEvalIntervalSeconds = 10
)
)
AI.attach(this)
}
}2. Navigation Graph
@Composable
fun KycNavGraph() {
val navController = rememberNavController()
NavHost(navController, startDestination = "phone_entry") {
composable("phone_entry") {
LaunchedEffect(Unit) { AI.setKycStep("phone_entry") }
PhoneEntryScreen(
onPhoneSubmitted = { navController.navigate("otp_verify") }
)
}
composable("otp_verify") {
LaunchedEffect(Unit) { AI.setKycStep("otp_verify") }
OtpVerifyScreen(
onOtpVerified = { navController.navigate("pan_entry") }
)
}
composable("pan_entry") {
LaunchedEffect(Unit) { AI.setKycStep("pan_entry") }
PanEntryScreen(
onPanSubmitted = { navController.navigate("document_upload") }
)
}
composable("document_upload") {
LaunchedEffect(Unit) { AI.setKycStep("document_upload") }
DocumentUploadScreen()
}
}
}3. Screen Implementation
@Composable
fun PhoneEntryScreen(
onPhoneSubmitted: (String) -> Unit
) {
var phone by remember { mutableStateOf("") }
var error by remember { mutableStateOf<String?>(null) }
Column {
Text("Enter your phone number")
TextField(
value = phone,
onValueChange = {
phone = it
error = null
// Report redacted input
AI.reportComponentInput(
componentId = "phone_field",
hint = maskPhone(it),
masked = true
)
}
)
error?.let { Text(it, color = Color.Red) }
Button(onClick = {
if (isValidPhone(phone)) {
onPhoneSubmitted(phone)
} else {
error = "Invalid phone number"
AI.trackValidationFailure(
failureReasonCode = "phone_invalid",
componentId = "phone_field",
hint = maskPhone(phone),
masked = true
)
}
}) {
Text("Continue")
}
}
}
fun maskPhone(phone: String): String {
return if (phone.length >= 4) {
"${phone.take(2)}****${phone.takeLast(2)}"
} else phone
}4. Voice Integration
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AI.setVoiceSessionListener { result ->
if (result.isValid) {
// Show voice UI
startVoiceCall(result)
}
}
setContent {
Column {
// Help FAB
FloatingActionButton(
onClick = { AI.startAssistant() }
) {
Icon(Icons.Default.Call, "Get help")
}
// Your nav graph
KycNavGraph()
}
}
}
private fun startVoiceCall(result: VoiceSessionResult) {
// Connect to LiveKit with credentials
// Show call UI
}
}Event Sequence
Activity stream shows:
identity— user setscreen_schema— schemas registeredscreen_schema_lazy→screen_schema— phone entrycomponent_input— phone typingvalidation_failed— if invalidtrigger— if user strugglingscreen_schema— otp verifysession_start— if voice startedvoice_conversation_turn— conversationsession_stop+reengagement
Key Points
- Schemas registered before init
setKycStepcalled on every navigationcomponent_inputreports redacted values- Validation failures tracked immediately
- Voice available via FAB and triggers