Events & Callbacks
The widget communicates through callbacks passed to Circadify.init(). All callbacks are optional but recommended for production use.
| Callback | When it fires |
|---|---|
onComplete | Scan finished successfully |
onError | Scan failed (camera, network, quota, etc.) |
onCancel | User closed the modal before completion (button mode only) |
onProgress | Per-frame phase + percent updates |
onQualityWarning | Lighting / motion / face-position issues during the scan |
onComplete
Section titled “onComplete”Fired when a scan completes successfully. Receives a WidgetResult object with the measured vital signs.
Circadify.init({ apiKey: 'ck_live_your_api_key_here', onComplete: (result) => { console.log('Heart Rate:', result.heartRate, 'BPM'); console.log('HRV:', result.hrv, 'ms'); console.log('Respiratory Rate:', result.respiratoryRate, 'breaths/min'); console.log('SpO2:', result.spo2, '%'); console.log('Blood Pressure:', result.systolicBp + '/' + result.diastolicBp, 'mmHg'); console.log('Confidence:', Math.round(result.confidence * 100) + '%'); },});WidgetResult Interface
Section titled “WidgetResult Interface”interface WidgetResult { heartRate: number; // Heart rate in BPM (always present) hrv?: number; // Heart rate variability in milliseconds respiratoryRate?: number; // Respiratory rate in breaths per minute spo2?: number; // Blood oxygen saturation (percentage) systolicBp?: number; // Systolic blood pressure in mmHg diastolicBp?: number; // Diastolic blood pressure in mmHg confidence: number; // Measurement reliability score (0–1) sessionId: string; // Unique session identifier timestamp: number; // Unix timestamp in milliseconds}Optional Vitals
Section titled “Optional Vitals”Fields marked with ? are optional and may be undefined depending on signal quality or the vitals filter you configured. heartRate and confidence are always present.
onError
Section titled “onError”Fired when a scan fails. Receives a standard Error object with a descriptive message property.
Circadify.init({ apiKey: 'ck_live_your_api_key_here', onError: (error) => { console.error('Scan failed:', error.message);
// Show a user-friendly message switch (error.message) { case 'CAMERA_PERMISSION_DENIED': alert('Please allow camera access to measure your vitals.'); break; case 'CAMERA_NOT_FOUND': alert('No camera detected. Please connect a camera and try again.'); break; case 'NETWORK_ERROR': alert('Network error. Please check your connection and try again.'); break; case 'INVALID_API_KEY': console.error('Check your API key configuration.'); break; default: alert('Something went wrong. Please try again.'); } },});Common Errors
Section titled “Common Errors”| Error Message | Description |
|---|---|
CAMERA_PERMISSION_DENIED | User denied camera access or it was blocked by browser settings. |
CAMERA_NOT_FOUND | No camera device available on the user’s machine. |
NETWORK_ERROR | Failed to reach the Circadify API (session creation or result submission). |
INVALID_API_KEY | The provided API key is invalid, revoked, or does not match the environment. |
SESSION_TIMEOUT | The scan took too long and was automatically terminated. |
WASM_LOAD_FAILED | The WebAssembly processing module failed to load. Usually a network or browser compatibility issue. |
onCancel
Section titled “onCancel”Fired when the user closes the modal before the scan completes. No arguments are passed. This only applies in button mode (the default) where the scanner opens in a modal.
Circadify.init({ apiKey: 'ck_live_your_api_key_here', onComplete: (result) => { console.log('Scan complete:', result); }, onCancel: () => { console.log('User cancelled the scan.'); // Optionally show a message or track the event },});Combining All Callbacks
Section titled “Combining All Callbacks”A complete example handling all three scenarios:
<script src="https://cdn.circadify.com/widget.js"></script><script> Circadify.init({ apiKey: 'ck_live_your_api_key_here', onComplete: (result) => { // Send results to your backend fetch('/api/vitals', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ heartRate: result.heartRate, spo2: result.spo2, confidence: result.confidence, sessionId: result.sessionId, }), }); }, onError: (error) => { // Log error for debugging console.error('Circadify scan error:', error.message);
// Show user-friendly notification showNotification('Unable to complete scan. Please try again.'); }, onCancel: () => { // Optional: track cancellation analytics analytics.track('vital_scan_cancelled'); }, });</script>onProgress
Section titled “onProgress”Fires during the readiness gate and capture phase with per-frame phase + percent. Use it to drive a custom progress UI (the modal already has one — this is for inline mode where you may render your own).
Circadify.init({ apiKey: 'ck_live_your_api_key_here', onProgress: (event) => { // event.phase: 'initializing' | 'readiness' | 'capturing' | 'uploading' | 'processing' // event.percent: 0–100 within the current phase document.getElementById('progress').style.width = `${event.percent}%`; },});onQualityWarning
Section titled “onQualityWarning”Fires when scan quality drops below the readiness threshold (lighting, motion, face position, occlusion). The widget already surfaces these in its own UI — wire this callback if you also want to log them or show your own toast.
Circadify.init({ apiKey: 'ck_live_your_api_key_here', onQualityWarning: (warning) => { // warning.type: 'lighting' | 'motion' | 'face_position' | 'occlusion' // warning.severity: 'low' | 'medium' | 'high' // warning.message: human-readable string console.warn(`[${warning.severity}] ${warning.type}: ${warning.message}`); },});Programmatic control
Section titled “Programmatic control”Circadify.init() returns an instance handle with three methods you can call later:
const widget = Circadify.init({ apiKey: 'ck_live_your_api_key_here', mode: 'button', onComplete: (result) => console.log(result),});
// Open the scan modal from your own buttondocument.getElementById('start-scan').addEventListener('click', () => widget.open());
// Close the modal mid-scan (cancels the in-flight scan)document.getElementById('close-scan').addEventListener('click', () => widget.close());
// Tear down the widget completely (releases camera, removes the floating button)widget.destroy();In inline mode, widget.close() is the equivalent of “cancel the in-flight scan” — there’s no modal to close, but it stops the camera and emits onCancel (or onError with CANCELLED).
Next Steps
Section titled “Next Steps”- Configuration — Full config reference
- Styling — Customize the widget appearance