REDCap Integration¶
Seamless integration with REDCap for participant enrollment and data synchronization.
Overview¶
Metricis integrates with REDCap to:
- Import participants from REDCap projects
- Export assessment data to REDCap
- Sync bidirectionally for real-time updates
- Map event-battery associations for visit scheduling
- Support webhooks for automated triggers
Configuration¶
REDCap Setup¶
Configure REDCap connection in the portal:
{
"site_id": "hospital_a",
"redcap_url": "https://redcap.hospital.org/api/",
"api_token": "your-api-token-here",
"event_name": "baseline_arm_1",
"participant_id_field": "record_id",
"sync_enabled": true
}
Field Mapping¶
Map assessment metrics to REDCap fields:
{
"simple_rt_mean": "cognitive_rt_mean",
"simple_rt_sd": "cognitive_rt_sd",
"cpt_dprime": "cpt_sensitivity",
"cpt_beta": "cpt_criterion",
"nback_accuracy_1": "nback_1back_acc",
"nback_accuracy_2": "nback_2back_acc",
"nback_accuracy_3": "nback_3back_acc",
"digit_span_forward": "ds_forward_max",
"digit_span_backward": "ds_backward_max"
}
Participant Import¶
Manual Import¶
Automated Sync¶
Configure periodic sync (e.g., daily):
# Celery task
@celery_app.task
def sync_redcap_participants(study_id: str):
redcap_service = REDCapService(study_id)
new_participants = redcap_service.import_new_participants()
# Process and create participants in Metricis
Webhook Integration¶
REDCap Data Entry Triggers (DET) can notify Metricis of new enrollments:
# app/routers/redcap.py
@router.post("/webhook")
async def redcap_webhook(request: Request):
data = await request.form()
project_id = data.get("project_id")
record_id = data.get("record")
# Import new participant
await redcap_service.import_participant(project_id, record_id)
return {"status": "success"}
Data Export¶
Automatic Export¶
After assessment completion:
# app/services/redcap_sync.py
async def sync_session_to_redcap(session_id: str):
session = await get_session(session_id)
participant = await get_participant(session.participant_id)
redcap_config = await get_redcap_config(participant.study_id)
# Map task summaries to REDCap fields
redcap_record = {
redcap_config.participant_id_field: participant.participant_id,
"redcap_event_name": redcap_config.event_name,
}
for task_name, summary in session.task_summaries.items():
for metric_name, metric_value in summary.items():
redcap_field = redcap_config.field_mappings.get(f"{task_name}_{metric_name}")
if redcap_field:
redcap_record[redcap_field] = metric_value
# Export to REDCap
project = Project(redcap_config.redcap_url, redcap_config.api_token)
project.import_records([redcap_record])
Manual Export¶
Export selected sessions:
// Portal UI
const exportToREDCap = async (sessionIds: string[]) => {
await api.exportSessionsToREDCap(sessionIds);
};
Event-Battery Mapping¶
Map REDCap events to Metricis batteries:
{
"baseline_arm_1": "baseline_battery",
"month_3_arm_1": "followup_battery",
"month_6_arm_1": "followup_battery",
"month_12_arm_1": "final_battery"
}
When a participant reaches a REDCap event, Metricis automatically schedules the corresponding battery.
Data Synchronization¶
Pull from REDCap¶
Import data from REDCap:
def pull_from_redcap(study_id: str):
project = get_redcap_project(study_id)
# Get all records
records = project.export_records()
for record in records:
participant_id = record["record_id"]
# Update participant data in Metricis
Push to REDCap¶
Export data to REDCap:
def push_to_redcap(study_id: str, session_ids: list[str]):
project = get_redcap_project(study_id)
records = []
for session_id in session_ids:
session = get_session(session_id)
# Map session data to REDCap record
record = map_session_to_redcap(session)
records.append(record)
# Import all records at once
project.import_records(records)
Bidirectional Sync¶
Keep both systems in sync:
- Enrollment - REDCap → Metricis (via webhook or scheduled pull)
- Assessment completion - Metricis → REDCap (automatic export)
- Status updates - REDCap ↔ Metricis (bidirectional sync)
Multi-Site Support¶
Configure multiple REDCap instances:
# app/config.py
SITE_CONFIGS = {
"site_a": SiteConfig(
site_id="site_a",
name="Hospital A",
redcap_url="https://redcap.hospitala.org/api/",
token_env_var="REDCAP_TOKEN_SITE_A",
),
"site_b": SiteConfig(
site_id="site_b",
name="Hospital B",
redcap_url="https://redcap.hospitalb.org/api/",
token_env_var="REDCAP_TOKEN_SITE_B",
),
}
Each site can have different: - REDCap URLs - API tokens - Event names - Field mappings
Error Handling¶
Sync Failures¶
Track failed sync attempts:
{
"session_id": "sess_123",
"sync_attempts": 3,
"last_attempt": "2026-01-24T12:34:56Z",
"last_error": "REDCap API rate limit exceeded",
"status": "pending_retry"
}
Retry logic: - Exponential backoff (1min, 5min, 15min, 1hr) - Manual retry option in portal - Email notification after 3 failed attempts
Validation Errors¶
Handle REDCap validation errors:
try:
project.import_records([record])
except RedcapError as e:
if "value out of range" in str(e):
# Log validation error
logger.warning("redcap_validation_error", field=..., value=...)
# Create query in Metricis
await create_query(...)
Monitoring¶
Sync Status Dashboard¶
Portal displays REDCap sync status:
- Last sync timestamp
- Pending records
- Failed syncs
- Sync history
Logs¶
All REDCap operations are logged:
{
"timestamp": "2026-01-24T12:34:56Z",
"operation": "export_session",
"session_id": "sess_123",
"participant_id": "P001",
"status": "success",
"records_exported": 1,
"duration_ms": 234
}
Security¶
API Token Management¶
- Tokens stored as environment variables
- Never exposed in logs or UI
- Separate tokens per site
- Regular token rotation recommended
Data Privacy¶
- Only export aggregate summaries (not raw trials)
- Configurable field mapping (exclude sensitive fields)
- Audit trail of all exports
Testing¶
Sandbox Mode¶
Test REDCap integration without affecting production:
Mock REDCap Server¶
Use mock server for development:
# tests/mocks/redcap.py
class MockREDCapProject:
def export_records(self):
return [
{"record_id": "001", "study_id": "STUDY-001"},
{"record_id": "002", "study_id": "STUDY-001"},
]
def import_records(self, records):
return {"count": len(records)}
Next Steps¶
- Validation & Queries - Data quality management
- Data Entry - Manual data entry
- Cognitive Assessments - Automated capture
- Server Architecture - Technical details