Part 1 of 7 · Appointment reminder series ~5 min read

An appointment reminder on AWS for a few dollars a month

Every business that books time loses money to no-shows. The customer who booked a Tuesday cleaning three weeks ago and forgot. The new client who double-booked and isn’t sure which one they meant to keep. The patient who meant to call and reschedule but never got around to it, so the chair sits empty during a slot you could have sold. This post walks through the design of a small reminder that watches every upcoming appointment, nudges each customer at the right time, lets them confirm or reschedule or cancel with one tap, and tells your front desk who went quiet so the gap can be filled early.

Key takeaways

  • Three sources for booked appointments: a Drive list, an inbox forwarding lane, and a calendar import lane.
  • Every appointment ends in one of four moves on each tick: scheduled, first reminder, second reminder, or gap alert.
  • Per-service rules: a dental visit might use a 48-hour and 2-hour reminder, a salon 24-hour and 1-hour.
  • Reminders go by text first, email as fallback, and respect quiet hours and your holiday calendar.
  • Designed on AWS for about $3/month at typical small-business volume.

The whole system on one page

Before any code, here’s the shape of what we’re designing.

System architecture: three sources, three pieces inside AWS At the top, three external boxes in a row. Far left, "Appointments" — a Google Drive list of every booked appointment, plus an inbox forwarding lane and a calendar import lane that add new bookings to the list. Centre, "Rules and voice" — a Drive folder with a rules doc covering per-service reminder schedules, channels, and quiet hours, plus a voice doc with the reminder message templates. Far right, "Customers" — the people with upcoming appointments; reminders land in their text messages or, if no phone is set, their email. Each connects via an arrow to the AWS account container below. Appointments have an outgoing arrow into AWS. Rules and voice feed in to ground every reminder. Customers receive reminders with the service, the date and time, the staff member, a link to the booking, and one-tap confirm, reschedule, and cancel links. Inside the AWS account are three components in a row, mirroring the layout above. On the left, the Appointment intake — receives bookings from each source, parses inbound booking emails via Textract and Bedrock, and writes the cleaned appointment into the list. In the middle, the Reminder — runs hourly; reads the list; computes hours-to-appointment per booking; picks one of four moves: scheduled, first reminder, second reminder, or gap alert. On the right, the Dispatch — sends the reminder via text or email, respects quiet hours and the holiday calendar, and tracks the reply per appointment. Internal arrows flow left to right. A note at the bottom reads: every reminder leaves with full context — the system never just says "you have an appointment." Appointments list, inbox, calendar Rules and voice schedules, channels, templates Customers where reminders land bookings in grounds reminder with context AWS account Appt intake parse, normalize, add to list Reminder picks one of four: scheduled, 1st, 2nd, gap Dispatch text or email, respects quiet hours appt move Every reminder leaves with full context — the system never just says “you have an appointment.”
Fig 1. Three sources outside, three pieces inside AWS. Bookings flow in from a Drive list, an inbox forwarding lane, and a calendar import lane. The Reminder runs hourly and picks one of four moves. Dispatch sends the right reminder to the right person at the right time.

