MEDWEAR

MED-API

REST API reference for wearable health data ingestion and management

Overview

MED-API is the backend service of the MEDWEAR platform. It provides a secure REST API for ingesting, querying, and managing wearable health data, deployed alongside MinIO, InfluxDB, PostgreSQL, Keycloak, Dagster, Grafana, and pgAdmin as a single-server stack.

For installation, Docker setup, authentication, and testing, see the MED-API Setup section in Documentation. Full documentation is also available on Google Drive.

Roles

Role Access
device Ingest data only
collaborator Read data from assigned projects
researcher Read data, download files
project_admin Manage users within their project
global_admin Full access: all projects, user/project management

Web Interface

Available at https://localhost. Login with Keycloak credentials.

Tab Access Description
Dashboard All roles Batch statistics, recent uploads
Upload collaborator+ JSON, CSV, raw file upload (.cwa / .json / .csv)
Query Data collaborator+ Filter and search batches, view/download signals
File Manager collaborator+ MinIO files: list, download, soft-delete, restore
Activities project_admin, global_admin Audit log - All Activities + File Activities tabs
Admin project_admin, global_admin User and project management
Health Check All roles Real-time service status

Data Ingestion

POST /v1/data/batch - Structured sensor data

Stores metadata in PostgreSQL and signals in InfluxDB. Supports JSON and CSV.

Supported signal types: ECG, BCG, PPG, IMU, EEG, EDA, HR, HRV

Example request
curl -k -X POST https://localhost/v1/data/batch \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "batches": [{
      "header": {
        "uuid": "your-uuid",
        "schema_id": "ecg-lead-1.x",
        "source_creation_date_time": "2026-03-12T10:00:00Z",
        "acquisition_rate": {"value": 250.0, "unit": "Hz", "number_of_times": 1}
      },
      "body": {
        "ecg_lead_data": [0.12, 0.15, 0.11],
        "unit": "mV",
        "effective_time_frame": {"time_interval": {
          "start_date_time": "2026-03-12T10:00:00Z",
          "end_date_time": "2026-03-12T10:00:03Z"
        }}
      },
      "signal_type": "ECG",
      "patient_id": 123456,
      "device_id": "polar-h10-01",
      "idempotency_key": "polar-h10-01-20260312-001"
    }]
  }'

POST /v1/data/upload - Raw file upload

Stores raw files (.cwa, .json, .csv) in MinIO. Max 1 GB.

Example request
curl -k -X POST https://localhost/v1/data/upload \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/octet-stream" \
  -H "X-Filename: ecg_data.csv" \
  --data-binary @ecg_data.csv

Query Endpoints

Method Endpoint Description
GET /v1/data List/filter batches
GET /v1/data/{uuid} Single batch (metadata + signals)
GET /v1/data/{uuid}/metadata Metadata only
GET /v1/data/{uuid}/signals Signals only
GET /v1/data/patients/{patient_id} All batches for a patient
DELETE /v1/data/{uuid} Soft delete batch
POST /v1/data/{uuid}/restore Restore soft-deleted batch
GET /v1/data/files List MinIO files
GET /v1/data/files/{path} Download file
DELETE /v1/data/files/{path} Soft delete file
POST /v1/data/files/{path}/restore Restore deleted file

Query parameters for /v1/data: patient_ids, signal_type, start, end, device_id, project_id, limit, offset, metadata_only, show_deleted, format (json/csv), downsample

Admin Endpoints

Method Endpoint Role required Description
POST /v1/data/admin/users project_admin+ Create user in Keycloak and assign to project
GET /v1/data/admin/users project_admin+ List users
DELETE /v1/data/admin/user-project project_admin+ Remove user from project
POST /v1/data/admin/projects global_admin Create new project
GET /v1/data/admin/projects project_admin+ List projects
DELETE /v1/data/admin/projects/{name} global_admin Soft delete project
POST /v1/data/admin/projects/{name}/restore global_admin Restore deleted project
GET /v1/data/admin/activities project_admin+ Audit log
POST /v1/data/auth/change-password any Change temporary password

Error Responses

All API errors return a consistent JSON format:

