Configuration
Configure the Circadify Android SDK with API keys, callbacks, and measurement options.
Create one CircadifySDK instance for the screen or flow that performs measurements. Only apiKey is required.
Basic initialization
import com.circadify.sdk.CircadifySDK
val sdk = CircadifySDK(
context = context,
apiKey = "ck_live_your_key_here",
)kotlinFor full control, pass a CircadifyConfig:
import com.circadify.sdk.CameraLensFacing
import com.circadify.sdk.CircadifyCallbacks
import com.circadify.sdk.CircadifyConfig
import com.circadify.sdk.CircadifySDK
val sdk = CircadifySDK(
context = context,
config = CircadifyConfig(
apiKey = "ck_live_your_key_here",
baseUrl = "https://api.circadify.com",
measurementDurationSeconds = 30,
targetCaptureFps = 10,
minimumAcceptedFrames = 150,
maximumAcceptedFrames = 720,
debug = false,
onDeviceOnly = true,
cameraLensFacing = CameraLensFacing.FRONT,
callbacks = CircadifyCallbacks(
onProgress = { event -> updateProgress(event.percent, event.phase) },
onQualityState = { state -> updateQualityMeters(state) },
onQualityWarning = { warning -> showQualityHint(warning.message) },
onLandmarks = { landmarks -> renderFaceOverlay(landmarks) },
onCameraReady = { info -> showPreviewReady(info.hasPreviewSurface) },
onScanFace = { face -> overlay.scanFace = face },
),
),
)kotlinFront or back camera
By default the SDK captures from the front (selfie) camera — the right choice for self-scans, where the user watches the live preview to frame their own face. Set cameraLensFacing = CameraLensFacing.BACK for the rear camera instead:
import com.circadify.sdk.CameraLensFacing
val config = CircadifyConfig(
apiKey = "ck_test_your_key_here",
cameraLensFacing = CameraLensFacing.BACK, // defaults to CameraLensFacing.FRONT
)kotlinReach for the rear camera when an operator measures another person (or in a kiosk / tripod setup). Its preview is not mirrored, and because the phone's screen no longer acts as a fill light for the subject (it faces away from them), ensure good ambient lighting. Both lenses feed the identical on-device pipeline and produce the same measurements. The bound lens is reported back on onCameraReady via CameraReadyInfo.lensFacing, and you can check availability before measuring with sdk.getDeviceCapabilities() (hasFrontCamera / hasBackCamera).
Configuration reference
| Property | Type | Default | Description |
|---|---|---|---|
apiKey | String | - | Required. Your API key (ck_test_* or ck_live_*). |
baseUrl | String | "https://api.circadify.com" | API base URL. |
measurementDurationSeconds | Int | 30 | Target measurement window and timeout basis. Upload can start earlier once enough quality frames are accepted. |
targetCaptureFps | Int | 10 | Analysis frame-rate target. Lower values reduce CPU, heat, and bitmap allocation pressure on lower-end devices. |
minimumAcceptedFrames | Int | 150 | Minimum quality frames required before a measurement can finish. |
maximumAcceptedFrames | Int | 720 | Safety cap for accepted frames and in-memory tensor size. |
debug | Boolean | false | Enables Android logcat debug logging. |
onDeviceOnly | Boolean | true | Requests the standard local capture and measurement payload preparation path. This does not mean results are computed fully on-device. |
callbacks | CircadifyCallbacks | empty callbacks | Progress, quality, landmark, camera readiness, and scan-face callbacks. |
cameraLensFacing | CameraLensFacing | FRONT | Which camera lens to capture from. FRONT (selfie) for self-measurement; BACK for the rear camera when measuring another subject. See Front or back camera. |
Callbacks
Callbacks are optional. Use them to build the scan UI around the headless SDK.
data class CircadifyCallbacks(
val onProgress: ((ProgressEvent) -> Unit)? = null,
val onQualityWarning: ((QualityWarning) -> Unit)? = null,
val onQualityState: ((QualityState) -> Unit)? = null,
val onLandmarks: ((List<Landmark>) -> Unit)? = null,
val onCameraReady: ((CameraReadyInfo) -> Unit)? = null,
val onScanFace: ((CircadifyScanFace?) -> Unit)? = null,
)kotlinSDK callbacks are delivered on the Android main thread. Heavy capture, ROI extraction, tensor preparation, upload, and result polling are dispatched internally by the SDK.
Progress
callbacks = CircadifyCallbacks(
onProgress = { event ->
progressBar.progress = event.percent
statusText.text = event.phase.name.lowercase()
qualityFramesText.text = "${event.acceptedFrames ?: 0}/${event.targetFrames ?: 0}"
},
)kotlin| Phase | Description |
|---|---|
INITIALIZING | Creating a session and preparing capture |
READINESS | Waiting for a scan-ready face and acceptable quality |
CAPTURING | Capturing the measurement |
UPLOADING | Uploading the measurement payload |
PROCESSING | Waiting for results |
During CAPTURING, ProgressEvent can include acceptedFrames, targetFrames, captureFps, and remainingSeconds. Use these fields for developer diagnostics or subtle scan copy, but avoid making users interpret raw frame counts.
Quality state
onQualityState emits the current lighting, motion, pose, readiness flag, and messages. Use it for persistent UI such as quality meters.
callbacks = CircadifyCallbacks(
onQualityState = { state ->
if (!state.isReady && state.messages.isNotEmpty()) {
statusText.text = state.messages.first()
}
},
)kotlinQuality warnings
onQualityWarning emits short-lived guidance such as lighting, motion, face position, or occlusion warnings.
callbacks = CircadifyCallbacks(
onQualityWarning = { warning ->
showToast(warning.message)
},
)kotlinLandmarks
onLandmarks emits normalized face landmarks. Use it only when you need a custom overlay.
callbacks = CircadifyCallbacks(
onLandmarks = { landmarks ->
overlayView.render(landmarks)
},
)kotlinThe SDK ships CircadifyOverlayView, which already renders the thermal scan glow for you. Drop it on top of your PreviewView and feed it from onScanFace instead of hand-rolling an overlay from onLandmarks. Reach for onLandmarks (raw landmarks) only when you genuinely need a fully custom overlay — see Scan Overlay.
Scan face overlay
onScanFace emits live CircadifyScanFace geometry for each analyzed frame — or null when no face is detected. Feed it straight to the bundled CircadifyOverlayView to render the thermal scan glow, or consume the geometry to draw your own overlay.
callbacks = CircadifyCallbacks(
onScanFace = { face -> overlay.scanFace = face }, // null when no face is detected
)kotlinSee Scan Overlay for the full overlay setup and theming.
Measurement options
Pass MeasurementOptions each time you call measureVitals().
val cancellationSignal = CancellationSignal()
val result = sdk.measureVitals(
MeasurementOptions(
lifecycleOwner = lifecycleOwner,
previewView = previewView,
demographics = Demographics(age = 35, sex = Sex.M, fitzpatrick = 3),
cancellationSignal = cancellationSignal,
),
)kotlin| Property | Type | Required | Description |
|---|---|---|---|
lifecycleOwner | LifecycleOwner | Yes | Lifecycle used by CameraX to bind and release camera use cases. |
previewView | PreviewView? | No | Caller-owned CameraX preview surface. Pass null for headless capture. |
demographics | Demographics? | No | Optional age, sex, and Fitzpatrick skin type. |
cancellationSignal | CancellationSignal? | No | Cancels the measurement without cancelling the whole coroutine scope. |
Demographics
Demographics is optional but can improve measurement accuracy when provided.
| Field | Type | Notes |
|---|---|---|
age | Int? | User age in years. Constructor enforces 0..130. |
sex | Sex? | Sex.M or Sex.F. Each constant's apiValue is what gets sent on the wire. |
fitzpatrick | Int? | Fitzpatrick skin type. Constructor enforces 1..6. |
Keep camera and result UI on the main thread. measureVitals() is dispatcher-safe, so a normal lifecycleScope.launch or Compose viewModelScope.launch is acceptable.
Next Steps
- Methods - SDK method reference
- Scan Overlay - The bundled thermal scan glow and theming
- Performance & Device Support - Capture tuning and lower-end device guidance
- Camera & Permissions - Runtime permission flow
- Error Handling - Handle failures and retries