How an appointment gets on the list
The reminder only reminds about what’s on the list. So the first job is making sure the list actually reflects what your business has booked. There are three ways an appointment gets on: it lands in the Drive sheet from your booking tool or by hand, somebody forwards a booking-confirmation email to a dedicated address, or somebody puts the appointment on a Google Calendar with a small tag. The first one is obvious. The other two exist because in real life bookings come in by email and on calendars, not as tidy spreadsheet rows.
Key takeaways
- Three intake lanes feed one list: the Drive sheet, an inbox-forwarding lane, and a calendar import.
- Inbound booking emails are parsed by Textract; Bedrock Haiku 4.5 reads the text and proposes a row.
- Every parsed row goes to the front-desk Slack for one-tap approval before it lands on the list.
- Calendar events tagged
#apptget pulled hourly via the Google Calendar API. - The list stays the canonical store. The other lanes are conveniences that write into it.
Three lanes into one list
Lane 1: the Drive sheet itself
The simplest lane. The booking lands as a row in the appointment sheet — either typed by the front desk, or pushed in by an export from your booking tool. The columns are short: customer name, phone, email, service, staff member, date, time, status, and a link to the booking. A small Lambda — drive-sync — runs every few minutes, exports the sheet as plain CSV via the Drive API, and writes it to s3://ar-appointments-source/appointments.csv if the sheet has changed since the last sync. The reminder reads from S3, not Drive directly. That keeps Drive API calls predictable and gives you S3 versioning for free, so a bad bulk-edit can be rolled back in one click.
This lane covers the bulk of bookings: most businesses already have a tool that knows the appointment, and getting its rows into one sheet is the smallest possible integration. Everything the reminder needs lives in those few columns.
Lane 2: inbox forwarding (for the one-off bookings)
Set up a dedicated inbound address — something like bookings@your-company.com — via Amazon SES. Anyone on the team forwards a booking-confirmation email to that address and the reminder takes it from there. SES writes the raw MIME to s3://ar-raw-mime/. The S3 PUT triggers a parser Lambda. The Lambda walks the MIME tree to the body text and any attachment, runs Amazon Textract if there’s a PDF or image (Textract reads PDF, PNG, JPEG, and TIFF natively), and gets back the extracted text plus any tables.
Then a Bedrock Haiku 4.5 call reads the text and emits a structured row: customer name, phone, email, service, staff member (if named), and the date and time. The model prompt is short: “Extract a row for the appointment list. Return JSON only. Mark each field with a confidence score. Do not invent a time that isn’t in the text.” The output goes to a small Slack interactive message that pings the front desk: the proposed row, the confidence per field, and three buttons — approve, edit, discard. On approve, a Lambda writes the row to the Drive sheet via the Sheets API. On edit, the desk gets a fillable modal pre-populated with the proposal. On discard, the message is logged and the email moved to a discarded prefix in S3 for audit.
The reason every parsed row goes to a human first is simple: a wrong appointment time the model misread is worse than a booking that never made it onto the list. The misread one will text the customer to show up at the wrong hour — which annoys them and burns your goodwill.
Lane 3: calendar import
Some teams already keep appointments on a shared calendar. The mobile groomer runs her whole week off a Google Calendar. The physiotherapist blocks slots on a clinic calendar. The tutor keeps sessions on a personal one. Forcing those teams to also type rows in a sheet is a fight you don’t need to have on day one.
Lane 3 picks up calendar events tagged with #appt in the description. A small calendar-sync Lambda runs hourly, iterates through the configured Google Calendars (using a service-account credential stored in Secrets Manager), and pulls any tagged events whose start time is in the future. Each pulled event becomes a proposal in the same Slack flow as Lane 2 — one-tap approve to add to the list. Once approved, the calendar event can stay where it is; the list now owns the reminder for it.
Calendar import is the most opt-in of the three lanes. A team that doesn’t use it loses nothing; a team that does avoids retyping appointments they already put on a calendar.
Why the list stays the source of truth
Three lanes in, but only one place where the reminder actually looks. That’s a deliberate constraint. If two lanes both wrote directly to the reminder’s state, every “why did this text go out?” question would mean checking three places. Funneling everything through the Drive sheet means there is exactly one row per appointment, and any staff member can read or edit any of it without learning a new tool. The convenience lanes are first-class for getting bookings in, but they always pass through the sheet on the way.
Next post: how the reminder actually reads the list, computes hours-to-appointment, and picks one of four moves.
All posts