๐Ÿ“˜ API Reference

Complete REST API endpoint documentation for the rPPG platform.

๐Ÿ“˜ API Reference

Complete REST API endpoint documentation for the rPPG platform.

Base URL

https://api.yourdomain.com/api

All endpoints are prefixed with /v1 for versioning.

Authentication

All requests require a Bearer token in the Authorization header:

Authorization: Bearer your-api-key-here

See Authentication for details.


Endpoints

POST /v1/scan-session

Create a new scan session.

Request:

POST /v1/scan-session HTTP/1.1
Host: api.yourdomain.com
Authorization: Bearer rppg_live_xxx
Content-Type: application/json

Response: 201 Created

{
  "session_id": "sess_20251026123456_abc123",
  "created_at": "2025-10-26T12:34:56.789Z",
  "owner": "user_xyz"
}

Response Fields:

  • session_id (string): Unique identifier for this session
  • created_at (string): ISO 8601 timestamp
  • owner (string): API key owner identifier

Example with cURL:

curl -X POST https://api.yourdomain.com/api/v1/scan-session \
  -H "Authorization: Bearer rppg_live_xxx" \
  -H "Content-Type: application/json"

Example with JavaScript:

const response = await fetch('https://api.yourdomain.com/api/v1/scan-session', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer rppg_live_xxx',
    'Content-Type': 'application/json'
  }
});

const session = await response.json();
console.log('Session ID:', session.session_id);

Error Responses:

  • 401 Unauthorized: Invalid or missing API key
  • 429 Too Many Requests: Rate limit exceeded
  • 500 Internal Server Error: Server error

POST /v1/scan-session/{session_id}/video

Upload a video file for analysis.

Request:

POST /v1/scan-session/sess_xxx/video HTTP/1.1
Host: api.yourdomain.com
Authorization: Bearer rppg_live_xxx
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="video.mp4"
Content-Type: video/mp4

[binary video data]
------WebKitFormBoundary--

Parameters:

  • session_id (path): Session ID from /v1/scan-session

Request Body:

  • file (multipart file): Video file to analyze

Video Requirements:

  • Max size: 100MB
  • Formats: MP4, WebM, MOV, AVI
  • Duration: 10-30 seconds recommended
  • Face clearly visible with good lighting

Response: 202 Accepted

{
  "session_id": "sess_20251026123456_abc123",
  "status": "processing",
  "message": "Video uploaded successfully and is being processed"
}

Example with cURL:

curl -X POST https://api.yourdomain.com/api/v1/scan-session/sess_xxx/video \
  -H "Authorization: Bearer rppg_live_xxx" \
  -F "file=@video.mp4"

Example with JavaScript:

const formData = new FormData();
formData.append('file', videoFile);

const response = await fetch(
  `https://api.yourdomain.com/api/v1/scan-session/${sessionId}/video`,
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer rppg_live_xxx'
    },
    body: formData
  }
);

const result = await response.json();
console.log('Status:', result.status);

Error Responses:

  • 400 Bad Request: Invalid file format or size
  • 401 Unauthorized: Invalid or missing API key
  • 404 Not Found: Session ID not found
  • 413 Payload Too Large: File exceeds 100MB
  • 422 Unprocessable Entity: Invalid video content
  • 429 Too Many Requests: Rate limit exceeded
  • 500 Internal Server Error: Server error

GET /v1/scan-session/{session_id}/vitals

Retrieve vital signs for a session.

Request:

GET /v1/scan-session/sess_xxx/vitals HTTP/1.1
Host: api.yourdomain.com
Authorization: Bearer rppg_live_xxx

Parameters:

  • session_id (path): Session ID

Response (Processing): 200 OK

{
  "status": "processing",
  "session_id": "sess_20251026123456_abc123"
}

Response (Completed): 200 OK

{
  "status": "completed",
  "session_id": "sess_20251026123456_abc123",
  "vitals": {
    "heart_rate": 72.5,
    "respiratory_rate": 16.2,
    "hrv": 45.3,
    "blood_pressure": null,
    "confidence": 0.89,
    "timestamp": "2025-10-26T12:35:30.123Z",
    "processing_time_ms": 32450,
    "signal_quality": {
      "snr": 12.5,
      "stability": 0.92,
      "periodicity": 0.88
    }
  }
}

