Skip to content

Results

After a session completes, vital signs results are returned directly in the POST /sdk/session/upload-complete response. By default, this is the only way results are delivered — no server-side storage occurs. When PERSIST_VITALS=true is configured for async/polling workflows, results are also available via GET /sdk/session/result/{sessionId} for the duration of the configured TTL.

{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"vitals": {
"heart_rate": 72,
"respiratory_rate": 16,
"hrv": 45.2,
"spo2": 98.1,
"systolic_bp": 122,
"diastolic_bp": 78,
"confidence": 0.87
},
"completed_at": 1712001900
}
FieldTypeUnitDescription
heart_ratenumberBPMHeart rate in beats per minute
respiratory_ratenumberbreaths/minRespiratory rate in breaths per minute
hrvnumbermsHeart rate variability (SDNN) in milliseconds
spo2number%Blood oxygen saturation percentage
systolic_bpnumbermmHgSystolic blood pressure
diastolic_bpnumbermmHgDiastolic blood pressure
confidencenumber0–1Measurement reliability score

All vital sign fields are present on every completed result. The confidence score applies to the measurement as a whole.

These are the expected ranges for healthy adults. Values outside these ranges are still valid measurements — they may indicate a health condition.

VitalTypical Range
Heart Rate60–100 BPM
Respiratory Rate12–20 breaths/min
HRV (SDNN)20–100 ms
SpO295–100%
Systolic BP90–140 mmHg
Diastolic BP60–90 mmHg

The confidence field indicates how reliable the measurement is, on a scale from 0.0 to 1.0:

RangeMeaningRecommendation
0.7–1.0High confidenceResults are reliable for most use cases
0.4–0.7Moderate confidenceResults are usable but may benefit from a re-scan
0.1–0.4Low confidenceResults may be unreliable — poor lighting, motion, or face detection issues
0.0Fallback valuesInference failed; synthetic values were generated. Do not treat as real measurements
const result = await sdk.measureVitals({
container: document.getElementById('scan-container'),
});
if (result.confidence === 0) {
// Fallback values — inference failed
showError('Measurement could not be completed. Please retry.');
} else if (result.confidence < 0.4) {
// Low confidence — quality issues
showWarning('Low confidence. Try again with better lighting and hold still.');
} else {
// Good measurement
displayResults(result);
}

In the default configuration, results are returned once in the upload-complete response and are not persisted server-side. Your application should handle the results immediately upon receiving the response.

When PERSIST_VITALS=true is enabled, results are cached for the configured TTL (default 15 minutes) and can be retrieved via polling. After the TTL expires, results are automatically and irreversibly deleted.

Uploaded tensor data is automatically deleted after processing is complete regardless of mode.

If you are using persist mode and calling the API directly (without the SDK), poll GET /sdk/session/result/{sessionId} every 2 seconds until the status is completed:

async function pollResults(sessionId, apiKey) {
const maxAttempts = 60; // 120 seconds at 2s intervals
for (let i = 0; i < maxAttempts; i++) {
const response = await fetch(
`https://api.circadify.com/sdk/session/result/${sessionId}`,
{ headers: { 'X-API-Key': apiKey } }
);
const data = await response.json();
if (data.status === 'completed' || data.status === 'failed') {
return data;
}
await new Promise(resolve => setTimeout(resolve, 2000));
}
throw new Error('Polling timed out');
}
  • Sessions — Session lifecycle and endpoints
  • Errors — Handle error responses
  • Data Flow — Full pipeline walkthrough