Circadify

Events & Callbacks

Handle scan results, errors, progress, and cancellations from the Circadify Embed Widget

The widget communicates through callbacks passed to Circadify.init(). All callbacks are optional but recommended for production use.

CallbackWhen it fires
onCompleteScan finished successfully
onErrorScan failed (camera, network, quota, etc.)
onCancelUser closed the modal before completion (button mode only)
onProgressPer-frame phase + percent updates
onQualityWarningLighting / motion / face-position issues during the scan

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) + '%');
  },
});
javascript

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
}
typescript
Tip

A confidence score above 0.7 indicates a reliable measurement. Below 0.4 suggests quality issues such as poor lighting, excessive movement, or partial face visibility. Consider prompting the user to retry if confidence is low.

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

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.');
    }
  },
});
javascript

Common Errors

Error MessageDescription
CAMERA_PERMISSION_DENIEDUser denied camera access or it was blocked by browser settings.
CAMERA_NOT_FOUNDNo camera device available on the user's machine.
NETWORK_ERRORFailed to reach the Circadify API (session creation or result submission).
INVALID_API_KEYThe provided API key is invalid, revoked, or does not match the environment.
SESSION_TIMEOUTThe scan took too long and was automatically terminated.
WASM_LOAD_FAILEDRequired SDK runtime assets failed to load. Usually a network or browser compatibility issue.

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
  },
});
javascript
Note

The onCancel callback is not fired in inline mode since there is no modal to close. To handle cancellation in inline mode, use the Circadify.stop() method programmatically.

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>
html

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}%`;
  },
});
javascript

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}`);
  },
});
javascript

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 button
document.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();
javascript

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