What you set up once (the outside)

  • Appointments. A Google Sheet in a Drive folder, one row per booking: customer name, phone, email, service (cleaning, consult, haircut, fitting, follow-up), staff member, date and time, status, and a link to the booking. Most appointments arrive here straight from your booking tool; new ones can also enter via two other lanes covered in Part 2 — an inbox-forwarding lane (forward a booking-confirmation email to a dedicated address and the reminder proposes a row for one-tap approval) and a calendar import lane (events tagged in Google Calendar with #appt get pulled in automatically).
  • A rules folder. Two short Google Docs in a Drive folder. The rules doc covers the reminder schedule for each service — how many hours ahead the reminder should send, and how many times. A dental visit might get a 48-hour and 2-hour reminder; a salon appointment 24-hour and 1-hour; a longer consult 72-hour, 24-hour, and 3-hour. The doc also lists the channel preference per customer (text or email), the quiet hours, the staff member who handles each service, and any holiday calendars to skip. The voice doc holds one reminder message template per service — what the text or email actually says.
  • Customers. The people with upcoming appointments. Each row has a phone number (so the reminder is a text) or, if no phone is set, an email address. Reminders land with the service, the date and time, the staff member, a link to the booking, and three one-tap links — “Confirm,” “Reschedule,” and “Cancel” — that stop further reminders or update the booking.

What runs on every tick (the inside)

  • The appointment intake. Three sources feed the list. The Drive sheet itself is the canonical store. New bookings can also be added via the inbox forwarding lane (forward a confirmation email to bookings@your-company.com, the reminder uses Textract to read it and Bedrock Haiku 4.5 to extract customer, service, date, and time, then drops a one-tap approval card in the front-desk Slack to confirm before the row is added) and the calendar import lane (events tagged #appt get pulled hourly by a small sync Lambda).
  • The reminder. Runs once an hour. Reads the list. For each upcoming appointment, computes hours-to-appointment. Compares against the per-service reminder schedule in the rules doc. Picks one of four moves. Scheduled: too far out for any reminder window — do nothing. First reminder: just crossed the first window — text the customer with full context. Second reminder: crossed a later window with no confirm — re-send, closer to the time. Gap alert: the customer cancelled, or the appointment is hours away and still unconfirmed — tell staff so the slot can be filled. The reminder itself doesn’t call a model on the hourly tick — the move logic is plain Python.
  • Dispatch. Reads the voice doc, formats the reminder for the chosen move and service, and sends it. Text messages go through SNS. Email goes through SES outbound. Both honor quiet hours (no reminders between 9pm and 8am local by default) and the holiday calendar (no reminders on configured days). Every dispatch writes a row in DynamoDB so the next tick can tell whether the customer replied. A weekly staff summary writes a short paragraph: appointments confirmed, customers who went quiet, and the slots that opened up.

In plain words

Mr. Tan booked a 9am dental cleaning three weeks ago. The rules doc says dental cleanings get a 48-hour text and a 2-hour text. Two days out, at a sensible hour, the reminder texts him: “Reminder: cleaning with Dr. Lim, Thursday 9:00am. Reply to confirm, reschedule, or cancel.” Mr. Tan taps Confirm. The reminder marks the appointment confirmed and stops — he won’t get the 2-hour text, because he already said he’s coming. If instead he taps Reschedule, a short page lets him pick a new time; the list updates and a fresh reminder chain starts against the new slot. If he taps Cancel, the slot is freed and the front desk gets a heads-up so they can offer it to someone on the waitlist. And if he simply goes quiet — no tap at all — the 2-hour reminder still goes out, and if he’s still silent the morning of, staff see him flagged on the daily list as “unconfirmed.”

The cost of running this is about $3 a month at SMB volume. The cost of not running it is the empty chair on a Thursday morning — the slot you could have sold twice over if you’d known the night before that it was opening up.

Design rules that shaped every decision

  • Every reminder ships with full context — service, time, staff member, link to the booking. The customer never has to dig.
  • Four moves, always. Scheduled, first reminder, second reminder, gap alert. There is no fifth.
  • Quiet hours and holidays are respected. A 6am text is worse than no text; bad timing burns goodwill.
  • Confirm stops further reminders for that appointment. Reschedule updates the list and resets the chain.
  • The list lives in Drive. Adding a booking, changing a time, or fixing a phone number doesn’t need a deploy.
  • Every dispatch and reply is logged. Look back at a no-show next month and you can see every reminder that went out.

Why this shape

Most businesses handle reminders one of three ways: the front desk calls everyone the day before, the booking tool fires a single generic reminder, or nobody reminds anyone and you eat the no-shows. The phone calls work until the desk is busy — which is exactly when the no-shows pile up. The single generic reminder is better, but it has no memory: it can’t tell a customer who already confirmed from one who hasn’t, so it either over-sends or under-sends. And no reminders at all is just paying for empty chairs.

The setup above keeps the booking list in a doc the team already edits, but adds a small system that looks at that list every hour and acts only when something needs acting on. Reminders come at the right time for the service. They carry enough context that the customer doesn’t have to call to ask what or when. They let the customer reply in one tap. And they stop the moment somebody confirms. The reminder is invisible most of the time; visible only when it actually saves you a no-show.

The next four posts walk through each piece in turn: how an appointment gets on the list, how the reminder knows when to send, how a reminder reaches the customer, and how a customer confirms, reschedules, or cancels. One diagram per post. A cost breakdown and a final engineering reference at the end.

All posts