{# Organization + WebSite structured data. Child pages can add their own JSON-LD in `extra_head` without overriding these site-wide entries. #} Skip to main content

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, or Appointment.status updates — 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:

  1. Your VisitConfirmed API key (paste it from the dashboard).
  2. 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.statusbooked, plus a Communication capturing the conversation
Cancelled Appointment.statuscancelled, 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:

  1. In Medplum admin, paste the AccessPolicy JSON from above and save it.
  2. Create a new ClientApplication and attach the AccessPolicy.
  3. Copy the Client ID, Client Secret, and your Medplum base URL into the VisitConfirmed dashboard.
  4. 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

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.