Patient engagement is one of the most persistent challenges in clinical trial delivery. Missed follow-ups, unanswered questionnaires, and poor appointment adherence directly affect data completeness and trial outcomes. Most clinical trial platforms — including REDCap — are built for data capture, not patient communication. This means research teams typically manage participant contact manually, through phone calls or paper reminders, creating inconsistent processes and significant staff burden.
This middleware bridges that gap. It connects REDCap directly to SMS providers, enabling automated outbound question delivery to trial participants and routing their replies back into REDCap records without manual intervention. It was developed and used in production at a UK academic clinical trials unit to support longitudinal follow-up studies where daily patient-reported outcomes were required.
One-way and Two-way SMS sending (SMS Works / FireText)
Automates daily participant follow-up messaging for REDCap projects. The middleware creates daily repeating instances, sends questions (q1a→q5b), ingests replies, saves answers back to REDCap, and drives the sequence automatically with reminders and morning auto-heal.
- REDCap baseline saved → new follow-up instance created
(key baseline fields:mob_number,date_baseline) - DET triggers → calls
api/send_outbound.php - System sends SMS via configured provider (SMS Works / FireText)
- Participant replies → provider posts to
api/send_inbound.php - PHP saves answer & sends next question until q5b is completed for the day
| Area | What it does |
|---|---|
| One-way sending | Sends daily questions and reminders |
| Two-way messaging | Parses inbound (1–10, HELP, STOP), saves answers, advances sequence |
| Auto-heal | Morning window catch-up for missed q1a |
| Reminders | 3-hour default reminder; gated by time window |
| DLR handling | Delivery receipts update provider msg ID and sent/delivered/failed status |
| Scheduling | Nightly instance creation + hourly heartbeat to guarantee runs |
| Secrets hygiene | All credentials live in secure/secrets.php (git-ignored) |
| Provider-agnostic | Works with SMS Works or FireText; select via $PROVIDER in config.php |
| Path / File | Purpose |
|---|---|
api/config.php |
Central configuration (no secrets) |
secure/secrets.php |
git-ignored secrets: REDCap token/URL, provider keys, VMNs, static JWT |
api/send_outbound.php |
Outbound engine: evaluates q1a window, reminders, auto-heal; sends SMS |
api/send_inbound.php |
Inbound parser: validates/saves reply; re-invokes outbound to continue sequence |
api/scheduler_tomorrow.php |
Creates tomorrow’s repeating instances and assessment dates |
api/cron_hourly.php |
Hourly heartbeat wrapper (ensures pipeline runs even if DET/alerts fail) |
api/dlr_smsworks.php (etc.) |
Provider delivery-receipt callbacks → update REDCap status fields |
tasks/run_hourly_sms.xml |
Windows Task Scheduler definition (import to run hourly) |
logs/ |
Runtime logs (outbound.log, inbound.log, scheduler_tomorrow.log, dlr_*.log, cron_hourly.log) |
.gitignore should include:
/secure/,/logs/, and any other environment-specific outputs.
Create secure/secrets.php (not committed) with only these constant names:
<?php
define('REDCAP_API_URL_SECRET', 'https://your-redcap.example/api/');
define('REDCAP_API_TOKEN_SECRET', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX');
define('SMSW_API_KEY_SECRET', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx');
define('SMSW_API_SECRET_SECRET', 'yyyyyyyyyyyyyyyyyyyyyyyyyyyy');
// Optional static JWT (bypass live generation):
define('SMSW_STATIC_JWT_SECRET', ''); // or 'JWT eyJ...'
define('FIRETEXT_API_KEY_SECRET', 'zzzzzzzzzzzzzzzzzzzzzzzzzz');
define('SMSW_REPLY_NUMBER_SECRET', '4478XXXXXXX');
define('FIRETEXT_REPLY_NUMBER_SECRET', '44786XXXXXX');
define('SMSW_VMNS_SENDER_SECRET', '4478XXXXXXX');
define('FIRETEXT_VMNS_SENDER_SECRET', '44786XXXXXX');
define('CA_CERT_PATH_SECRET', null);
define('PROXY_HTTP_SECRET', null);
define('PROXY_HTTPS_SECRET', null);
$PROVIDER = getenv('SMS_PROVIDER') ?: 'smsworks'; // or 'firetext'
$TIMEZONE = 'Europe/London';
$BASELINE_EVENT = 'baseline_arm_1';
$FOLLOWUP_EVENT = 'followup__1_30_day_arm_1';
$FOLLOWUP_REPEAT_INSTR = 'goal_setting_assessments';
// q1a window & auto-heal window
define('Q1A_GUARD_START_HOUR', 7);
define('Q1A_GUARD_END_HOUR', 21);
define('AUTO_HEAL_WINDOW_START_HOUR', 7);
define('AUTO_HEAL_WINDOW_END_HOUR', 21);
// Reminders & HELP
define('REMINDER_ENABLED', true);
define('REMINDER_SECONDS', 3*3600);
define('REMINDER_WINDOW_START_HOUR', 8);
define('REMINDER_WINDOW_END_HOUR', 21);
define('HELP_AUTOREPLY_ENABLED', true);
define('HELP_RATE_LIMIT_MINUTES', 60);Tip: Copy
api/config.example.phptoapi/config.phpand update the values for your REDCap project structure before deploying.
This middleware processes personal data (mobile numbers) and potentially sensitive health information (patient responses). Before deploying in a clinical or research context, ensure the following are addressed:
- Consent: Participants must have explicitly consented to receive SMS communications as part of their study informed consent process. Do not send messages to participants who have not consented.
- Data minimisation: Only store the minimum data required. Mobile numbers should be stored in REDCap under appropriate access controls, not in application logs or config files.
- Retention: Define and enforce a retention period for SMS logs. The
logs/directory should be cleared regularly in line with your institution's data retention policy. - Audit trail: REDCap's built-in audit log captures all field-level changes made via the API. Ensure your REDCap project is configured to retain audit logs for the duration required by your ethics approval.
- STOP handling: The middleware parses inbound STOP replies and should update a REDCap field to flag opt-out. Ensure your study workflow stops sending messages immediately when a STOP is received.
- Data transfers: If your SMS provider is outside the UK or EU, ensure an appropriate data transfer mechanism is in place (e.g. Standard Contractual Clauses) as mobile numbers constitute personal data under UK GDPR.
This list is not exhaustive. Always consult your institution's Data Protection Officer and Research Ethics Committee before deploying in a live study.
This project is licensed under the MIT License — see the LICENSE file for details.
Last updated: April 2026