Blog /
Two-way appointment confirmations on Medplum, in five minutes
Two-way appointment confirmations on Medplum, in five minutes.
A patient gets a confirmation, replies in their own words, and the appointment gets rescheduled — without anyone in your office picking up the phone:
VisitConfirmed: Hi Maria, this is VisitConfirmed reaching out for your cardiology follow-up with Dr. Patel at Mass General on Thursday at 2:00 PM. Reply YES to confirm or let me know if you need a different time.
Maria: thursday is bad for me, can I do friday morning?
VisitConfirmed: I have openings with Dr. Patel on Friday: 9:30 AM, 10:15 AM, or 11:00 AM. Which works?
Maria: 10:15
VisitConfirmed: Done — your cardiology follow-up with Dr. Patel is moved to Friday at 10:15 AM. You'll get a calendar invite shortly.
Behind the scenes, the original Appointment flipped to cancelled,
a new Appointment was created at the new time, and a Communication
resource captured the full thread on Maria's patient record. Your
care team sees the schedule change in Medplum the moment Maria does —
no inbox to triage, no phone tag, no manual EHR entry.
That's what you get in five minutes:
- SMS the moment an Appointment is created, with real conversation handling for reschedules, cancellations, and "I'm running late"
- Voice escalation when SMS goes unanswered
- Every outcome written back to Medplum as proper
Communication,Task, orAppointment.statusupdates — your care team sees the same source of truth they already use - Timezone-aware quiet hours, retry windows, consent handling, and idempotent write-back that respects human edits
Medplum stays the source of truth. VisitConfirmed handles the engagement layer. No code in your project. One command. Let's get you set up.
Step 1 — Sign up for VisitConfirmed and grab your API key
Go to visitconfirmed.com and click Get Started Free. No credit card. The Pilot tier is free for thirty days and covers up to 500 appointments — plenty to validate the integration with real patients before deciding whether to upgrade. Full pricing is on the pricing page.
Once you are signed in, the dashboard's first screen is Connect Medplum. You see your VisitConfirmed API key and the connection command on this screen. Keep the key handy.
Step 2 — Run the connection command
In your terminal:
npx @visitconfirmed/medplum
The command prompts for two things:
- Your VisitConfirmed API key (paste it from the dashboard).
- Your Medplum base URL. The default is
https://api.medplum.com. For self-hosted Medplum, enter your URL.
It then verifies your Medplum CLI login. If medplum login is
current, you are good. If not, the tool tells you what to run and
exits cleanly.
The command itself takes a few seconds. The
@visitconfirmed/medplum package is on npm
and the source is on GitHub
if you want to see exactly what it does before running it.
Step 3 — What the command provisioned
Everything the tool creates lives in your Medplum project, where you can inspect, edit, or delete it.
An AccessPolicy that scopes VisitConfirmed to exactly the FHIR resources it needs:
{
"resourceType": "AccessPolicy",
"name": "VisitConfirmed Integration",
"resource": [
{ "resourceType": "Appointment", "readonly": false },
{ "resourceType": "Patient", "readonly": true },
{ "resourceType": "Practitioner", "readonly": true },
{ "resourceType": "Location", "readonly": true },
{ "resourceType": "Communication", "readonly": false },
{ "resourceType": "Task", "readonly": false },
{ "resourceType": "Subscription", "readonly": false }
]
}
Read access for context — who is the patient, which doctor, where the appointment is. Write access for the outcomes VisitConfirmed produces: status updates, message logs, escalation tasks. Nothing more.
A ClientApplication bound to that policy. Medplum returns a fresh Client ID and Client Secret. The tool sends those credentials directly to VisitConfirmed's API, authenticated with your VisitConfirmed API key — no manual copy-paste, and your Medplum admin password never leaves your machine.
A Subscription that fires when an Appointment is created or updated:
{
"resourceType": "Subscription",
"status": "active",
"reason": "VisitConfirmed appointment confirmations",
"criteria": "Appointment?status=pending,proposed",
"channel": {
"type": "rest-hook",
"endpoint": "https://visitconfirmed.com/api/medplum/fhir-appointment/",
"header": ["X-Medplum-Api-Key: <token-issued-to-your-org>"]
}
}
The criteria field uses standard FHIR search syntax, so you can
scope the trigger as tightly as you want. Filter by practitioner,
location, appointment type — anything Medplum indexes. You can edit
the Subscription, pause it, or delete it from the Medplum admin UI at
any time.
Step 4 — Watch a real confirmation flow through
Open Medplum admin and create a test Appointment with your own phone number on the Patient resource. Within a few seconds, you receive an SMS:
Hi Sarah, this is VisitConfirmed reaching out for your cardiology follow-up with Dr. Patel at Mass General on Thursday at 2:00 PM. Reply YES to confirm or RESCHEDULE if you need a different time.
Reply YES. Within another few seconds, two things happen in your Medplum project.
The Appointment status updates from pending to booked:
{
"resourceType": "Appointment",
"id": "...",
"status": "booked",
"...": "..."
}
A Communication resource appears, capturing the SMS thread:
{
"resourceType": "Communication",
"status": "completed",
"category": [{
"coding": [{
"system": "http://hl7.org/fhir/communication-category",
"code": "notification"
}]
}],
"subject": { "reference": "Patient/..." },
"about": [{ "reference": "Appointment/..." }],
"sent": "2026-04-29T15:32:14Z",
"received": "2026-04-29T15:33:02Z",
"payload": [
{ "contentString": "Hi Sarah, this is VisitConfirmed..." },
{ "contentString": "YES" }
]
}
That is the full loop. Subscription fires, VisitConfirmed reaches out, patient responds, Medplum gets updated. No code on your side.
If the patient does not respond, retries are spaced according to your
quiet-hours and timezone settings. If they still do not respond by the
configured threshold, AI voice escalation kicks in on the Scale tier
and a real conversation happens — with the full transcript written
back as a Communication.
What gets written back
The write-back is what makes this FHIR-native instead of "a reminder service that calls an API."
| Patient outcome | What VisitConfirmed writes to Medplum |
|---|---|
| Confirmed | Appointment.status → booked, plus a Communication capturing the conversation |
| Cancelled | Appointment.status → cancelled, plus a Communication |
| No answer or at-risk | Task assigned to your care team, plus a Communication log of attempts |
| Rescheduled | Old Appointment cancelled, new Appointment posted, plus a Communication |
These are standard FHIR resources. They show up in patient histories, they feed into your existing dashboards, and they let your care team see what happened in the same UI they already use.
The write-back is also idempotent. If you edit an Appointment manually after VisitConfirmed has already updated it, your edit wins. VisitConfirmed never overwrites a human change.
Security boundary
Three things worth being explicit about.
Your Medplum credentials never reach VisitConfirmed. The npx tool runs on your machine, uses your local Medplum CLI authentication, and only sends the output of provisioning — a single ClientApplication's credentials — to VisitConfirmed.
VisitConfirmed only ever has the scopes you granted. Delete the AccessPolicy or revoke the ClientApplication in Medplum and VisitConfirmed loses access cleanly. There is no other channel.
Everything is auditable in Medplum. The Subscription, AccessPolicy, and ClientApplication all live in your project. Medplum's AuditEvent records every read and write. You see exactly what VisitConfirmed has done.
VisitConfirmed is HIPAA-compliant with BAAs in place for SMS, email, and voice. PHI is handled appropriately end-to-end.
What if you do not want to run a CLI
The npx flow is the fastest path, but it is not the only one. If you prefer explicit credential control, or you cannot run Node on the machine that has Medplum CLI access, you can connect manually:
- In Medplum admin, paste the AccessPolicy JSON from above and save it.
- Create a new ClientApplication and attach the AccessPolicy.
- Copy the Client ID, Client Secret, and your Medplum base URL into the VisitConfirmed dashboard.
- VisitConfirmed creates the Subscription on your behalf and verifies the connection.
Same end state, more clicks.
Get started
Sign up free, run the npx command, and let real
appointments flow through for a few weeks. The full
Medplum appointment confirmation integration page
has more on positioning and architecture. The
@visitconfirmed/medplum package
is on npm; the full source — including the optional advanced Bot
path — is on GitHub.
Further reading
- Why appointment reminders are not appointment confirmations — the difference between "we sent the message" and "the patient is actually coming"
If you hit anything confusing during onboarding, the team is in Medplum's Discord and on email at hello@visitconfirmed.com.
Five minutes. One command. World-class appointment confirmations writing back to Medplum as proper FHIR.