Guide
Automating appointment reminders with Salesforce Flow
A practical build for confirmations and reminders that stay right when appointments move: the data model, the two ways to schedule, and how to test it safely.
Key takeaways
- Keep appointments on a record with a Date/Time start and a lookup to the person, then trigger everything from that record.
- Either wait in Flow with a scheduled path and send at that moment, or send straight away with a future Send At time and let ConnectSMS hold the text.
- Save the reminder's Message ID so a reschedule or cancellation can cancel it.
- Build idempotency keys from the record ID and the start time, so saving twice never sends twice.
- Don't ask customers to reply CANCEL or YES. Those are opt-out and opt-in keywords.
Plan the messages
Before opening Flow Builder, decide what the customer receives and when. Most appointment programs need four messages, and only one of them is scheduled:
| Message | When | How |
|---|---|---|
| Confirmation | When the booking is saved | Sent immediately |
| Reminder | 24 hours before, or the morning of | Scheduled |
| Change notice | When the time changes | Sent immediately; the old reminder is canceled and a new one scheduled |
| Cancellation notice | When the appointment is canceled | Sent immediately; the reminder is canceled |
Send them all from one number that your front desk watches. Customers reply to reminders ("Can I come at 11 instead?"), and those replies should reach a person who can act on them.
Choose a data model
Option A: a custom Appointment object
A small custom object gives you full control and keeps the Flow simple. A typical design:
| Field | Type | Purpose |
|---|---|---|
| Start__c | Date/Time | When the appointment starts |
| Contact__c | Lookup(Contact) | Who to text |
| Status__c | Picklist: Booked, Canceled, Completed | Drives the automation |
| Location__c | Text | Where to go |
| Time_Label__c | Text | The start time as the customer should read it |
| Reminder_Message_Id__c | Text(18) | The scheduled reminder, so it can be canceled |
It also makes reports such as Appointments tomorrow easy, which matters if you later want a daily list or outreach.
Option B: Salesforce Events
Events already hold a start time and a WhoId pointing to a Contact or a Lead, and they appear on the Salesforce
calendar. Record-triggered flows run on Events too. The trade-offs: WhoId can be either object, so handle both;
custom fields on Events are activity fields shared with Tasks; and recurring series and calendar-sync tools add cases to
test. If you use Salesforce Scheduler or Field Service, appointments are ServiceAppointment records, and the same patterns
apply.
Get the time right
Salesforce stores Date/Time values in UTC and shows them in each user's time zone. That makes one part easy and one part tricky.
-
When to send is easy. "24 hours before
Start__c" is an exact moment. It's right wherever the customer happens to be. -
What the text says is trickier. When automation turns a Date/Time into words, it formats it for the user
the automation runs as, in their time zone and locale, not the customer's. If that user is set to your business's time zone
and you only serve one, that's fine. Otherwise, fill a text field such as
Time_Label__cwhen the appointment is booked, or store a Date field plus a Time field; Salesforce Time fields don't shift with time zones. - "9 AM the day before" needs the customer's time zone; "24 hours before" doesn't. Start with the simpler rule unless you already store each customer's time zone.
Option 1: a record-triggered flow with a scheduled path
This is the standard Salesforce approach, and it works with any SMS action.
- Create a record-triggered flow on your appointment object that runs when a record is created or updated, with the entry condition
Status__c = Booked, set to run only when a record is updated to meet the condition requirements. - Add a scheduled path based on
Start__c, for example 1 day before. - On the path, get the appointment again and check it's still booked and still at the time you expect. Don't rely on the wait alone.
- Send the text with Send Text Message, leaving Send At blank so it goes now.
Strengths: no limit on how far ahead the appointment is, and the text is written with the data as it is when the path runs. Weaknesses: the pending reminder lives in Salesforce's scheduled-path queue, where front-desk staff can't see it on the customer's message history, and changes depend on how Salesforce re-evaluates pending paths when the record changes. Check Salesforce's scheduled path considerations before relying on it.
Option 2: schedule the text with Send At
With ConnectSMS you can also schedule the reminder at the moment the appointment is saved. The Send Text Message action has an optional Send At input. ConnectSMS stores the message as Scheduled and sends it in the first 15-minute slot at or after that time, up to 35 days ahead.
In the flow's immediate path, after the record is saved, set these inputs:
| Input | Value | Notes |
|---|---|---|
| Record ID | {!$Record.Id} | Needs your appointment object added in ConnectSMS Recipients, with a phone field such as Contact__r.MobilePhone. Or pass {!$Record.Contact__c} to text the Contact. |
| Template ID | Your reminder template | Merge fields are filled from the record. Or use Message with Fill In Merge Fields. |
| Business Number | Blank, or your appointments line | Blank uses the automation default number. |
| Send At | {!ReminderAt} | A future time, at most 35 days ahead. |
| Idempotency Key | {!ReminderKey} | A new start time gets a new key; a second save doesn't. |
| Client Reference | Optional, such as the appointment number | Stored on the message, never sent. |
// Record-triggered Flow on Appointment__c, after save
// Entry: Status__c = 'Booked' AND (ISNEW() OR ISCHANGED({!$Record.Start__c}))
ReminderAt (formula, Date/Time)
{!$Record.Start__c} - 1
ReminderKey (formula, Text)
{!$Record.Id} & '-reminder-' & TEXT({!$Record.Start__c})
CanSchedule (formula, Boolean)
{!ReminderAt} > NOW() && {!ReminderAt} < NOW() + 35
The action returns Success, Message ID, Status (Scheduled), Scheduled For, and
Error Code and Error Message when something is wrong. Save the Message ID in
Reminder_Message_Id__c, and add a decision on Success so a failure is logged or routed to someone.
A few rules to design around:
- Past times are refused. For a booking made less than a day ahead, the reminder time has already passed. The
CanSchedulecheck lets you skip it or send a single confirmation instead. - 35 days is the limit. For appointments further out, combine both options: a scheduled path 30 days before the start makes the Send At call.
- The text is filled in when it's scheduled. If the appointment details change, cancel the reminder and schedule a new one.
- It's visible. The reminder shows on the record's message history with the time it will send, and staff can cancel it there too.
Write the message
Keep reminder wording in a ConnectSMS template rather than in the flow, so changing a sentence doesn't need a deployment. For an appointment object, a reminder template might read:
Hi {!Appointment__c.Contact__r.FirstName|there}, a reminder of your appointment at {!Appointment__c.Location__c|our office} on {!Appointment__c.Time_Label__c}. Reply here if you need to reschedule.
- Fallbacks go after a vertical bar and are used when the field is empty: up to 80 characters, no braces.
- Leave some fields without one. A blank field without a fallback blocks the send, and for automation the request is recorded as Blocked with the reason. For the appointment time, that's what you want: no reminder is better than "your appointment on ."
- Preview it against a real appointment in the template editor before you activate it.
- Keep it to one segment: straight quotes, no emoji, a short location. Why that matters.
Handle reschedules and cancellations
A reminder for the wrong time is worse than no reminder. When the start time changes or the appointment is canceled, cancel the pending text with the Cancel Scheduled Text Message action, which takes one input, Message ID.
// Start__c changed, or Status__c changed to 'Canceled'
Decision Reminder_Message_Id__c is not blank?
Cancel Scheduled Text Message
Message ID {!$Record.Reminder_Message_Id__c}
Decision Success?
true // the old reminder is now Canceled
false // NOT_CANCELLABLE: it was already handed to Twilio
Decision Still booked?
Send Text Message // new Send At and a new Idempotency Key
Update Records Reminder_Message_Id__c = new Message ID
- Cancellation works until the message is handed to Twilio. After that it returns
NOT_CANCELLABLE, because a sent text can't be recalled; decide whether to send a correction. - The flow's running user must be allowed to cancel: the user who requested the message, a ConnectSMS admin, or someone with Send access to its number.
- With the scheduled-path approach there's nothing to cancel in ConnectSMS; the check on the path does the work.
Replies, quiet hours and opt-outs
Replies
A reply to a reminder joins the conversation for that business number and appears in the ConnectSMS inbox for everyone with access to the number. Staff with Send access answer from the same number, and the conversation is linked automatically when exactly one Contact, or failing that one open Lead, has the customer's number.
ConnectSMS acts on opt-out and opt-in keywords and nothing else, so other replies are for people to read. That makes the wording of your prompt important. "Reply CANCEL to cancel" opts the customer out of texts, because CANCEL is a standard opt-out keyword, and "Reply YES to confirm" can opt someone back in. Ask customers to reply in their own words.
Quiet hours
If your admin turns on quiet hours, automated texts that fall inside them are held until quiet hours end, in the business time zone. A reminder scheduled for 7:00 AM with quiet hours ending at 8:00 AM goes at 8:00 AM, and Scheduled For shows the later time. Staff messages and replies are never delayed, so choose reminder times outside quiet hours.
Opt-outs
ConnectSMS checks opt-outs when the reminder is scheduled and again when it's due. If the customer has already opted out,
Success is false with the error code OPTED_OUT. If they opt out after it was scheduled, the check at
send time blocks it. How opt-outs work.
Test in a sandbox
When a sandbox is created or refreshed from production, ConnectSMS turns sending off in the copy automatically until an admin turns it on there, so a refresh can't start texting real customers. Before you turn it on, make sure the test records carry your team's own phone numbers, and consider a separate Twilio number for testing. Then work through the cases:
- A booking two days out: the reminder shows as Scheduled with the right time.
- Saving the booking again without changes: no second reminder.
- Moving the start time: the old reminder is Canceled and a new one Scheduled.
- Canceling the appointment: the reminder is Canceled and the notice sent.
- A booking for tomorrow morning, whose reminder time has passed.
- A booking more than 35 days out.
- A test phone that has texted STOP: Success is false with
OPTED_OUT. - A reminder inside quiet hours: Scheduled For shows when they end.
- A reply from the test phone: it appears in the inbox.
Get Text Message Status returns the current status in a test flow, or check the timeline on the record. For every ConnectSMS action and input, see sending SMS from Salesforce Flow; for the front-desk side, see appointment texting.
