Emails each client booked for tomorrow a spoken reminder as an audio attachment, and never reminds the same booking twice.
Uses
Claude
Cal.comElevenLabs
Gmail
Google Sheets
Voice-note reminders for tomorrow's appointments
logSheetUrl to blank.Set logSheetUrl to blank.logTabName to Reminder log.Set logTabName to Reminder log.voiceId to blank.Set voiceId to blank.businessName to blank.Set businessName to blank.maxReminders to 30.Set maxReminders to 30.remindFromHours to 15.Set remindFromHours to 15.remindToHours to 39.Set remindToHours to 39.bookingsToScan to 100.Set bookingsToScan to 100.remindedBookingIds.Start an empty list and save it as remindedBookingIds.logReadErrorIf a step below fails, save the error as logReadErrorTry
remindedBookingIds to the values from the Google Sheets โRead column by numberโ step.Change remindedBookingIds to the values from the Google Sheets โRead column by numberโ step.On error
bookingSearch.List bookings. Save it as bookingSearch.bookings to bookingSearch.bookings.Set bookings to bookingSearch.bookings.sentCount to 0.Set sentCount to 0.skippedAlreadyReminded to 0.Set skippedAlreadyReminded to 0.skippedNoEmail to 0.Set skippedNoEmail to 0.skippedIncomplete to 0.Set skippedIncomplete to 0.skippedUnverifiedScript to 0.Set skippedUnverifiedScript to 0.failedCount to 0.Set failedCount to 0.remindersTried to 0.Set remindersTried to 0.skippedOverLimit to 0.Set skippedOverLimit to 0.notLogged to 0.Set notLogged to 0.problems to an empty list.Set problems to an empty list.booking in bookings:For each booking in bookings:bookingId to blank.Set bookingId to blank.idErrorIf a step below fails, save the error as idErrorTry
booking.uid and save it as bookingId.Trim booking.uid and save it as bookingId.On error
bookingId to blank.Change bookingId to blank.bookingId is emptyIf bookingId is emptyYes
skippedIncomplete.Add 1 to skippedIncomplete.No
No steps
remindedBookingIds contains bookingIdIf remindedBookingIds contains bookingIdYes
skippedAlreadyReminded.Add 1 to skippedAlreadyReminded.No
No steps
hoursAway to -1.Set hoursAway to -1.startErrorIf a step below fails, save the error as startErrorTry
booking.start and save it as hoursAway.Count the hours from the current date and time to booking.start and save it as hoursAway.On error
hoursAway to -1.Change hoursAway to -1.hoursAway multiplied by 1 is less than remindFromHours multiplied by 1 orโฆIf hoursAway multiplied by 1 is less than remindFromHours multiplied by 1 orโฆYes
No
No steps
appointmentTitle to blank.Set appointmentTitle to blank.titleErrorIf a step below fails, save the error as titleErrorTry
booking.title and save it as appointmentTitle.Trim booking.title and save it as appointmentTitle.On error
appointmentTitle to blank.Change appointmentTitle to blank.appointmentTitle is emptyIf appointmentTitle is emptyYes
skippedIncomplete.Add 1 to skippedIncomplete.No
No steps
clientEmail to blank.Set clientEmail to blank.emailErrorIf a step below fails, save the error as emailErrorTry
booking.attendees.email and save it as clientEmail.Trim the first item of booking.attendees.email and save it as clientEmail.On error
clientEmail to blank.Change clientEmail to blank.clientEmail is a valid email address is noIf clientEmail is a valid email address is noYes
skippedNoEmail.Add 1 to skippedNoEmail.No
No steps
clientName to blank.Set clientName to blank.nameErrorIf a step below fails, save the error as nameErrorTry
booking.attendees.name and save it as clientName.Trim the first item of booking.attendees.name and save it as clientName.On error
clientName to blank.Change clientName to blank.clientName is emptyIf clientName is emptyYes
clientName to there.Change clientName to there.No
No steps
startsAt to blank.Set startsAt to blank.startsAtErrorIf a step below fails, save the error as startsAtErrorTry
booking.start and save it as startsAt.Trim booking.start and save it as startsAt.On error
startsAt to blank.Change startsAt to blank.remindersTried is greater than or equal to maxReminders multiplied by 1If remindersTried is greater than or equal to maxReminders multiplied by 1Yes
skippedOverLimit.Add 1 to skippedOverLimit.clientName (clientEmail): not reminded, this run reached its limit of maxReminders reminders - raise 'maxReminders' to cover your busiest day to the end of problems and save it as problems.Add clientName (clientEmail): not reminded, this run reached its limit of maxReminders reminders - raise 'maxReminders' to cover your busiest day to the end of problems and save it as problems.No
No steps
clientName by a space and save it as nameParts.Split clientName by a space and save it as nameParts. between in appointmentTitle and save it as appointmentName.Get the text before between in appointmentTitle and save it as appointmentName.appointmentName is blankIf appointmentName is blankYes
appointmentName to appointmentTitle.Change appointmentName to appointmentTitle.No
No steps
clientTimeZone to blank.Set clientTimeZone to blank.timeZoneErrorIf a step below fails, save the error as timeZoneErrorTry
booking.attendees[0].timeZone and save it as clientTimeZone.Trim booking.attendees[0].timeZone and save it as clientTimeZone.On error
clientTimeZone to blank.Change clientTimeZone to blank.startTimeText to blank.Set startTimeText to blank.clientTimeZone isn't blankIf clientTimeZone isn't blankYes
zoneErrorIf a step below fails, save the error as zoneErrorTry
startsAt readable as โMonday, November 2 at 2:05 PMโ and save it as localStart.Make startsAt readable as โMonday, November 2 at 2:05 PMโ and save it as localStart.@localStart (@clientTimeZone) and save it as startTimeText.Build the text @localStart (@clientTimeZone) and save it as startTimeText.On error
No steps
No
No steps
startTimeText is blankIf startTimeText is blankYes
startsAt readable as โMonday, November 2 at 2:05 PM (UTCZ)โ and save it as startTimeText.Make startsAt readable as โMonday, November 2 at 2:05 PM (UTCZ)โ and save it as startTimeText.No
No steps
requiredPhrase to your appointmentName is booked for tomorrow.Set requiredPhrase to your appointmentName is booked for tomorrow.You are writing a very short reminder that will be read aloud as a voice note to a client about an appointment. Write 2 to 3 short sentences in a warm, plain s... and save it as prompt.Build the text You are writing a very short reminder that will be read aloud as a voice note to a client about an appointment. Write 2 to 3 short sentences in a warm, plain s... and save it as prompt.prompt to promptChange prompt to prompt
The script must contain this exact phrase, word for word: requiredPhrase.remindersTried.Add 1 to remindersTried.script to blank.Set script to blank.scriptProblem to blank.Set scriptProblem to blank.scriptErrorIf a step below fails, save the error as scriptErrorTry
prompt. Save it as modelReply.Ask Claude: prompt. Save it as modelReply.
Maximum response length: 200modelReply.response_text and save it as script.Trim modelReply.response_text and save it as script.On error
script to blank.Change script to blank.scriptProblem to scriptError.message.Change scriptProblem to scriptError.message.script doesn't contain requiredPhrase (ignoring case)If script doesn't contain requiredPhrase (ignoring case)Yes
skippedUnverifiedScript.Add 1 to skippedUnverifiedScript.clientName (clientEmail): the AI's script did not name the appointment, so nothing was sent. scriptProblem to the end of problems and save it as problems.Add clientName (clientEmail): the AI's script did not name the appointment, so nothing was sent. scriptProblem to the end of problems and save it as problems.No
No steps
audioRef to blank.Set audioRef to blank.voiceProblem to blank.Set voiceProblem to blank.voiceErrorIf a step below fails, save the error as voiceErrorTry
voiceId. Save it as speech.Convert text to speech with voice voiceId. Save it as speech.scriptspeech.document_ref and save it as audioRef.Trim speech.document_ref and save it as audioRef.On error
audioRef to blank.Change audioRef to blank.voiceProblem to voiceError.message.Change voiceProblem to voiceError.message.audioRef is emptyIf audioRef is emptyYes
failedCount.Add 1 to failedCount.clientName (clientEmail): ElevenLabs could not make the voice note, so nothing was sent. voiceProblem to the end of problems and save it as problems.Add clientName (clientEmail): ElevenLabs could not make the voice note, so nothing was sent. voiceProblem to the end of problems and save it as problems.No
No steps
emailSent to false.Set emailSent to false.sendErrorIf a step below fails, save the error as sendErrorTry
clientEmail.Send an email to clientEmail.
Subject: Reminder: appointmentName tomorrowscript
Your appointment starts startTimeText.
The same reminder is attached as a short voice note.
businessNamesentCount.Add 1 to sentCount.emailSent to true.Change emailSent to true.On error
failedCount.Add 1 to failedCount.clientName (clientEmail): the email could not be sent - sendError.message to the end of problems and save it as problems.Add clientName (clientEmail): the email could not be sent - sendError.message to the end of problems and save it as problems.emailSent is noIf emailSent is noYes
No
No steps
bookingId to the end of remindedBookingIds and save it as remindedBookingIds.Add bookingId to the end of remindedBookingIds and save it as remindedBookingIds.logWriteErrorIf a step below fails, save the error as logWriteErrorTry
logTabName in logSheetUrl.Add a row to logTabName in logSheetUrl.On error
notLogged.Add 1 to notLogged.clientName (clientEmail): the reminder went out but could not be written to the '@logTabName' tab. Add bookingId to column A before running this again today, or they will get a second email to the end of problems and save it as problems.Add clientName (clientEmail): the reminder went out but could not be written to the '@logTabName' tab. Add bookingId to column A before running this again today, or they will get a second email to the end of problems and save it as problems.Voice reminders sent: @sentCount. Already reminded on an earlier run: @skippedAlreadyReminded. Skipped, no usable email address: @skippedNoEmail. Skipped, book... and save it as summary.Build the text Voice reminders sent: @sentCount. Already reminded on an earlier run: @skippedAlreadyReminded. Skipped, no usable email address: @skippedNoEmail. Skipped, book... and save it as summary.problems with a new line and save it as problemList.Join problems with a new line and save it as problemList.problems isn't emptyIf problems isn't emptyYes
summaryStop and report a problem: summary
problemList.No
No steps
summary.Show in the output: summary.