Sessions
Create measurement sessions, upload measurement data, and retrieve results via the REST API.
Sessions represent a single vital signs measurement workflow. SDK integrations normally manage these calls for you.
Session Lifecycle
-
Create a session via
POST /sdk/session/start. -
Upload the SDK-prepared measurement payload to the secure upload URL returned by the API.
-
Notify the API via
POST /sdk/session/upload-complete. The API accepts the upload and starts processing. -
Read the result by polling
GET /sdk/session/{sessionId}/result.
The low-level upload payload format is not documented publicly. Use an SDK unless Circadify has approved direct upload access for your integration.
Create a Session
POST /sdk/session/starthttpHeaders:
X-API-Key: ck_live_your_key_here
Content-Type: application/jsonhttpRequest body (optional):
{
"demographics": {
"age": 35,
"sex": "M",
"fitzpatrick": 3
}
}jsonDemographics are optional but can improve measurement quality when provided.
Response 200 OK:
{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"expires_at": "2026-05-24T18:00:00.000Z",
"upload_url": "https://storage.googleapis.com/<bucket>/uploads/a1b2c3d4-e5f6-7890-abcd-ef1234567890/video.webm?X-Goog-Algorithm=GOOG4-RSA-SHA256&...",
"status": "pending"
}json| Field | Type | Description |
|---|---|---|
session_id | string | Unique session identifier |
expires_at | string | ISO-8601 timestamp when the session expires |
upload_url | string | Google Cloud Storage V4 signed PUT URL for the SDK-prepared upload payload |
status | string | Initial session status, usually pending |
The upload_url is a time-limited GCS signed URL on https://storage.googleapis.com/... — if you run CSP or egress controls, allowlist storage.googleapis.com (not a Circadify host) for the upload step.
Upload Measurement Payload
SDKs upload the measurement payload automatically. Approved direct REST integrations upload the SDK-prepared binary payload to the upload_url returned from session creation:
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: video/webm" \
--data-binary @measurement-payload.binbashThe upload payload is not raw video, raw frames, or a public media format. The low-level payload structure is intentionally private and may change between SDK versions.
Authenticated proxy upload (Web SDK 4.1+)
As an alternative to the signed upload_url, the API exposes authenticated proxy-upload endpoints. These are used by the Web SDK 4.1+ (so the browser only ever talks to api.circadify.com); direct REST integrators may continue using the signed upload_url.
| Method | Path | Description |
|---|---|---|
PUT | /sdk/session/{sessionId}/upload | Single-shot upload (body up to ~30 MiB) — returns 204 |
PUT | /sdk/session/{sessionId}/upload/part/{index} | One chunk of a chunked upload — returns 204 |
POST | /sdk/session/{sessionId}/upload/finalize | Compose uploaded chunks into the session object (body { "parts": N }) — returns 204 |
All three authenticate with X-API-Key and accept Content-Type: video/webm or application/octet-stream. Other content types are rejected with 415; oversize bodies with 413.
Notify Upload Complete
POST /sdk/session/upload-completehttpHeaders:
X-API-Key: ck_live_your_key_here
Content-Type: application/jsonhttpRequest body:
{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}jsonResponse 202 Accepted:
{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "processing"
}jsonAfter this response, poll GET /sdk/session/{sessionId}/result until the session reaches completed or failed. SDKs handle this polling automatically.
Session Statuses
| Status | Description |
|---|---|
pending | Session initialized, awaiting upload completion |
uploaded | Upload has completed and is awaiting processing |
processing | Processing in progress |
completed | Results available |
failed | Processing failed |
expired | Session timed out before completion |
Errors
| Status | Error Code | Description |
|---|---|---|
400 | INVALID_REQUEST | Missing session_id or invalid parameters |
401 | API_KEY_INVALID | Invalid, revoked, or missing API key |
403 | FORBIDDEN | Developer account is not active, or the session's environment (live/test) does not match the calling key |
404 | SESSION_NOT_FOUND | Session ID does not exist — also returned for sessions that belong to another developer (existence is hidden, so a session you don't own looks like a 404, not a 403) |
410 | SESSION_EXPIRED | Session expired before completion (on upload/upload-complete). The result poll instead returns 410 with { "session_id", "status": "expired" } and no error envelope. The result poll also returns 410 (status completed, no vitals) once a completed result passes the 24-hour retention window and its vitals are purged |
422 | — | Processing failed. The result poll returns { "session_id", "status": "failed", "error": "<message>" } — failed is a session status, not an error code |
429 | RATE_LIMIT_EXCEEDED | Sandbox (ck_test_) request rate limit exceeded (best-effort, retryable) |
429 | QUOTA_EXCEEDED | Monthly scan quota exhausted on a production (ck_live_) key (not retryable) |
On POST /sdk/session/start, a live key whose monthly scan quota is exhausted returns 429 QUOTA_EXCEEDED (retryable: false) — track usage on the Usage page in the developer portal. No Retry-After header is emitted on either 429.
See Errors for the full error reference.
Next Steps
- Results - Interpret vital signs data
- Rate Limits - Understand rate and quota limits
- Data Flow - High-level lifecycle