How a reminder reaches the customer
The reminder picked a move — first reminder, second reminder, or gap alert. Now the dispatch Lambda has to figure out who to send it to, on what channel, at what time of day, and with what context attached. Get any of those wrong and the reminder is worse than no reminder: a 6am text, a generic “you have an appointment,” a message to a number the customer changed a year ago. Four small guardrails sit between the move and the actual send.
Key takeaways
- Channel choice: text if a phone is on file, email if not; a gap alert goes to staff in Slack instead.
- Text is the default for customers; email is the fallback when no phone is set.
- Quiet hours and holiday calendars defer reminders to the next sensible hour.
- Every reminder ships with the service, date, time, staff member, link, and one-tap reply links.
- A gap alert is the one move that goes to staff, not the customer — so an opening gets filled.
Four guardrails on every dispatch
Gate 1: pick the channel
The first thing the dispatch Lambda decides is who the message is even for. A first or second reminder is for the customer. A gap alert is for the front desk. Those go to completely different places, so the channel choice forks here.
For a customer reminder, the dispatch looks up the row’s phone number. If one is set, the reminder is a text — texts get read within minutes and feel personal, which is what you want for an appointment nudge. If no phone is on file, it falls back to email so nobody slips through. For a gap alert, the message goes to a configured Slack channel where staff watch the day’s bookings; it never goes to the customer, because a gap alert is an internal “this slot might open” signal, not something the customer should see.
Gate 2: quiet hours
The reminder runs hourly, so a window can come due at any hour — including the middle of the night. A 48-hour window on a 7am appointment comes due at 7am two days before, which is fine. But a 2-hour window on a 10pm appointment would come due at 8pm, and a tight schedule could line up a send for after the customer’s bedtime.
Gate 2 reads the rules doc’s quiet-hours setting (default 9pm to 8am, configurable per business). If the current local time is in the quiet window, the dispatch creates a one-off EventBridge Scheduler rule that fires at the next allowed minute and exits without sending. The Scheduler invokes the same dispatch Lambda with the same payload at the deferred time, where Gate 2 will let it through. A reminder that can’t be sent before the appointment itself (because the appointment is inside quiet hours) is dropped and logged rather than waking the customer.
Gate 3: holiday calendar
The rules doc lists the days you’re closed — either a static list (“public holidays, the week between Christmas and New Year...”) or a reference to a Google Calendar that holds them. Gate 3 checks the current local date against that list and, if it’s a closed day, defers the reminder to the next open day.
The list is on purpose — the reminder won’t auto-detect a country’s public holidays for you. The failure modes are very different. A closed day you forgot to add sends a reminder for an appointment that can’t happen. A day in the list that you’re actually open just delays a reminder by a day, which is fine. The trade-off favors keeping the list explicit.
Gate 4: compose with full context, then ship
The voice doc has one message template per service: a short message with placeholders for the service, the date and time, the staff member, and a link to the booking. The dispatch Lambda fills the placeholders, adds three short one-tap links — Confirm, Reschedule, Cancel — and ships the message via SNS for a text or SES for an email. The reply links point at a Function URL that records the customer’s choice; Part 5 covers what each one does.
Text messages are short by nature, so the template is tight: “Hi {name}, reminder: {service} with {staff}, {day} {time}. Confirm: {link} · Reschedule: {link} · Cancel: {link}.” Email gives a little more room for the booking details and a clearer set of buttons, but carries the same fields and the same three links.
A gap-alert move composes differently: it’s a staff message, so it names the customer, the service, the time, and how long until the appointment, plus a note on why it fired (“cancelled” or “2 hours out, still unconfirmed”). The point is to hand the front desk everything they need to decide whether to call the customer or release the slot.
Every dispatch — text, email, or gap alert — writes a row to ar-sends in DynamoDB. The next tick reads that row and knows not to send the same window again.
Why the guardrails exist
None of these gates are exotic. They’re the kind of small care a thoughtful receptionist would take if they were sending the reminders by hand — text the people who text, don’t message at 6am, skip the days you’re closed, include enough detail that the customer doesn’t have to call back to ask what time. Putting them in code as four small sequential gates makes them part of the design, not a feature you’re trusting the writer of any one message to remember.
Next post: how a customer confirms, reschedules, or cancels once a reminder lands — how the list updates, how the chain resets, and how the front desk sees the change.
All posts