Sessions Guide
All yes:id SDK modules validate a session token before processing. Sessions are short-lived tokens that tie a capture operation to a verified backend workflow.
Android apps create sessions in-process with YesidSessionManager. Web apps create sessions on a backend, then pass sessionId and a publishable key into the browser widgets.
How it worksβ
Android
Your app β YesidSessionManager.setup(...) β createSession(steps)
β sessionId
Your app β setSessionID(sessionId) in each SDK config
SDK validates sessionId before starting capture
Web
Browser β POST /create-session β your backend
Backend β POST api.yesid.tech/.../session (X-Api-Key: sk_β¦)
β { sessionId }
Browser β widgets get publishableKey + sessionId
Androidβ
1. Initialise YesidSessionManagerβ
Call this once β in Application.onCreate() or before any SDK is shown:
import io.yesid.licensing.domain.YesidSessionManager
YesidSessionManager.setup(
api = "YOUR_API_KEY",
publishable = "YOUR_PUBLISHABLE_KEY",
consumerUsername = "YOUR_CONSUMER_USERNAME"
)
2. Create a sessionβ
Call createSession() with the steps your workflow needs:
val response = YesidSessionManager.createSession(
steps = SessionSteps(
documentCapture = DocumentCaptureStep(
enabled = true,
captureMode = "both",
documentsAllowed = listOf("passport", "national_id"),
ocrEnabled = true
),
liveness = LivenessStep(enabled = true),
faceMatch = FaceMatchStep(enabled = true)
),
refId = UUID.randomUUID().toString(),
ttlMinutes = 1440,
allowResubmission = true
)
val sessionId = response?.sessionId
Recommended: wrap in a SessionManager with state flowsβ
For production apps, expose session state to your UI so you can show a loading screen while the session creates and an error screen if it fails:
object SessionManager {
private val _sessionId = MutableStateFlow<String?>(null)
private val _isLoading = MutableStateFlow(false)
private val _error = MutableStateFlow<String?>(null)
val sessionId = _sessionId.asStateFlow()
val isLoading = _isLoading.asStateFlow()
val error = _error.asStateFlow()
suspend fun createSession() {
_isLoading.update { true }
_error.update { null }
try {
val response = YesidSessionManager.createSession(
steps = SessionSteps(
documentCapture = DocumentCaptureStep(
enabled = true,
captureMode = "both",
documentsAllowed = listOf("passport", "national_id"),
ocrEnabled = true
),
liveness = LivenessStep(enabled = true),
faceMatch = FaceMatchStep(enabled = true)
),
refId = UUID.randomUUID().toString(),
ttlMinutes = 1440,
allowResubmission = true
)
_sessionId.update { response?.sessionId }
} catch (e: Exception) {
_error.update { e.message ?: "Unknown error" }
_sessionId.update { null }
} finally {
_isLoading.update { false }
}
}
}
3. Observe session state and launch the SDKβ
Use the state flows in your composable. Create a session on app start, show a loading screen until it resolves, then pass the sessionId to whichever SDK you open:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val scope = rememberCoroutineScope()
val sessionId by SessionManager.sessionId.collectAsState()
val isLoading by SessionManager.isLoading.collectAsState()
val error by SessionManager.error.collectAsState()
LaunchedEffect(Unit) { scope.launch { SessionManager.createSession() } }
when {
isLoading -> LoadingScreen("Starting a secure sessionβ¦")
error != null -> ErrorScreen(
message = "Couldn't start a session\n${error}",
onRetry = { scope.launch { SessionManager.createSession() } }
)
sessionId != null -> HomeScreen(
sessionId = sessionId!!,
openOCR = { startActivity(Intent(this, OCRActivity::class.java)
.putExtra("sessionId", sessionId)) },
openFace = { startActivity(Intent(this, FaceActivity::class.java)
.putExtra("sessionId", sessionId)) }
)
}
}
}
}
4. Pass the sessionId into each SDKβ
Every SDK module accepts the session ID through its configuration builder:
OCRConfigurationBuilder
.setUserLicense("YOUR_LICENSE_KEY")
.setSessionID(sessionId)
.setOCRMode(OCR_MODE.camera)
FaceAuthConfigurationBuilder
.setUserLicense("YOUR_LICENSE_KEY")
.setSessionID(sessionId)
FaceEnrollmentConfigurationBuilder
.setUserLicense("YOUR_LICENSE_KEY")
.setSessionID(sessionId)
TouchlessConfigurationBuilder
.setUserLicense("YOUR_LICENSE_KEY")
Web (browser)β
The web SDKs (@yesid/document-scanner and @yesid/liveness-sdk) use the same session model, but session creation must happen on your backend. The browser never sees YESID_API_KEY.
Browser β GET /config β publishableKey
Browser β POST /create-session β your backend
Your backend β POST https://api.yesid.tech/api/v1/sdk/session
β { sessionId }
Browser β widgets get publishableKey + sessionId
Browser β GET /session-results/:id β your backend (secret key)
1. Keep the secret key on the serverβ
YESID_API_KEY=sk_test_your_secret_key_here
YESID_PUBLISHABLE_KEY=pk_live_your_publishable_key_here
YESID_SESSION_URL=https://api.yesid.tech/api/v1/sdk/session
Expose the publishable key from GET /config. Create sessions from POST /create-session. Proxy results from GET /session-results/:sessionId.
2. Create a session from your backendβ
curl -X POST https://api.yesid.tech/api/v1/sdk/session \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: YOUR_SECRET_API_KEY' \
-H 'X-Idempotency-Key: unique-reference-id' \
-d '{
"referenceId": "unique-reference-id",
"ttlMinutes": 1440,
"allowResubmission": true,
"steps": {
"documentCapture": {
"enabled": true,
"allowedDocuments": ["passport", "national_id", "driver_license", "voter_card", "visa"],
"sourceMode": "both",
"ocrEnabled": true
},
"liveness": { "enabled": true },
"faceMatch": { "enabled": true }
}
}'
Response:
{ "sessionId": "vs_β¦" }
faceMatch is configured on the session. After document capture and liveness both succeed, yes:id runs matching automatically β there is no face-match web widget.
Allowed documentCapture.allowedDocuments values: passport, national_id, driver_license, residence_permit, visa, voter_card, not_recognized.
sourceMode is "camera", "upload", or "both".
3. Pass credentials into the widgetsβ
const scanner = document.querySelector('yesid-document-capture');
const liveness = document.querySelector('liveness-check');
scanner.publishableKey = publishableKey;
scanner.sessionId = sessionId;
liveness.publishableKey = publishableKey;
liveness.sessionId = sessionId;
React: pass publishableKey and sessionId as props to DocumentCapture and LivenessReact. See Document Capture β Usage and Liveness β Usage.
4. Fetch results from your backendβ
curl https://api.yesid.tech/api/v1/sdk/session/{sessionId}/results \
-H 'X-Api-Key: YOUR_SECRET_API_KEY'
Never call this URL from the browser with the secret key. Proxy it, as in the yes:id web demo's /session-results/:sessionId route.
SessionSteps referenceβ
| Field | Type | Description |
|---|---|---|
documentCapture.enabled | Boolean | Enable document scanning for this session |
documentCapture.captureMode | String | Android: "front" or "both" |
documentCapture.sourceMode | String | Web API: "camera", "upload", or "both" |
documentCapture.documentsAllowed | List<String> | Android: e.g. ["passport", "national_id"] |
documentCapture.allowedDocuments | string[] | Web API: same values, different field name |
documentCapture.ocrEnabled | Boolean | Extract text fields from the scanned document |
liveness.enabled | Boolean | Enable liveness capture for this session |
faceMatch.enabled | Boolean | Enable face matching for this session |
The Android SDK builders use captureMode / documentsAllowed. The HTTP Session API used by the web SDKs uses sourceMode / allowedDocuments.
Troubleshootingβ
sessionId stays null after createSession()
Check SessionManager.error.value β the most common cause is a wrong API key or no network connection.
Session validation returns false on an SDK page
Sessions expire after ttlMinutes. Create a fresh session and pass the new ID. Set allowResubmission = true to let the same session be reused for retries during development.
IllegalStateException: Session ID is empty
setSessionID(...) was not called on the configuration builder. Verify the sessionId flow is non-null before navigating to any SDK screen.
Web widgets do not appear, or the camera never starts
Confirm publishableKey and sessionId are set, the page is on https (or localhost), and the browser has camera permission.
Web session creation returns 401
You are calling the Session API with a publishable key, or the secret key is wrong. The X-Api-Key header must be sk_β¦ and must only be sent from your backend.
CORS errors from the browser
The browser must not call api.yesid.tech with the secret key. Create sessions and fetch results through your own backend.