Flash Call Verification Sdk
What is Flash Call Verification?
Flash Call Verification is a seamless, cost-effective, and highly secure alternative to traditional SMS OTPs (One-Time Passwords). Instead of sending an SMS text message containing a code that the user must wait for and manually type, the Kasookoo Flash Call system automatically verifies a user's mobile phone number by placing a brief, missed call (a "flash call") to the user's device.
Key Benefits
- Zero Friction: Users do not need to switch apps, read text messages, or manually type 6-digit codes.
- High Speed & Conversion: Verification completes within seconds as soon as the test call reaches the device.
- Cost Efficiency: Reduces delivery costs compared to international SMS OTP gateway rates.
- Enhanced Security: Eliminates risks associated with SMS interception, SIM swapping, and human input errors.
How Flash Call Verification Works (Detailed Workflow)
[ User Device ] [ Partner App / SDK ] [ Kasookoo Platform ]
| | |
|--- 1. Enters Phone Number ------>| |
| |--- 2. Request Flash Call Session -->|
| |<-- 3. Returns Session ID & Target --|
| | |
|<== 4. Incoming Missed Call ============================================|
| (Detects Calling Line ID) | |
| | |
| |--- 5. Match Found & Verify CLI ---->|
| |<-- 6. Verification Confirmed ------|
Step-by-Step Flow:
-
User Initiates Verification:
The user enters their mobile phone number into the mobile application and taps the "Verify" button. -
Session Creation:
The application communicates with the Kasookoo backend platform to request a new verification session for the given phone number. -
Target Assignment:
The platform creates a session and returns details including a unique session identifier and the designated target caller number (CLI) from which the verification call will originate. -
Outbound Test Call:
The Kasookoo platform places a short test call to the user's mobile number. The call rings briefly and is automatically disconnected before the user can answer, ensuring no charges are incurred by the user. -
Automated Call Detection:
The embedded SDK monitors incoming calls on the user's device in real-time. When the call arrives, the SDK automatically reads the caller ID without answering the call. -
Automatic Number Matching & Confirmation:
The SDK compares the incoming caller ID with the target caller ID assigned to the session. Upon a successful match, the SDK confirms the verification with the platform, completing the phone number verification instantly.
SDK Integration Guide
This guide provides step-by-step instructions on how to integrate and use the sdk-release.aar in your Android project.
Step 1: Add Permissions to Manifest
The SDK requires telephony permissions to detect incoming flash calls and verify the phone number. Add the following permissions to your AndroidManifest.xml inside the <manifest> tag:
<!-- Required for Internet access -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Required for Flash Call Verification -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />Step 2: Import the .aar File
- Locate your
sdk-release.aarfile. - Copy the file into your app module's
libsdirectory (e.g.,app/libs/). - If the
libsfolder doesn't exist, create it.
Step 3: Configure build.gradle
Open your app-level build.gradle (or build.gradle.kts) and add the AAR as a dependency.
Using Kotlin DSL (build.gradle.kts):
build.gradle.kts):android {
// ... other config
buildFeatures {
viewBinding = true
}
}
dependencies {
// Import the local AAR file
implementation(files("libs/sdk-release.aar"))
// Required to prevent OkHttp version mismatch crashes
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.12.0"))
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")
// The SDK requires these transitively (ensure they are present)
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.10.0")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.10.0")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
}Step 5: Create the Verification Activity & VoLTE Network Settings
Create a new Activity (e.g., FlashCallActivity) to host the verification UI. Below is the standard implementation using the SDK's FlashCallManager.
Why & How to Use openNetworkSettings()
openNetworkSettings()Why to Use openNetworkSettings():
openNetworkSettings():- VoLTE & Mobile Data Requirement: Flash Call verification requires an active internet connection while detecting incoming calls. If VoLTE is disabled, the device drops to a 2G/3G voice network during incoming calls, which disconnects 4G/5G mobile data and causes verification to fail or time out.
- Seamless Navigation: Calling
flashCallManager.openNetworkSettings()takes the user directly to their device's Mobile Network Settings so they can enable VoLTE / Mobile Data.
Why VoLTE is Required: Flash Call verification requires an active internet connection while detecting incoming calls. Enabling VoLTE (Voice over LTE) prevents your 4G/5G mobile data from disconnecting during the test call, ensuring fast and seamless verification.
How to Use openNetworkSettings():
openNetworkSettings():Attach flashCallManager.openNetworkSettings() to a "Network Settings" or "Enable VoLTE" button in your Activity:
binding.networkSettingsButton.setOnClickListener {
flashCallManager.openNetworkSettings()
}Or call it directly via KasookooSdkHelper:
KasookooSdkHelper.openNetworkSettings(context)FlashCallActivity.kt
package com.your.package
import android.os.Bundle
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.app.sdk.flashcall.*
import com.app.sdk.databinding.ActivityFlashCallBinding // Note: Uses SDK's ViewBinding
class FlashCallActivity : AppCompatActivity() {
private lateinit var binding: ActivityFlashCallBinding
private lateinit var flashCallManager: FlashCallManager
/**
* REQUIRED: Replace with your actual credentials.
* Throws an error if left empty.
*/
private val PUBLISHABLE_KEY = "your_key_here" // Your unique key from the Kasookoo dashboard
private val viewModel: FlashCallViewModel by viewModels {
FlashCallViewModelFactory(FlashCallRepository(this))
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Ensure credentials are provided
if (PUBLISHABLE_KEY == "your_key_here" || PUBLISHABLE_KEY.isBlank()) {
throw RuntimeException("SDK Error: Please provide a valid PUBLISHABLE_KEY in FlashCallActivity")
}
binding = ActivityFlashCallBinding.inflate(layoutInflater)
setContentView(binding.root)
// 1. Initialize the Manager
flashCallManager = FlashCallManager(this, viewModel)
// 2. Observe state changes
observeUiState()
// 3. Setup click listeners
setupClickListeners()
// 4. Start permission checks and initialization
flashCallManager.initialize()
}
private fun observeUiState() {
viewModel.uiState.observe(this) { state ->
updateUi(state)
}
}
private fun updateUi(state: FlashCallUiState) {
binding.statusTextView.text = state.statusText
binding.footerTerms.text = state.logs
// Manage layout transitions based on SDK state
if (state.isVerificationStarted) {
binding.phoneInputLayout.visibility = View.GONE
binding.phoneLabel.visibility = View.GONE
binding.verifyButton.visibility = View.GONE
binding.titleTextView.visibility = View.GONE
binding.subtitleTextView.visibility = View.GONE
binding.statusLayout.visibility = View.VISIBLE
}
if (state.isSuccess) {
binding.statusLayout.visibility = View.GONE
binding.successLayout.visibility = View.VISIBLE
// Access session details
val sessionId = state.sessionId
val msisdn = state.msisdn
if (sessionId != null && msisdn != null) {
Log.e( "Session_id","SID: $sessionId, msisdn: $msisdn")
}
}
if (state.isTimeout) {
resetUi()
Toast.makeText(this, state.errorMessage ?: "Verification timed out", Toast.LENGTH_LONG).show()
}
state.errorMessage?.let {
if (!state.isTimeout) {
Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
}
}
}
private fun resetUi() {
binding.phoneInputLayout.visibility = View.VISIBLE
binding.phoneLabel.visibility = View.VISIBLE
binding.verifyButton.visibility = View.VISIBLE
binding.titleTextView.visibility = View.VISIBLE
binding.subtitleTextView.visibility = View.VISIBLE
binding.statusLayout.visibility = View.GONE
binding.successLayout.visibility = View.GONE
viewModel.resetState()
}
private fun setupClickListeners() {
binding.verifyButton.setOnClickListener {
val phone = binding.etPhone.text.toString().trim()
if (phone.isEmpty()) {
binding.etPhone.error = "Phone number required"
return@setOnClickListener
}
hideKeyboard()
viewModel.onVerifyClicked(
phone = phone,
key = PUBLISHABLE_KEY
)
}
binding.showLogs.setOnClickListener {
val isVisible = binding.footerTerms.isVisible
binding.footerTerms.visibility = if (isVisible) View.GONE else View.VISIBLE
binding.showLogs.text = if (isVisible) "Show Logs" else "Hide Logs"
}
// Open System Network Settings to allow user to enable VoLTE / Mobile Data
binding.networkSettingsButton.setOnClickListener {
flashCallManager.openNetworkSettings()
}
}
private fun hideKeyboard() {
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(currentFocus?.windowToken, 0)
}
// Required: Handle permission results for the SDK
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (!flashCallManager.handlePermResult(requestCode, grantResults)) {
Toast.makeText(this, "Permissions required for flash verification", Toast.LENGTH_LONG).show()
}
}
override fun onDestroy() {
super.onDestroy()
// Required: Cleanup listeners
flashCallManager.destroy()
}
}Step 6: SDK Lifecycle Summary
To properly run the SDK, ensure you:
- Call
flashCallManager.initialize(): This checks for runtime permissions and registers the telephony listeners. - Override
onRequestPermissionsResult: Delegate the result toflashCallManager.handlePermResult. - Call
flashCallManager.destroy(): Always call this inonDestroy()to avoid memory leaks and prevent background listeners from running. - Observe
viewModel.uiState: This is how you receive feedback about the verification progress (Loading, Success, Timeout, or Error).- On Success: When
state.isSuccessis true, you can also retrievestate.sessionIdandstate.msisdnfor your backend records. - On Timeout: When
state.isTimeoutis true, the SDK has reached the maximum wait time for a call. You should callviewModel.resetState()(as shown inresetUi) to allow the user to try again.
- On Success: When
Step 7: Handling Flow Failures & Error Scenarios
When integrating the Flash Call SDK, various failure scenarios may occur during the verification lifecycle. Below is a guide on what happens during each failure and the recommended action to take in your app:
1. Missing or Denied Telephony Permissions
- What Happens:
flashCallManager.handlePermResult()returnsfalse, preventing the SDK from listening to incoming call states. - What to Do: Prompt the user explaining why
READ_PHONE_STATE,READ_CALL_LOG, andREAD_PHONE_NUMBERSpermissions are required. If permanently denied, direct the user to App Settings to grant permissions manually.
2. Session Initialization Failure (Invalid Key or Network Error)
- What Happens:
KasookooSdkHelper.startFlashCallFeaturetriggersonError. Thestate.errorMessageis updated inviewModel.uiState. - What to Do:
- Check that a valid
PUBLISHABLE_KEYis configured in your Activity/ViewModel. - Verify device internet connectivity.
- Call
viewModel.resetState()and allow the user to try again.
- Check that a valid
3. Outbound Trigger Failure
- What Happens:
KasookooSdkHelper.triggerFlashCallfails (e.g., invalid phone number format, carrier rejection). - What to Do:
- Ensure the user enters a valid phone number with proper country code format.
- Display the error message from
state.errorMessage. - Allow the user to edit their phone number and retry.
4. Call Detection Timeout (state.isTimeout == true)
state.isTimeout == true)- What Happens: The SDK waits for an incoming flash call up to the configured timeout period (default 60 seconds). If no matching call is detected,
state.isTimeoutbecomestrue. - What to Do:
- Invoke
resetUi()andviewModel.resetState()to return the UI to its initial input state. - Show a user-friendly message ("Verification timed out").
- Allow the user to retry or offer an alternative fallback method (e.g., SMS OTP).
- Invoke
5. Verification API Failure
- What Happens:
KasookooSdkHelper.verifyFlashCallfails during the final verification call. - What to Do: The SDK automatically attempts to refresh expired tokens. If verification fails repeatedly, call
viewModel.resetState()and prompt the user to start a fresh attempt.
6. Resetting the State
In any failure scenario where you want to allow the user to start over, always invoke:
viewModel.resetState()This cancels active timeout jobs, resets session data, clears target numbers, and prepares the ViewModel for a new verification attempt.
Step 8: OpenTelemetry & Grafana Telemetry Tracking
The SDK features built-in structured logging and tracing powered by OpenTelemetry (OTel). All key lifecycle events, API request/response payloads, and call detection states are exported asynchronously to Grafana (Loki / Tempo).
1. How Telemetry is Implemented in the SDK
- Initialization:
KasookooSdkHelper.initialize(context, config)sets up theOpenTelemetrySdkwith an OTLP HTTP Span Exporter (/v1/traces) and an OTLP HTTP Log Exporter (/v1/logs). - Endpoints:
- Debug/Test Environment (
isDebug = true):https://monitoring-test.kasookoo.ai - Production Environment (
isDebug = false):https://monitoring.kasookoo.ai
- Debug/Test Environment (
- Correlation Header &
call_trace_id:- Every API request automatically includes
X-Call-Trace-IDandX-Request-IDheaders via OkHttp interceptor (TelemetryInterceptor). - When the outbound call is triggered (
/api/v1/silent-auth-call/sessions/flashcall/start), the returnedsession_idis automatically set as thecall_trace_id. - All subsequent telemetry logs and API requests carry this exact
call_trace_idfor end-to-end trace correlation in Grafana.
- Every API request automatically includes
2. Log Events Pipeline
Below are the standard structured log events emitted during a Flash Call lifecycle:
| Event Name | Emitted When | Key Payload Attributes |
|---|---|---|
SDK_INITIALIZED | SDK is initialized via KasookooSdkHelper.initialize() | user_id, service.name |
FLASH_CALL_SESSION_INITIATED | startFlashCallFeature() is invoked | user_id, feature_type |
FLASH_CALL_SESSION_CREATED | Client session API succeeds | session_id, api_url, api_response (token redacted) |
FLASH_CALL_TRIGGER_INITIATED | triggerFlashCall() is invoked | user_id, feature_type |
FLASH_CALL_TRIGGERED | Outbound trigger API succeeds | call_trace_id, api_url, api_response |
FLASH_CALL_RECEIVED | Polling detects incoming ringing call (CALL_STATE_RINGING) | call_trace_id, call_state |
FLASH_CALL_VERIFIED | Quick verify API succeeds | call_trace_id, api_url, api_response |
FLASH_CALL_FAILED / FLASH_CALL_TRIGGER_FAILED | Any step encounters an error | error, level: "ERROR" |
3. JSON Log Format
All log records exported to Grafana Loki are formatted in structured JSON string bodies:
{
"event": "FLASH_CALL_TRIGGERED",
"level": "INFO",
"call_trace_id": "sess_8f3a92b1",
"user_id": "user_12345",
"feature_type": "flash_call",
"api_url": "https://sdk.kasookoo.ai/api/v1/silent-auth-call/sessions/flashcall/start",
"api_response": "{\"session_id\":\"sess_8f3a92b1\",\"msisdn\":\"+923001234567\",\"fixed_cli\":\"+18005550199\",\"verification_wait_timeout_seconds\":60}"
}4. Tracking Logs on the Grafana Web Dashboard
You can track and query your SDK telemetry on Grafana using Loki's Explore view or custom dashboards:
Step 1: Open Grafana Explore View
Navigate to your Grafana web console (e.g. https://monitoring.kasookoo.ai or https://monitoring-test.kasookoo.ai) and select the Loki data source under Explore.
Step 2: Querying Logs with LogQL
Use LogQL queries to filter and parse logs in JSON format:
-
Filter all logs for a specific service:
{service_name="kasookoo-android"} | json -
Track a specific verification session using
call_trace_id:{service_name="kasookoo-android"} | json | call_trace_id = "sess_8f3a92b1" -
Filter by specific event types:
{service_name="kasookoo-android"} | json | event = "FLASH_CALL_RECEIVED" -
Filter errors only:
{service_name="kasookoo-android"} | json | level = "ERROR"
Step 3: Auto-Extracting Fields
By adding | json to your LogQL query, Grafana Loki automatically parses every JSON field (event, call_trace_id, api_url, api_response, user_id, etc.) into selectable panel fields, allowing you to create dashboards, alert rules, and duration metrics.
Updated 7 days ago