Response (Failed): 200 OK

{
  "status": "failed",
  "session_id": "sess_20251026123456_abc123",
  "error": "No face detected in video"
}

Vitals Fields:

  • heart_rate (number): Heart rate in beats per minute (bpm)
  • respiratory_rate (number): Respiratory rate in breaths per minute
  • hrv (number | null): Heart rate variability
  • blood_pressure (string | null): Blood pressure (systolic/diastolic)
  • confidence (number | null): Confidence score (0-1)
  • timestamp (string): ISO 8601 timestamp of analysis
  • processing_time_ms (number): Processing duration in milliseconds
  • signal_quality (object): Signal quality metrics
    • snr (number): Signal-to-noise ratio
    • stability (number): Signal stability (0-1)
    • periodicity (number): Signal periodicity (0-1)

Status Values:

  • pending: Session created, no video uploaded
  • processing: Video uploaded and being analyzed
  • completed: Analysis complete, vitals available
  • failed: Analysis failed (see error field)

Example with cURL:

curl -X GET https://api.yourdomain.com/api/v1/scan-session/sess_xxx/vitals \
  -H "Authorization: Bearer rppg_live_xxx"

Example with JavaScript (Polling):

async function pollForVitals(sessionId) {
  const maxAttempts = 40; // 2 minutes at 3 second intervals
  
  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(
      `https://api.yourdomain.com/api/v1/scan-session/${sessionId}/vitals`,
      {
        headers: {
          'Authorization': 'Bearer rppg_live_xxx'
        }
      }
    );
    
    const result = await response.json();
    
    if (result.status === 'completed') {
      return result.vitals;
    } else if (result.status === 'failed') {
      throw new Error(result.error);
    }
    
    // Wait 3 seconds before next poll
    await new Promise(resolve => setTimeout(resolve, 3000));
  }
  
  throw new Error('Polling timeout');
}

Error Responses:

  • 401 Unauthorized: Invalid or missing API key
  • 404 Not Found: Session ID not found or expired
  • 429 Too Many Requests: Rate limit exceeded
  • 500 Internal Server Error: Server error

GET /health

Health check endpoint (no authentication required).

Request:

GET /health HTTP/1.1
Host: api.yourdomain.com

Response: 200 OK

{
  "status": "ok",
  "timestamp": "2025-10-26T12:34:56.789Z",
  "service": "rppg-backend"
}

Response Headers

All responses include standard headers:

Content-Type: application/json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1635264000

Rate Limit Headers:

  • X-RateLimit-Limit: Total requests allowed per minute
  • X-RateLimit-Remaining: Requests remaining in current window
  • X-RateLimit-Reset: Unix timestamp when limit resets

Error Responses

All errors follow a consistent format:

{
  "detail": "Error message describing what went wrong"
}

Common Error Codes

| Status Code | Meaning | Common Causes | |-------------|---------|---------------| | 400 | Bad Request | Invalid input, malformed data | | 401 | Unauthorized | Missing or invalid API key | | 403 | Forbidden | API key lacks permissions | | 404 | Not Found | Resource doesn't exist | | 413 | Payload Too Large | File exceeds size limit | | 422 | Unprocessable Entity | Invalid video format or content | | 429 | Too Many Requests | Rate limit exceeded | | 500 | Internal Server Error | Server-side error | | 503 | Service Unavailable | Service temporarily down |


Webhooks

๐Ÿšง Coming Soon: Webhook support for real-time notifications when processing completes.

See Webhooks for details.


Pagination

Current API endpoints do not support pagination. Sessions are ephemeral and results are accessed individually by session ID.


Versioning

The API uses URL-based versioning:

  • Current version: /v1
  • All endpoints are prefixed with /v1/

Future versions will be introduced as /v2/, /v3/, etc., with backwards compatibility maintained for at least 12 months after a new version is released.


Next Steps

Have questions? Contact us at support@circadify.com