Use Circadify with AI Coding Agents
If your team uses an AI coding agent to integrate Circadify, drop these skill files into the agent’s context so it gets the SDK shape, types, and idioms right from the start.
Each skill is a single Markdown file with YAML frontmatter — the format recognized by Claude Code, Cursor, and any agent that consumes AGENTS.md-style instructions.
How to install
Section titled “How to install”Skills
Section titled “Skills”circadify (parent)
Section titled “circadify (parent)”The router — tells the agent which child skill to load based on the target stack, and covers the REST fallback. Always include this one.
---name: circadifydescription: Use when integrating the Circadify SDK (rPPG vital signs from camera) into any app — routes to the platform-specific child skill.---
# Using the Circadify SDK
Circadify measures vital signs (heart rate, HRV, respiratory rate, SpO2, blood pressure) from a device camera. Each platform's SDK has its own idioms, types, and lifecycle — use the child skill that matches the target stack rather than generalizing across platforms.
## Routing
| Stack | Skill ||---|---|| Browser, vanilla JS / TS | `circadify-web` || Browser, React 18+ | `circadify-react` || Native iOS (Swift, iOS 15+) | `circadify-ios` || Native Android (Kotlin, API 24+) | `circadify-android` || Drop-in `<script>` widget | `circadify-embed` || Server-side / no SDK | See "REST fallback" below |
## REST fallback
Direct REST integration is supported only for accounts Circadify has approved for custom payload upload. The upload payload format is not public. Default to an SDK unless support has explicitly confirmed REST access. Endpoint and auth reference: https://docs.circadify.com/rest-api/overview/
## Verification
Before claiming integration is done, the agent should produce a successful `measureVitals` call that returns a result object with a non-zero `confidence` and a UUID `sessionId` — that confirms the full round-trip succeeded, not just compilation.
## See also
- Full docs: https://docs.circadify.com- Architecture: https://docs.circadify.com/architecture/overview/circadify-web
Section titled “circadify-web”For browser apps using @circadify/web-sdk directly (vanilla JS, TypeScript, Vue, Svelte, or any framework with a build step that is not React).
---name: circadify-webdescription: Use when integrating the Circadify Web SDK (@circadify/web-sdk) in a browser app — vanilla JS/TS or any framework with a build step (not React; see circadify-react).---
# Circadify Web SDK
Headless TypeScript SDK for browser-based vital sign measurement. The caller owns the `<video>` element and any overlay UI; the SDK drives the camera, capture, secure upload, and result polling.
## Install
`@circadify/web-sdk` is a private package on GitHub Packages. The customer needs read access on the `@circadify` scope (granted by Circadify support) and a GitHub Personal Access Token with the `read:packages` scope.
Add a project-level `.npmrc`:
```ini@circadify:registry=https://npm.pkg.github.com//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}```
Then export the token and install:
```bashexport GITHUB_TOKEN=ghp_your_read_packages_tokennpm install @circadify/web-sdk```
Requires HTTPS for camera access (localhost works in dev). Browser support: Chrome 80+, Firefox 75+, Safari 14+, Edge 80+.
## Initialize
```typescriptimport { CircadifySDK } from '@circadify/web-sdk';
const sdk = new CircadifySDK({ apiKey: 'ck_live_your_key_here', onProgress: (e) => updateProgressBar(e.phase, e.percent), onQualityState: (q) => updateQualityMeters(q), onQualityWarning: (w) => showToast(w.message),});```
Callbacks are registered on the constructor (not via `.on()`). Full callback list: https://docs.circadify.com/web-sdk/configuration/
## Run a measurement
Render a `<video autoplay playsinline muted>` and pass it as `videoElement`:
```typescriptconst videoEl = document.getElementById('preview') as HTMLVideoElement;
const result = await sdk.measureVitals({ videoElement: videoEl, demographics: { age: 35, sex: 'M', fitzpatrick: 3 }, // optional signal: abortController.signal, // optional});```
If you omit `videoElement`, the SDK creates an off-DOM video and fires `onCameraReady({ stream, video })` so you can mirror the stream into your own UI.
## Result shape
```typescriptinterface VitalSignsResult { heartRate: number; hrv?: number; respiratoryRate?: number; spo2?: number; systolicBp?: number; diastolicBp?: number; confidence: number; // 0–1 sessionId: string; // UUID timestamp: number; // ms}```
`heartRate` and `confidence` are always present; the rest are optional. Treat `confidence` as 0–1; multiply by 100 only when rendering as a percentage.
## Errors
`measureVitals` throws `CircadifyError`. Branch on `error.code`; consult `error.isRetryable` before retrying.
```typescriptimport { CircadifySDK, CircadifyError } from '@circadify/web-sdk';
try { const result = await sdk.measureVitals({ videoElement: videoEl });} catch (error) { if (error instanceof CircadifyError) { if (error.code === 'CAMERA_PERMISSION_DENIED') { showSettingsPrompt(); } else if (error.isRetryable) { showRetry(error.message); } else { showError(error.message); } }}```
Full code list: https://docs.circadify.com/web-sdk/error-codes/
## Cancellation
Use an `AbortController` and pass `signal` to `measureVitals`, or call `sdk.cancel()`.
## Cleanup
Call `sdk.destroy()` when the page or component tears down to release the camera and runtime resources. After `destroy()`, create a new instance for the next measurement.
## See also
- Web SDK reference: https://docs.circadify.com/web-sdk/installation/- Vanilla JS guide: https://docs.circadify.com/integration-guide/vanilla-js/- Custom UI from callbacks: https://docs.circadify.com/integration-guide/custom-ui/circadify-react
Section titled “circadify-react”For React 18+ apps using @circadify/react.
---name: circadify-reactdescription: Use when integrating the Circadify SDK into a React 18+ app via @circadify/react.---
# Circadify React Bindings
`@circadify/react` provides a provider, hooks, and prebuilt views for the Circadify Web SDK. Use it for any React 18+ app. For framework-less browser apps, use `circadify-web` instead.
## Install
`@circadify/react` and `@circadify/web-sdk` are both private packages on GitHub Packages. After the `.npmrc` + PAT setup (see `circadify-web`), install both:
```bashnpm install @circadify/web-sdk @circadify/react```
Import the stylesheet once (typically at the app entry):
```typescriptimport '@circadify/react/styles.css';```
Peer dependencies: `react@>=18`, `react-dom@>=18`. The package is client-only. In Next.js App Router it works without extra config (it injects `"use client"`); in Pages Router or other SSR setups, dynamic-import the scan UI with `ssr: false`.
## Provider
Wrap the subtree that uses scanning:
```tsximport { CircadifyProvider } from '@circadify/react';
function App() { return ( <CircadifyProvider apiKey="ck_live_your_key_here"> <ScanPage /> </CircadifyProvider> );}```
The provider creates one SDK instance and destroys it on unmount. Keep `apiKey` stable across renders (module scope, `useState`, or env var) so the provider doesn't tear down and recreate the SDK each render.
## All-in-one component
The fastest path. Renders Readiness → Scan → Results:
```tsximport { CircadifyScan } from '@circadify/react';
function ScanPage() { return ( <CircadifyScan onResult={(result) => console.log(result.heartRate)} onError={(error) => console.error(error)} /> );}```
Useful props: `autoStart`, `demographics`, `showReadiness`, `showResults`, `onRescan`, `resultsActions`. Full prop list: https://docs.circadify.com/integration-guide/react/
## Hooks (custom UI)
```tsximport { useCircadify, useSession } from '@circadify/react';
function CustomScan() { const { start, stop, result, error, progress, isScanning } = useSession({ type: 'standard', });
return ( <div> <button onClick={() => start()} disabled={isScanning}>Start</button> {result && <p>Heart Rate: {result.heartRate} BPM</p>} </div> );}```
Pass `videoElement: videoRef.current` to `start()` after the ref is bound (inside an effect or click handler, never during render). Additional helpers: `useCamera`, `summarizeQuality`, `formatVitals`.
## Composable views
When `<CircadifyScan>` is too opinionated, compose your own flow with `<CircadifyReadiness>`, `<CircadifyScanView>`, and `<CircadifyResults>` driven by `useSession()`.
## Result and error types
Identical to the Web SDK: `VitalSignsResult` with `heartRate`, `hrv`, `respiratoryRate`, `spo2`, `systolicBp`, `diastolicBp`, `confidence` (0–1), `sessionId`, `timestamp`. Errors are `CircadifyError` instances with `code` and `isRetryable`.
## See also
- Full React guide: https://docs.circadify.com/integration-guide/react/- Web SDK reference: https://docs.circadify.com/web-sdk/methods/circadify-ios
Section titled “circadify-ios”For native iOS apps (Swift, iOS 15+) using the CircadifySDK Swift package.
---name: circadify-iosdescription: Use when integrating the Circadify iOS SDK (CircadifySDK Swift package) into a native iOS 15+ app.---
# Circadify iOS SDK
Native Swift package for vital sign measurement. Requires iOS 15+ and a physical device with a front camera (the iOS Simulator can't run camera capture).
## Install
Add the Swift package via Xcode (**File → Add Package Dependencies**) using:
```texthttps://github.com/circadify/circadify-ios-sdk```
Or in `Package.swift`:
```swiftdependencies: [ .package(url: "https://github.com/circadify/circadify-ios-sdk", from: "1.0.0")]```
The repo is private — access is provisioned during enterprise onboarding.
## Info.plist
Add a clear camera usage description (vague strings risk App Store review rejection):
```xml<key>NSCameraUsageDescription</key><string>This app uses the camera to measure your vital signs contactlessly.</string>```
Without this entry, iOS terminates the app on camera access.
## Initialize
```swiftimport CircadifySDK
let sdk = try CircadifySDK(apiKey: "ck_live_your_key_here")```
For full config:
```swiftlet sdk = try CircadifySDK(config: CircadifyConfig( apiKey: "ck_live_your_key_here", measurementDuration: 30, debug: false))```
## Camera permission
Check before measuring:
```swiftlet status = sdk.checkCameraPermission() // AVAuthorizationStatuslet granted = await sdk.requestCameraPermission() // shows system dialog if .notDetermined```
If status is `.denied` or `.restricted`, deep-link to Settings via `UIApplication.openSettingsURLString` — iOS shows the system dialog only once per install.
## Run a measurement
```swiftlet result = try await sdk.measureVitals()```
With demographics for improved accuracy:
```swiftlet demographics = Demographics(age: 35, sex: .male, fitzpatrick: 3)let result = try await sdk.measureVitals( options: MeasurementOptions(demographics: demographics))```
`Demographics`:
| Field | Type | Notes ||---|---|---|| `age` | `Int?` | User age in years. || `sex` | `Sex?` | `.male` or `.female`. Encoded on the wire as `"M"` / `"F"`. || `fitzpatrick` | `Int?` | Fitzpatrick skin type, `1`–`6`. |
## Result shape
```swiftpublic struct VitalSignsResult { public let heartRate: Int // BPM public let hrv: Double? // ms public let respiratoryRate: Int? public let spo2: Double? // % public let systolicBp: Int? // mmHg public let diastolicBp: Int? public let confidence: Double // 0–1 public let sessionId: String public let timestamp: Date}```
## Callbacks
Set on the instance for progress and quality updates:
```swiftsdk.onProgress = { update in print("\(update.phase) — \(Int(update.percent))%")}sdk.onQualityWarning = { warning in showHint(warning.message)}```
## Errors
`measureVitals` throws `CircadifyError` (an enum). Switch on cases; every case has an `errorDescription` and an `isRetryable` flag.
```swiftdo { let result = try await sdk.measureVitals()} catch let error as CircadifyError { switch error { case .cameraPermissionDenied: showSettingsPrompt() case .rateLimited(let retryAfter): scheduleRetry(after: retryAfter) default: if error.isRetryable { showRetry(error.errorDescription ?? "") } else { showError(error.errorDescription ?? "") } }}```
Full case list: https://docs.circadify.com/ios-sdk/error-handling/
## Cancellation
```swiftsdk.cancel() // the running measureVitals() throws .cancelled```
## See also
- iOS SDK installation: https://docs.circadify.com/ios-sdk/installation/- Camera & permissions: https://docs.circadify.com/ios-sdk/camera-permissions/- Methods reference: https://docs.circadify.com/ios-sdk/methods/circadify-android
Section titled “circadify-android”For native Android apps (Kotlin, API 24+) using com.circadify:circadify-android-sdk.
---name: circadify-androiddescription: Use when integrating the Circadify Android SDK (com.circadify:circadify-android-sdk) into a native Kotlin Android app (API 24+).---
# Circadify Android SDK
Native Kotlin Android library for vital sign measurement. Requires Android API 24+ and a front camera; built on CameraX. The host app owns the UI, permission prompts, and the preview surface; the SDK runs capture, quality checks, secure upload, and result polling.
## Install
The SDK is a private Maven package on GitHub Packages. The customer needs read access on the package and a GitHub token with `read:packages` scope, stored in environment (not committed).
In `settings.gradle.kts`:
```kotlindependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven("https://maven.pkg.github.com/circadify/circadify-kotlin-sdk") { credentials { username = System.getenv("GITHUB_ACTOR") password = System.getenv("GITHUB_TOKEN") } } }}```
In the app module `build.gradle.kts`:
```kotlindependencies { implementation("com.circadify:circadify-android-sdk:0.1.1")}```
## Manifest
```xml<uses-feature android:name="android.hardware.camera" android:required="false" /><uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.INTERNET" />```
## Camera permission (runtime)
Use the AndroidX Activity Result API to request `Manifest.permission.CAMERA` before calling `measureVitals()`. Re-check on resume — Android can revoke permissions while the app is backgrounded.
## Initialize
```kotlinimport com.circadify.sdk.CircadifySDK
val sdk = CircadifySDK(context = context, apiKey = "ck_live_your_key_here")```
With full config and callbacks:
```kotlinval sdk = CircadifySDK( context = context, config = CircadifyConfig( apiKey = "ck_live_your_key_here", callbacks = CircadifyCallbacks( onProgress = { e -> progressBar.progress = e.percent }, onQualityWarning = { w -> showToast(w.message) }, ), ),)```
Callbacks are delivered on the Android main thread.
## Run a measurement
```kotlinlifecycleScope.launch { try { val result = sdk.measureVitals( MeasurementOptions( lifecycleOwner = this@MainActivity, previewView = previewView, demographics = Demographics(age = 35, sex = Sex.M, fitzpatrick = 3), ), ) showResult(result) } catch (e: CircadifyError) { if (e.isRetryable) showRetry(e.message) else showError(e.message) }}```
`MeasurementOptions`:
| Field | Type | Notes ||---|---|---|| `lifecycleOwner` | `LifecycleOwner` | Required. Usually the Activity / Fragment. || `previewView` | `PreviewView?` | Caller-owned CameraX preview. `null` for headless capture. || `demographics` | `Demographics?` | Optional. || `cancellationSignal` | `CancellationSignal?` | Cancels this measurement without cancelling the surrounding coroutine scope. |
`Demographics`:
| Field | Type | Notes ||---|---|---|| `age` | `Int?` | User age in years. Constructor enforces `0..130`. || `sex` | `Sex?` | `Sex.M` or `Sex.F`. || `fitzpatrick` | `Int?` | Fitzpatrick skin type. Constructor enforces `1..6`. |
## Result shape
```kotlinpublic data class VitalSignsResult( val heartRate: Double, val hrv: Double?, val respiratoryRate: Double?, val spo2: Double?, val systolicBp: Double?, val diastolicBp: Double?, val confidence: Double, // 0–1 val timestamp: Long, val sessionId: String,)```
## Errors
```kotlinpublic class CircadifyError( val code: CircadifyErrorCode, override val message: String, val details: Map<String, Any?>? = null,) : Exception(message) { val isRetryable: Boolean}```
Switch on `error.code`. Full code list: https://docs.circadify.com/android-sdk/error-handling/
## Cancellation and cleanup
- `sdk.cancel()` cancels a running measurement; the suspended `measureVitals()` throws `CircadifyError` with code `CANCELLED`.- `sdk.destroy()` in `onDestroy()` to release SDK resources. Create a new instance for the next screen.
## See also
- Android SDK installation: https://docs.circadify.com/android-sdk/installation/- Performance & device support: https://docs.circadify.com/android-sdk/performance/- Camera & permissions: https://docs.circadify.com/android-sdk/camera-permissions/circadify-embed
Section titled “circadify-embed”For drop-in <script> widget integrations on marketing pages and quick demos.
---name: circadify-embeddescription: Use when adding contactless vital sign scanning to a website with a single <script> tag (no build step).---
# Circadify Embed Widget
Drop-in JavaScript widget. No npm install, no bundler — one `<script>` tag and a call to `Circadify.init()`. Ideal for marketing pages, demos, and lightweight integrations. For production apps with full UI control, prefer `circadify-web` (or `circadify-react`).
## Install
Add the script in the page `<body>`:
```html<script src="https://cdn.circadify.com/widget.js"></script>```
## Initialize
```html<script> Circadify.init({ apiKey: 'ck_live_your_key_here', onComplete: (result) => { console.log('Heart Rate:', result.heartRate, 'BPM'); }, onError: (error) => { console.error(error.message); }, });</script>```
A floating "Check Vitals" button appears in the bottom-right corner. Click it to open the modal scanner.
## Modes
`button` (default) — floating trigger opens a modal scanner.
`inline` — embed the scanner directly in a container element:
```html<div id="scanner" style="width: 480px; height: 360px;"></div>
<script> Circadify.init({ apiKey: 'ck_live_your_key_here', mode: 'inline', container: '#scanner', onComplete: (result) => console.log(result), });</script>```
The container must have explicit width and height (minimum 320×240).
## Theme
```javascriptCircadify.init({ apiKey: 'ck_live_your_key_here', theme: { accentColor: '#10B981', borderRadius: '8px', }, onComplete: (result) => console.log(result),});```
The widget renders inside a Shadow DOM, so site CSS and widget CSS cannot leak into each other — no conflict risk.
## Result shape
```typescriptinterface WidgetResult { heartRate: number; hrv?: number; respiratoryRate?: number; spo2?: number; systolicBp?: number; diastolicBp?: number; confidence: number; // 0–1 sessionId: string; timestamp: number;}```
## Callbacks
| Callback | When it fires ||---|---|| `onComplete(result)` | Scan finished successfully || `onError(error)` | Scan failed || `onCancel()` | User closed the modal (button mode only) || `onProgress(event)` | Per-frame phase + percent || `onQualityWarning(warning)` | Lighting / motion / face / occlusion guidance |
## Programmatic control
`Circadify.init()` returns an instance handle:
```javascriptconst widget = Circadify.init({ apiKey: 'ck_live_your_key_here', onComplete: (result) => console.log(result),});
widget.open(); // open the modal from your own buttonwidget.close(); // close the modal / cancel the scanwidget.destroy(); // release the camera and remove the floating button```
## Filtered vitals
To return only specific fields:
```javascriptCircadify.init({ apiKey: 'ck_live_your_key_here', vitals: ['heartRate', 'spo2'], onComplete: (result) => console.log(result),});```
Scan duration is unchanged; only the result fields are filtered.
## See also
- Embed widget overview: https://docs.circadify.com/embed/overview/- Configuration reference: https://docs.circadify.com/embed/configuration/- Events & callbacks: https://docs.circadify.com/embed/events/- Styling: https://docs.circadify.com/embed/styling/