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”?

GoalTypical TimingSMS Example
Satisfaction Survey1 hour after visit“How was your visit with Dr. Oh? Reply 1‑5.”
Medication ReminderSame 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)
  1. EHR fires an “appointment complete” event to your service.
  2. Your service calls responseSolicitationStart to create the follow‑up workflow.
  3. OhMD sends timed messages; replies post to the same channel.
  4. 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

SituationResponse
Patient opts out (STOP)Consent flips to DENY; remaining workflow steps auto‑cancel.
Appointment canceledCall workflowCancel with workflowId to halt messages.
After‑hours end timeOffsets 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

  1. Prototype the JSON above in Postman (sandbox key).
  2. Visualize workflows in your dashboard using workflow.identifiers.
  3. 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.