{
  "detail": {
    "error": "UPPER_SNAKE_CODE",
    "detail": "Human-readable message",
    "timestamp": "2026-03-31T20:00:00Z"
  }
}
HTTP Error Code Meaning
400 INVALID_JSON Malformed JSON body
400 SCHEMA_VALIDATION_FAILURE Missing or invalid fields
400 CHECKSUM_MISMATCH SHA-256 checksum did not match
400 CSV_PARSE_ERROR CSV could not be parsed
400 UNSUPPORTED_FILE_TYPE File extension not allowed
401 UNAUTHORIZED Missing or invalid token
403 FORBIDDEN Insufficient role
404 NOT_FOUND Resource does not exist
409 DUPLICATE_BATCH Idempotency key already used
409 DUPLICATE_FILE File already exists in MinIO
413 PAYLOAD_TOO_LARGE Batch exceeds 10 MB limit
422 INVALID_TIMESTAMP_ORDER end_date_time ≤ start_date_time
422 MISSING_SAMPLING_RATE sampling_rate = 0 and cannot be inferred
429 RATE_LIMIT_EXCEEDED 100 requests/minute per token exceeded
500 STORAGE_ERROR MinIO / InfluxDB write failure
500 INTERNAL_SERVER_ERROR Unexpected server error

Rate Limiting

Endpoint Limit
POST /v1/data/batch 50 / minute
POST /v1/data/upload 20 / minute
All other endpoints 100 / minute

Multi-Project Architecture

Each project has its own PostgreSQL database (medwear_project1, medwear_project2, etc.).

  • Tables per project: measurement_batch, audit_log, user_project, deleted_files
  • Central postgres DB tracks soft-deleted projects in deleted_projects
  • global_admin can create and delete projects via the Admin tab or API
  • Grafana datasources are auto-provisioned for each project on startup and sync hourly

Soft Delete & Purge

All deletes are soft - data is marked deleted and permanently removed after 24 hours.

Resource Soft delete Hard delete
Batch (metadata + signals) is_deleted=true in PostgreSQL Purge removes PostgreSQL row + InfluxDB data
File Entry in deleted_files table Purge removes from MinIO + table
Project Entry in deleted_projects table Purge drops database

The purge loop runs every hour. Deleted items can be restored before the 24h window via the frontend or API.

Two-Factor Authentication (2FA)

Optional TOTP-based 2FA using any authenticator app (e.g. Google Authenticator).

Method Endpoint Description
GET /v1/auth/2fa/status Check if 2FA is enabled
POST /v1/auth/2fa/setup Generate secret and QR code
POST /v1/auth/2fa/enable Verify code and activate 2FA
POST /v1/auth/2fa/disable Verify code and deactivate 2FA
POST /v1/auth/2fa/verify Verify a TOTP code after login

GDPR / Privacy

MedWear implements GDPR compliance via /v1/data/gdpr/* endpoints. All write operations require project_admin or global_admin role.

Article Endpoint Description
Art. 7 POST /v1/data/gdpr/consent/{patient_id} Grant consent
Art. 7 §3 DELETE /v1/data/gdpr/consent/{patient_id}/{consent_type} Revoke consent
Art. 7 GET /v1/data/gdpr/consent/{patient_id} List all consents for a patient
Art. 17 DELETE /v1/data/gdpr/erasure/{patient_id} Right to erasure - cascades through PostgreSQL → InfluxDB → MinIO
Art. 20 GET /v1/data/gdpr/export/{patient_id} Data portability - exports all patient data as a JSON archive

ROS 2 Real-Time Bridge

Streams live biosignal data from ROS 2 topics directly into the MedWear API.

Signal Message type Unit
EEG healthcare_msgs/msg/EEG uV
EDA healthcare_msgs/msg/EDA uS
HR healthcare_msgs/msg/HeartRate bpm
HRV healthcare_msgs/msg/HeartRateVariability ms
Run with Docker
docker build -t medwear-ros2-bridge ./ros2_to_medwear

docker run --rm \
    --network host \
    -e MEDWEAR_USERNAME=<keycloak_user> \
    -e MEDWEAR_PASSWORD=<keycloak_password> \
    -e API_BASE_URL=https://<server-ip>/v1 \
    medwear-ros2-bridge \
    --config config.yaml \
    --device corsano-001 \
    --patient <patient_id> \
    --project <project_id>

Code Structure

medwear_med_api/
  medwear_med_api/
    routers/
      utils.py       - shared helpers
      batches.py     - batch query/delete/restore endpoints
      files.py       - MinIO file endpoints
      patients.py    - patient timeline endpoint
      admin.py       - user/project/activity management
      totp.py        - 2FA (TOTP) endpoints
    query_router.py
    batch_router.py  - data ingestion endpoints
    auth.py          - Keycloak JWT validation
    audit.py         - request audit logging middleware
    db.py            - per-project session factory
    models.py        - SQLModel table definitions
    grafana_utils.py - Grafana datasource auto-provisioning
    influx_reader.py - InfluxDB signal queries
    influx_writer.py - InfluxDB signal writes
  scripts/
    run_med_api.py
    test_phase3.py / test_phase4.py / test_phase5.py