TL;DR – Stop chasing no‑shows and post‑visit tasks manually. With OhMD’s Special Delivery API you can trigger a series of scheduled, consent‑aware texts the moment an appointment ends—collect feedback, push care‑plan reminders, and drive revenue cycle touch‑points automatically.
1. What Counts as a “Patient Follow‑Up”?
Goal | Typical Timing | SMS Example |
---|---|---|
Satisfaction Survey | 1 hour after visit | “How was your visit with Dr. Oh? Reply 1‑5.” |
Medication Reminder | Same night | “Remember to start amoxicillin tonight. Reply DONE when taken.” |
Physical Therapy Video | +24 h | “Ready to stretch? Watch your Day‑1 video → {{link}}” |
Invoice Reminder | +7 d | “Your copay is ready. Pay securely → {{pay‑link}}.” |
Each is just a timed SMS tied to the same encounter—perfect for automation.
2. Architecture Overview
EHR / Practice Mgmt ──▶ OhMD API ──▶ Carrier ──▶ Patient SMS
▲ │
│ └─◀─ Webhooks (SOLICITED_RESPONSE_RECEIVED, MESSAGE_STATUS_CHANGE)
└────── Encounter completed (event)
- EHR fires an “appointment complete” event to your service.
- Your service calls responseSolicitationStart to create the follow‑up workflow.
- OhMD sends timed messages; replies post to the same channel.
- Webhooks feed responses/results back to the EHR.
3. Prereqs
- Patient has SMS consent (see previous consent post).
- You know the appointment’s external ID and end-time.
- You’ve created any custom calendars (e.g. CLINIC_HOURS).
4. Kick‑Off the Workflow (responseSolicitationStart)
POST /api/responseSolicitationStart
{
"event": {
"eventType": "APPOINTMENT",
"timelineType": "REQUEST", // follow‑ups begin after event
"eventDate": "2025-06-27T15:00:00-04:00",
"identifier": {"id": "APPT_47832", "authority": "EXTERNAL"},
"owner": {"identifier": {"id": "0188bf4c-bd7d-…"}}
},
"workflow": {
"name": "POST_VISIT_FOLLOW_UP",
"steps": [
{
"offset": {"hours": 1},
"function": "messageDispatch",
"body": {
"recipient": [{
"patientIdentity": {"identifier": {"id": "2000"}},
"device": {"phone": {"number": "+16464624000"}},
"destinationCode": "SMS"
}],
"body": {"actualBody": "How was today’s visit? Reply 1‑5."},
"consentAgreementCode": "SMS",
"schedule": {"calendarCode": "FEDERAL_TCPA"}
}
},
{
"offset": {"hours": 24}, // 24 h after event
"function": "messageDispatch",
"body": {
"recipient": [{"patientIdentity": {"identifier": {"id": "2000"}}}],
"body": {"actualBody": "Watch your rehab video → {{link}}"},
"trackableLinks": [{"url": "https://rehab.example.com/v/abc"}],
"consentAgreementCode": "SMS",
"schedule": {"calendarCode": "FEDERAL_TCPA"}
}
}
]
}
}
Why use responseSolicitationStart? It ties every outbound message and inbound reply to the same workflowId, simplifying audit & analytics.
5. Capture Patient Replies (SOLICITED_RESPONSE_RECEIVED Webhook)
{
"workflow": {"identifiers": [{"id": "3018fa3a-49…", "authority": "INTERNAL"}]},
"message": {
"message": {"body": {"actualBody": "5"}}, // patient’s rating
"previousMessage": {…}
},
"presumedEvent": {"eventType":"APPOINTMENT", "eventDate":"2025-06-27T15:00:00-04:00"}
}
Use the workflow.identifiers[0].id to correlate the 5‑star reply back to the appointment.
6. Error‑Handling & Edge Cases
Situation | Response |
Patient opts out (STOP) | Consent flips to DENY; remaining workflow steps auto‑cancel. |
Appointment canceled | Call workflowCancel with workflowId to halt messages. |
After‑hours end time | Offsets queue relative to next open time defined by calendar. |
7. Observability: Track Every Step
- MESSAGE_STATUS_CHANGE — delivered, failed, queued.
- WORKFLOW_STATUS_CHANGE — started, completed, canceled.
Store these in your analytics DB for cohort analysis (e.g., NPS vs. response rate).
8. Developer Checklist
- Create one workflow template per follow‑up type (survey, PT, billing).
- Always set schedule.calendarCode for each step.
- Subscribe to both SOLICITED_RESPONSE_RECEIVED & CONSENT_CHANGE_DEFAULT webhooks.
- Build a cron that retries stuck QUEUED messages if network issues occur.
- Use x-ohmd-testflight:true to dry‑run workflows in staging.
Next Steps
- Prototype the JSON above in Postman (sandbox key).
- Visualize workflows in your dashboard using workflow.identifiers.
- Go live and start measuring decreased no‑shows & higher CSAT in days.
Automated follow‑ups turn every visit into an ongoing conversation – and you ship it with < 50 lines of code.