Skip to content

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.

The router — tells the agent which child skill to load based on the target stack, and covers the REST fallback. Always include this one.

circadify.md
---
name: circadify
description: 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/

For browser apps using @circadify/web-sdk directly (vanilla JS, TypeScript, Vue, Svelte, or any framework with a build step that is not React).

circadify-web.md
---
name: circadify-web
description: 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:
```bash
export GITHUB_TOKEN=ghp_your_read_packages_token
npm install @circadify/web-sdk
```
Requires HTTPS for camera access (localhost works in dev). Browser support: Chrome 80+, Firefox 75+, Safari 14+, Edge 80+.
## Initialize
```typescript
import { 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`:
```typescript
const 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
```typescript
interface 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.
```typescript
import { 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/

For React 18+ apps using @circadify/react.

circadify-react.md
---
name: circadify-react
description: 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:
```bash
npm install @circadify/web-sdk @circadify/react
```
Import the stylesheet once (typically at the app entry):
```typescript
import '@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:
```tsx
import { 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:
```tsx
import { 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)
```tsx
import { 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/

For native iOS apps (Swift, iOS 15+) using the CircadifySDK Swift package.

circadify-ios.md
---
name: circadify-ios
description: 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:
```text
https://github.com/circadify/circadify-ios-sdk
```
Or in `Package.swift`:
```swift
dependencies: [
.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
```swift
import CircadifySDK
let sdk = try CircadifySDK(apiKey: "ck_live_your_key_here")
```
For full config:
```swift
let sdk = try CircadifySDK(config: CircadifyConfig(
apiKey: "ck_live_your_key_here",
measurementDuration: 30,
debug: false
))
```
## Camera permission
Check before measuring:
```swift
let status = sdk.checkCameraPermission() // AVAuthorizationStatus
let 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
```swift
let result = try await sdk.measureVitals()
```
With demographics for improved accuracy:
```swift
let 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
```swift
public 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:
```swift
sdk.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.
```swift
do {
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
```swift
sdk.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/

For native Android apps (Kotlin, API 24+) using com.circadify:circadify-android-sdk.

circadify-android.md
---
name: circadify-android
description: 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`:
```kotlin
dependencyResolutionManagement {
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`:
```kotlin
dependencies {
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
```kotlin
import com.circadify.sdk.CircadifySDK
val sdk = CircadifySDK(context = context, apiKey = "ck_live_your_key_here")
```
With full config and callbacks:
```kotlin
val 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
```kotlin
lifecycleScope.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
```kotlin
public 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
```kotlin
public 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/

For drop-in <script> widget integrations on marketing pages and quick demos.

circadify-embed.md
---
name: circadify-embed
description: 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
```javascript
Circadify.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
```typescript
interface 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:
```javascript
const widget = Circadify.init({
apiKey: 'ck_live_your_key_here',
onComplete: (result) => console.log(result),
});
widget.open(); // open the modal from your own button
widget.close(); // close the modal / cancel the scan
widget.destroy(); // release the camera and remove the floating button
```
## Filtered vitals
To return only specific fields:
```javascript
Circadify.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/