Core Concepts
Runtime Provider Model
SDK runtime architecture and backend alignment
Provider Architecture
KYCIS uses a provider-like runtime boundary:
Host App
↓ (calls SDK APIs)
Android SDK (AI facade)
↓ (applies local policy + merged remote config)
Backend APIs (/v1/*)
↓ (evaluates triggers, returns decisions)
Voice Agent (LiveKit)Runtime Core (SdkRuntime.kt)
Single internal orchestrator holding:
RuntimeContext— session identity and user infoRuntimePolicy— local configurationPassiveTracker— interaction and timing signalsBackendClient— HTTP abstractionAndroidUiBridge— current Activity and UI overlays
Backend Client Abstraction
interface BackendClient {
fun setUser(userId: String, phone: String?)
fun trackError(code: String, properties: Map<String, String>?)
fun trackValidationFailure(...)
fun reportComponentInput(...)
fun evaluateTrigger(signals: TriggerSignals): TriggerDecision
fun startAssistant(...): VoiceSessionResult
fun stopAssistant(sessionId: String)
}Implementations:
HttpBackendClient— Production HTTP clientNoOpBackendClient— No network, for tests
Config Merging
Effective config is the merge of:
- SDK XML defaults (
kycis_config.xml) - Remote config from
GET /v1/sdk/config - In-memory snapshot via
AI.getConfig()
val cfg = AI.getConfig()
val enabled = cfg.isFeatureEnabled("some_feature")Data Flow
Event Ingestion
App Event
↓
SDK BackendClient
↓
POST /v1/events
↓
Backend Store (upsert_event)
↓
Activity Stream + Context BuildingTrigger Flow
Passive Timer / Error Event
↓
Evaluate Trigger
↓
POST /v1/assistant/trigger/evaluate
↓
Backend Decision (trigger, action, reason)
↓
SDK Applies TriggerStartMode
↓
Start Assistant OR Show Confirm UIVoice Session Flow
Start Decision
↓
POST /v1/assistant/session/start
↓
Backend: Create LiveKit Dispatch
↓
Return: token, livekit_url, livekit_room
↓
SDK: Connect to LiveKit
↓
Voice Agent Joins Room
↓
GET /v1/assistant/context/{session_id}Context Building
Voice agent fetches context from backend:
GET /v1/assistant/context/{session_id}
↓
Session State (screen, errors, validation history)
↓
KYC Knowledge Base (screen aliases, rules)
↓
Component Input Hints (redacted)
↓
System Prompt for LLMSession State
Backend maintains in-memory session state:
| Field | Source |
|---|---|
session_id | SDK generated UUID |
user_id | From identity event |
screen | From screen_state / setKycStep |
latest_event | Last event payload |
active | Assistant session flag |
last_validation_failure | From validation_failed |
Threading Model
- SDK operations are async (callbacks/listeners)
- Passive evaluation runs on background thread
- UI callbacks dispatched to main thread
- HTTP calls use internal thread pool
Error Handling
SDK provides structured error codes:
enum class SdkStatusCode {
NOT_INITIALIZED,
INITIALIZED,
REDUCED_TRACKING_MODE,
PERMISSION_DENIED,
NETWORK_UNAVAILABLE,
VOICE_SESSION_CONNECT_FAILED,
VOICE_SESSION_RUNTIME_ERROR
}Trace Propagation
Automatic header injection:
X-Request-ID: unique-per-request
traceparent: w3c-trace-context
X-KYCIS-SDK-Version: 1.0.0
X-KYCIS-Session-ID: session-uuidUse AI.traceHeadersForRequest() for host app HTTP calls to maintain correlation.