Circadify

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",
)
kotlin

For 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 },
        ),
    ),
)
kotlin

Front 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
)
kotlin

Reach 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

PropertyTypeDefaultDescription
apiKeyString-Required. Your API key (ck_test_* or ck_live_*).
baseUrlString"https://api.circadify.com"API base URL.
measurementDurationSecondsInt30Target measurement window and timeout basis. Upload can start earlier once enough quality frames are accepted.
targetCaptureFpsInt10Analysis frame-rate target. Lower values reduce CPU, heat, and bitmap allocation pressure on lower-end devices.
minimumAcceptedFramesInt150Minimum quality frames required before a measurement can finish.
maximumAcceptedFramesInt720Safety cap for accepted frames and in-memory tensor size.
debugBooleanfalseEnables Android logcat debug logging.
onDeviceOnlyBooleantrueRequests the standard local capture and measurement payload preparation path. This does not mean results are computed fully on-device.
callbacksCircadifyCallbacksempty callbacksProgress, quality, landmark, camera readiness, and scan-face callbacks.
cameraLensFacingCameraLensFacingFRONTWhich 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,
)
kotlin

SDK 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
PhaseDescription
INITIALIZINGCreating a session and preparing capture
READINESSWaiting for a scan-ready face and acceptable quality
CAPTURINGCapturing the measurement
UPLOADINGUploading the measurement payload
PROCESSINGWaiting 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()
        }
    },
)
kotlin

Quality warnings

onQualityWarning emits short-lived guidance such as lighting, motion, face position, or occlusion warnings.

callbacks = CircadifyCallbacks(
    onQualityWarning = { warning ->
        showToast(warning.message)
    },
)
kotlin

Landmarks

onLandmarks emits normalized face landmarks. Use it only when you need a custom overlay.

callbacks = CircadifyCallbacks(
    onLandmarks = { landmarks ->
        overlayView.render(landmarks)
    },
)
kotlin
Want the face / heat-glow overlay? Use CircadifyOverlayView — don't rebuild it

The 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
)
kotlin

See 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
PropertyTypeRequiredDescription
lifecycleOwnerLifecycleOwnerYesLifecycle used by CameraX to bind and release camera use cases.
previewViewPreviewView?NoCaller-owned CameraX preview surface. Pass null for headless capture.
demographicsDemographics?NoOptional age, sex, and Fitzpatrick skin type.
cancellationSignalCancellationSignal?NoCancels the measurement without cancelling the whole coroutine scope.

Demographics

Demographics is optional but can improve measurement accuracy when provided.

FieldTypeNotes
ageInt?User age in years. Constructor enforces 0..130.
sexSex?Sex.M or Sex.F. Each constant's apiValue is what gets sent on the wire.
fitzpatrickInt?Fitzpatrick skin type. Constructor enforces 1..6.
Tip

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