Texts each new Airtable enquiry your Cal.com booking link in your texting hours, sends one reminder if they have not booked, then stops.
Uses
Airtable
Cal.com
Twilio
Text new enquiries a booking link, then chase once
Every hour, the workflow looks for enquiries added to your Airtable table in the last few days and texts each one your Cal.com booking link. If they still have not booked after the waiting period you choose, it sends exactly one reminder, then leaves them alone. Texts only go out between the hours you set (9:00 to 19:00 unless you change them), so nobody hears from you at 2am, and enquiries already in your table when you connect it are never texted.
Nobody who has booked is chased: before every text, Cal.com is checked for an upcoming booking under their email address. A row is marked 'Link sent' or 'Reminded' only once Twilio has accepted the text, so nobody is texted twice. A row with no usable mobile number or email address is marked 'Needs attention' for you to look at instead of being texted at a guess. If Twilio refuses a text, Cal.com cannot be checked or a row cannot be updated, the run is marked as failed and names the people affected, so a problem never looks like a quiet day.
You need Airtable, Cal.com and Twilio connected, and an Airtable table with 'Name', 'Phone', 'Email', 'Follow-up Status' and 'Follow-up Date' columns. Fill in the base and table, your Cal.com booking link, the Twilio number to text from, your business name and the country your customers' numbers are from.
airtableBaseId to blank.Set airtableBaseId to blank.airtableTableName to blank.Set airtableTableName to blank.bookingLink to blank.Set bookingLink to blank.twilioFromNumber to blank.Set twilioFromNumber to blank.businessName to blank.Set businessName to blank.phoneRegion to US.Set phoneRegion to US.daysToWaitBeforeReminder to 3.Set daysToWaitBeforeReminder to 3.maxTextsPerRun to 12.Set maxTextsPerRun to 12.calcomEventTypeId to blank.Set calcomEventTypeId to blank.enquiriesToCheck to 100.Set enquiriesToCheck to 100.textingHoursStart to 9.Set textingHoursStart to 9.textingHoursEnd to 19.Set textingHoursEnd to 19.onlyTextEnquiriesFromLastDays to 3.Set onlyTextEnquiriesFromLastDays to 3.today.Get today's date and save it as today.rightNow.Get the current date and time and save it as rightNow.rightNow and save it as hourNow.Get the hour of rightNow and save it as hourNow.withinTextingHours to false.Set withinTextingHours to false.hourNow is greater than or equal to textingHoursStart multiplied by 1 and…If hourNow is greater than or equal to textingHoursStart multiplied by 1 and…withinTextingHours to true.Change withinTextingHours to true.invitesSent to 0.Set invitesSent to 0.remindersSent to 0.Set remindersSent to 0.alreadyBooked to 0.Set alreadyBooked to 0.stillWaiting to 0.Set stillWaiting to 0.skippedNoPhone to 0.Set skippedNoPhone to 0.skippedNoEmail to 0.Set skippedNoEmail to 0.skippedProblem to 0.Set skippedProblem to 0.textsSent to 0.Set textsSent to 0.hitTextLimit to false.Set hitTextLimit to false.waitingForTextingHours to 0.Set waitingForTextingHours to 0.textsTried to 0.Set textsTried to 0.refusedIds to an empty list.Set refusedIds to an empty list.notes to an empty list.Set notes to an empty list.airtableTableName. Save it as enquiryPage.List records in airtableTableName. Save it as enquiryPage.enquiries to enquiryPage.records.Set enquiries to enquiryPage.records.enquiry in enquiries:For each enquiry in enquiries:textsTried is greater than or equal to maxTextsPerRun multiplied by 1If textsTried is greater than or equal to maxTextsPerRun multiplied by 1hitTextLimit to true.Change hitTextLimit to true.fields to enquiry.fields.Set fields to enquiry.fields.followUpStatus to blank.Set followUpStatus to blank.statusUnreadableIf a step below fails, save the error as statusUnreadablefields["Follow-up Status"] and save it as followUpStatus.Trim fields["Follow-up Status"] and save it as followUpStatus.followUpStatus to blank.Change followUpStatus to blank.followUpStatus isn't blank and followUpStatus is not Link sentIf followUpStatus isn't blank and followUpStatus is not Link sentpersonName to there.Set personName to there.nameUnreadableIf a step below fails, save the error as nameUnreadablefields.Name and save it as personName.Trim fields.Name and save it as personName.personName to there.Change personName to there.personName is emptyIf personName is emptypersonName to there.Change personName to there.personName by a space and save it as nameParts.Split personName by a space and save it as nameParts.mobile to blank.Set mobile to blank.mobileUsable to false.Set mobileUsable to false.mobileUnreadableIf a step below fails, save the error as mobileUnreadablefields.Phone and save it as mobile.Trim fields.Phone and save it as mobile.mobile is a valid phone number and save it as mobileUsable.Check whether mobile is a valid phone number and save it as mobileUsable.mobileUsable to false.Change mobileUsable to false.emailAddress to blank.Set emailAddress to blank.emailUsable to false.Set emailUsable to false.emailUnreadableIf a step below fails, save the error as emailUnreadablefields.Email and save it as emailAddress.Trim fields.Email and save it as emailAddress.emailAddress is a valid email address and save it as emailUsable.Check whether emailAddress is a valid email address and save it as emailUsable.emailUsable to false.Change emailUsable to false.unusableReason to blank.Set unusableReason to blank.mobileUsable is noIf mobileUsable is noskippedNoPhone.Add 1 to skippedNoPhone.unusableReason to no usable mobile number.Change unusableReason to no usable mobile number.skippedNoEmail.Add 1 to skippedNoEmail.unusableReason to no usable email address.Change unusableReason to no usable email address.unusableReason isn't blankIf unusableReason isn't blankmarkProblemIf a step below fails, save the error as markProblemenquiry in airtableTableName.Update enquiry in airtableTableName.personName - unusableReason to the end of notes and save it as notes.Add Marked 'Needs attention': personName - unusableReason to the end of notes and save it as notes.skippedProblem.Add 1 to skippedProblem.personName 'Needs attention' (unusableReason): markProblem.message to the end of notes and save it as notes.Add Could not mark personName 'Needs attention' (unusableReason): markProblem.message to the end of notes and save it as notes.mobile and save it as textTo.Format the phone number mobile and save it as textTo.Hi @nameParts[0], and save it as greeting.Build the text Hi @nameParts[0], and save it as greeting.inviteOpening to greetingthanks for getting in touch with businessName.Set inviteOpening to greetingthanks for getting in touch with businessName.inviteText to inviteOpening. Pick a time that suits you here: bookingLink Reply STOP to opt out.Set inviteText to inviteOpening. Pick a time that suits you here: bookingLink Reply STOP to opt out.reminderOpening to greetingwe have not got you booked in at businessName.Set reminderOpening to greetingwe have not got you booked in at businessName.reminderText to reminderOpening yet. Here is the link again: bookingLink Reply STOP to opt out.Set reminderText to reminderOpening yet. Here is the link again: bookingLink Reply STOP to opt out.textToSend to blank.Set textToSend to blank.statusAfterText to blank.Set statusAfterText to blank.enquiryProblemIf a step below fails, save the error as enquiryProblembookingLookup.List bookings. Save it as bookingLookup.bookingLookup.bookings isn't emptyIf bookingLookup.bookings isn't emptyalreadyBooked.Add 1 to alreadyBooked.enquiry in airtableTableName.Update enquiry in airtableTableName.fields["Follow-up Date"] and save it as linkSentOn.Trim fields["Follow-up Date"] and save it as linkSentOn.linkSentOn to today and save it as daysWaited.Count the days from linkSentOn to today and save it as daysWaited.daysWaited is greater than or equal to daysToWaitBeforeReminder multiplied by…If daysWaited is greater than or equal to daysToWaitBeforeReminder multiplied by…textToSend to reminderText.Change textToSend to reminderText.statusAfterText to Reminded.Change statusAfterText to Reminded.stillWaiting.Add 1 to stillWaiting.textToSend to inviteText.Change textToSend to inviteText.statusAfterText to Link sent.Change statusAfterText to Link sent.skippedProblem.Add 1 to skippedProblem.personName - enquiryProblem.message to the end of notes and save it as notes.Add Not checked, tried again next run: personName - enquiryProblem.message to the end of notes and save it as notes.textToSend is blankIf textToSend is blankwithinTextingHours is noIf withinTextingHours is nowaitingForTextingHours.Add 1 to waitingForTextingHours.textsTried.Add 1 to textsTried.textWentOut to false.Set textWentOut to false.sendProblemIf a step below fails, save the error as sendProblemtextTo. Save it as sentText.Send a text message to textTo. Save it as sentText.textToSendtextWentOut to true.Change textWentOut to true.enquiry.id to the end of refusedIds and save it as refusedIds.Add enquiry.id to the end of refusedIds and save it as refusedIds.personName (textTo): sendProblem.message to the end of notes and save it as notes.Add Twilio refused the text to personName (textTo): sendProblem.message to the end of notes and save it as notes.textWentOut is noIf textWentOut is notextsSent.Add 1 to textsSent.statusAfterText is RemindedIf statusAfterText is RemindedremindersSent.Add 1 to remindersSent.invitesSent.Add 1 to invitesSent.recordProblemIf a step below fails, save the error as recordProblemenquiry in airtableTableName.Update enquiry in airtableTableName.personName (textTo) but could not mark their row '@statusAfterText' in Airtable: recordProblem.message. Set their Follow-up Status to '@statusAfterText' by hand before the next run, or they will be texted again. Stopped here so nobody else is texted without a record.Stop and report a problem: Texted personName (textTo) but could not mark their row '@statusAfterText' in Airtable: recordProblem.message. Set their Follow-up Status to '@statusAfterText' by hand before the next run, or they will be texted again. Stopped here so nobody else is texted without a record.refusedNote to blank.Set refusedNote to blank.refusedIds isn't emptyIf refusedIds isn't emptytextsSent is greater than 0If textsSent is greater than 0refusedId in refusedIds:For each refusedId in refusedIds:refusedMarkProblem If a step below fails, save the error as refusedMarkProblemrefusedId in airtableTableName.Update refusedId in airtableTableName.refusedMarkProblem.message to the end of notes and save it as notes.Add Could not mark a refused text 'Needs attention': refusedMarkProblem.message to the end of notes and save it as notes.refusedNote to:Change refusedNote to:Other texts went out, so each refused number is marked 'Needs attention' - usually a landline, or someone who replied STOP. Fix the row and set Follow-up Status back to empty (or 'Link sent' for a reminder) to try again.refusedNote to:Change refusedNote to:No text went out this run, which usually means a problem with your Twilio account or the 'twilioFromNumber' step. Nothing was marked, so the next run tries again.summaryLines to [Booking links texted: invitesSent, Reminders texted: remindersSent, Already booked, marked Booked: alreadyBooked, Still inside the waiting period: stillWaiting, Due a text, held until texting hours: waitingForTextingHours, Marked 'Needs attention', no usable mobile number: skippedNoPhone, Marked 'Needs attention', no usable email address: skippedNoEmail, Texts Twilio refused: the length of refusedIds, Could not be checked: skippedProblem].Set summaryLines to [Booking links texted: invitesSent, Reminders texted: remindersSent, Already booked, marked Booked: alreadyBooked, Still inside the waiting period: stillWaiting, Due a text, held until texting hours: waitingForTextingHours, Marked 'Needs attention', no usable mobile number: skippedNoPhone, Marked 'Needs attention', no usable email address: skippedNoEmail, Texts Twilio refused: the length of refusedIds, Could not be checked: skippedProblem].summaryLines with a new line and save it as summary.Join summaryLines with a new line and save it as summary.limitNote to blank.Set limitNote to blank.hitTextLimit is yesIf hitTextLimit is yeslimitNote to Change limitNote to
Stopped early at this run's limit of maxTextsPerRun texts. The rest are picked up next run.notes with a new line and save it as noteList.Join notes with a new line and save it as noteList.Enquiry follow-up finished. @summary@limitNote @noteList@refusedNote and save it as report.Build the text Enquiry follow-up finished. @summary@limitNote @noteList@refusedNote and save it as report.refusedIds isn't empty or skippedProblem is greater than 0If refusedIds isn't empty or skippedProblem is greater than 0report.Stop and report a problem: report.report.Show in the output: report.