Circadify

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

  1. Create a session via POST /sdk/session/start.

  2. Upload the SDK-prepared measurement payload to the secure upload URL returned by the API.

  3. Notify the API via POST /sdk/session/upload-complete. The API accepts the upload and starts processing.

  4. Read the result by polling GET /sdk/session/{sessionId}/result.

Caution

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/start
http

Headers:

X-API-Key: ck_live_your_key_here
Content-Type: application/json
http

Request body (optional):

{
  "demographics": {
    "age": 35,
    "sex": "M",
    "fitzpatrick": 3
  }
}
json

Demographics 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
FieldTypeDescription
session_idstringUnique session identifier
expires_atstringISO-8601 timestamp when the session expires
upload_urlstringGoogle Cloud Storage V4 signed PUT URL for the SDK-prepared upload payload
statusstringInitial 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.bin
bash

The 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.

MethodPathDescription
PUT/sdk/session/{sessionId}/uploadSingle-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/finalizeCompose 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-complete
http

Headers:

X-API-Key: ck_live_your_key_here
Content-Type: application/json
http

Request body:

{
  "session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
json

Response 202 Accepted:

{
  "session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "processing"
}
json

After this response, poll GET /sdk/session/{sessionId}/result until the session reaches completed or failed. SDKs handle this polling automatically.

Session Statuses

StatusDescription
pendingSession initialized, awaiting upload completion
uploadedUpload has completed and is awaiting processing
processingProcessing in progress
completedResults available
failedProcessing failed
expiredSession timed out before completion

Errors

StatusError CodeDescription
400INVALID_REQUESTMissing session_id or invalid parameters
401API_KEY_INVALIDInvalid, revoked, or missing API key
403FORBIDDENDeveloper account is not active, or the session's environment (live/test) does not match the calling key
404SESSION_NOT_FOUNDSession 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)
410SESSION_EXPIREDSession 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
422Processing failed. The result poll returns { "session_id", "status": "failed", "error": "<message>" }failed is a session status, not an error code
429RATE_LIMIT_EXCEEDEDSandbox (ck_test_) request rate limit exceeded (best-effort, retryable)
429QUOTA_EXCEEDEDMonthly 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