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_admincan 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