1. Booking Confirmation and Ladder Scheduling
Trigger: appointment created on any main service calendar, by any route: staff, web booking, chat, phone, or waitlist fill.
Conditions and branches: branch on Appointment Type to select the reminder profile. Branch on how far out the appointment sits, which determines where in the ladder it starts. Branch on Preferred Channel.
- Write Appointment Type, Appointment Value Band, and Confirmation Status as Unconfirmed. Create the card at Booked - Unconfirmed. Apply appt-unconfirmed, plus first-appointment if the contact has no prior attended appointment, plus high-value-appt where the band is High.
- Send immediately: within two minutes, because a confirmation that arrives an hour later reads as a system error. SMS: "Booked, {{contact.first_name}}: {{appointment.day}} {{appointment.date}} at {{appointment.time}}, {{appointment.location}}. We'll remind you nearer the time. Need to change it? {{reschedule_link}}"
- Send immediately, email with the same content plus the calendar attachment, the duration, who they will be seeing, and the access notes. The calendar attachment is doing more work than the text.
- Set Reminder Count Sent to 1 and stamp Last Reminder Sent At.
- Ladder position branch: more than 7 days out schedules the full ladder from the prep step; 2–7 days out skips prep scheduling to the day it falls due and starts at the 3-day confirmation request; under 48 hours starts at the 24-hour reminder; under 4 hours sends one short message only and nothing else.
- Preferred Channel of No Reminders: send the confirmation only, apply reminder-opted-out, and schedule nothing. Respecting this is what keeps people on the list.
- Repeat no-show branch: if No Show Count is 2 or more and the booking was not taken on the Deposit-Required Calendar, create a task for the manager: "Repeat no-show has booked a standard slot. Move to deposit calendar?" Do not message the customer about it automatically.
Exit: when the ladder is scheduled. Any cancellation or reschedule cancels every scheduled step and the workflow re-runs from the top on the new appointment.
2. Pre-Appointment Preparation
Trigger: 7 days before the appointment, or at booking if the appointment is between 2 and 7 days out.
Conditions and branches: skip entirely for appointment types with no prep requirement. Do not send an empty prep message for the sake of ladder symmetry. Skip if Prep Instructions Sent is already on.
- Send email: this step is email-first because it carries detail. Subject: "Getting ready for {{appointment.day}}". Body: what will happen, how long it takes, what to bring, where to park or how to get access, and a single link to the Pre-Appointment Form.
- Send SMS only if the appointment type genuinely needs something done in advance: "{{contact.first_name}}, two quick things before {{appointment.day}}: {{prep_form_link}}. Takes a minute."
- Switch on Prep Instructions Sent. Increment Reminder Count Sent.
- Wait 2 days. If the prep form is required and still not completed, send one reminder: "Still need those details for {{appointment.day}}, {{prep_form_link}}" and create a task for the assigned staff member if the form is genuinely blocking.
- Do not ask for confirmation in this message, in either channel. It is a separate job and combining them reduces both response rates.
- On form completion, write the answers to the contact and notify the staff member only if an answer triggers a flag the business has defined. An access restriction, a requirement for extra time, anything that changes the booking.
Stop conditions: cancellation, reschedule, or reminder-opted-out.
3. Confirmation Request
Trigger: 3 days before the appointment.
Conditions and branches: skip if Confirmation Status is already Confirmed. Branch on Preferred Channel. Branch on whether first-appointment is applied. First-timers get the fuller version with reassurance, repeat customers get one line.
- SMS, repeat customer: "{{contact.first_name}}, {{appointment.day}} {{appointment.time}} still good? Reply Y to confirm, R to move it, C to cancel."
- SMS, first appointment: "{{contact.first_name}}, looking forward to seeing you {{appointment.day}} at {{appointment.time}}, {{appointment.location}}. {{duration_line}} Reply Y to confirm, R to move it, C to cancel."
- Email variant for Email-preference contacts, carrying three buttons rather than reply codes.
- Increment Reminder Count Sent and stamp Last Reminder Sent At.
- On Y: set Confirmation Status Confirmed, stamp Confirmed At, write Confirmation Channel, move the card to Confirmed, swap appt-unconfirmed for appt-confirmed, and reply once: "Great. See you then." Then downgrade the 24-hour reminder to informational and cancel the 2-hour message and the At Risk path.
- On R: move to Reschedule Requested, send the Reschedule Calendar link, and hand to the reschedule workflow.
- On C: hand to the cancellation workflow immediately. Do not attempt to talk them out of it in this message.
- On no response: wait 36 hours, then send one shorter nudge: "Just need a quick Y or N for {{appointment.day}}, {{contact.first_name}}, so we know whether to hold the slot." The phrase about holding the slot matters; it reframes the reply as useful to them rather than to you.
- Still no response: hand to the At Risk workflow.
Stop conditions: confirmation, cancellation, reschedule, opt-out, or the appointment passing.
4. Final Reminder Sequence
Trigger: 24 hours before the appointment, then conditionally 2 hours before.
Conditions and branches: both steps branch on Confirmation Status. The 2-hour step additionally branches on appointment type and travel.
- 24 hours, confirmed: informational only, no ask. "Tomorrow at {{appointment.time}}, {{appointment.location}}. {{custom_values.travel_or_access_notes}} Running late or need to move it? {{reschedule_link}}"
- 24 hours, unconfirmed: keep the ask. "{{contact.first_name}}, we've got you tomorrow at {{appointment.time}} but haven't heard back. Reply Y if you're coming, or C and we'll free the slot for someone else."
- Increment Reminder Count Sent. This step is exempt from the frequency guard. It is the message the whole build exists to deliver.
- 2 hours, confirmed and no travel involved: send nothing. This restraint is the difference between a ladder people tolerate and one they mute.
- 2 hours, unconfirmed: "{{appointment.time}} today, {{contact.first_name}}, {{appointment.location}}. {{custom_values.travel_or_access_notes}}"
- 2 hours, appointment type involves the customer traveling a distance or a tradesperson attending their property: send regardless of confirmation, because the information is operationally useful rather than a nag. "On track for {{appointment.time}}. {{access_or_arrival_note}}"
- 30 minutes before, staff-side only: notify the assigned staff member with the contact's prep form answers and any flags. No customer message.
Stop conditions: cancellation, reschedule, sms-opted-out, reminder-opted-out.
5. At Risk Intervention
Trigger: appointment is within 24 hours with Confirmation Status still Unconfirmed after the nudge.
Conditions and branches: branch on Appointment Value Band and on first-appointment. This workflow spends human time, so it has to be selective.
- Apply appt-at-risk and move the card to At Risk. The board now shows tomorrow's risk list, which is the view the person running the calendar actually wants.
- High band, or first appointment: create a task for the assigned staff member, "Call to confirm, {{contact.full_name}}, {{appointment.day}} {{appointment.time}}": due within 3 hours and inside business hours. A human call at this point recovers appointments that no amount of texting will.
- Standard or Low band: no call. Send one last message with the easiest possible exit: "{{contact.first_name}}, if tomorrow doesn't work any more just reply C and we'll free it up. No problem either way." Giving permission to cancel converts silent no-shows into released slots, which is a win.
- Wait 4 hours. If the call task is still open and the appointment is inside 12 hours, reassign once to whoever is on the calendar.
- On contact made: record the outcome, update Confirmation Status, and either confirm or release the slot. A released slot goes straight to the waitlist workflow.
- On no contact by 2 hours before: leave the appointment in place, flag it on the staff member's daily list as unconfirmed, and pre-queue the waitlist candidates for that slot so a fill can happen in minutes if the no-show is confirmed.
Stop conditions: confirmation, cancellation, or the appointment start time passing.
6. Cancellation, Reschedule and Waitlist Fill
Trigger: cancellation received by any route, or appointment released by the At Risk path.
Conditions and branches: branch on notice period (more than 72 hours, 24–72 hours, or under 24 hours) which determines whether a waitlist fill is realistic.
- Immediately move the card to Canceled - Slot Released, cancel every scheduled ladder step, and remove the appointment from the calendar so the slot is genuinely free.
- Collect Cancellation Reason: conversationally by BO if the cancellation came by text, by the Cancellation Form otherwise. One question, never two.
- Offer alternatives once, in the same message: "Canceled, {{contact.first_name}}. If you'd rather just move it, I've got {{slot_1}}, {{slot_2}} or {{slot_3}}, otherwise all done." On acceptance, move to Rescheduled and re-run the booking workflow for the new time.
- Reason of Price or Found Alternative: create a task for the owner rather than an automated save attempt. These are worth a human conversation and nothing else.
- Waitlist fill, notice over 24 hours: find contacts with waitlist-active whose stated notice tolerance and day or time preferences match the freed slot, order by Waitlist Priority, and offer to the top one only. "A slot's just opened: {{appointment.day}} {{appointment.time}}. Want it? Reply YES in the next 90 minutes and it's yours." Apply waitlist-offered.
- Wait 90 minutes. On no reply, remove waitlist-offered and move to the next candidate. Repeat through at most five candidates, then stop and notify the calendar owner that the slot is still open. Sequential single offers, never a blast, offering one slot to twelve people produces eleven disappointments and one booking.
- Waitlist fill, notice under 24 hours: offer to the top three candidates simultaneously with an explicit first-come line, because the window is too short for sequencing. Be honest that it is first-come.
- On acceptance: book the slot, set Waitlist Status to Accepted, remove waitlist-active, and start the ladder at the correct position for the time remaining.
- Reschedule count guard: if Reschedule Count reaches 3 on one appointment, stop automated rescheduling and create a task for a human to call. Three moves means something else is going on.
Stop conditions: slot filled, candidate list exhausted, or the slot time passing.
7. Attendance Marking and No-Show Recovery
Trigger: 15 minutes after the scheduled start time.
Conditions and branches: branch on whether attendance has been marked, then on No Show Count.
- Attendance marked: move the card to Attended, apply appt-attended, and hand to the post-appointment sequence, which waits 2 hours before sending anything, because a review request that arrives while the customer is still in the parking lot is the most common cause of a bad review.
- Not marked: notify the assigned staff member once: "Is {{contact.first_name}} with you? Tap to mark attended or no-show." Marking must be one tap or it will not happen, and unmarked appointments wreck the reporting that justifies the build.
- Still unmarked after 60 minutes: treat as a no-show for workflow purposes but flag the record as unverified, so the metrics can distinguish real no-shows from admin failure.
- No-show confirmed: move the card to No Show, apply appt-no-show, increment No Show Count, stamp Last No Show Date.
- Wait 2 hours. First-time no-show, send one recovery message with no reproach: "Sorry we missed you today, {{contact.first_name}}, hope everything's alright. Want to pick another time? {{calendar_link}}" Nothing accusatory, no mention of the cost, no second message.
- Second or later no-show: apply repeat-no-show and send the rebook link pointing at the Deposit-Required Calendar, with one honest line: "Happy to rebook. We hold these slots with a deposit now. {{deposit_calendar_link}}"
- Wait 1 day. If no rebook, send the optional No-Show Reason Form, one question, and nothing after it.
- On rebook: move the card to Recovered After No Show and run the booking workflow for the new appointment.
Stop conditions: rebooking, or 3 days after the missed appointment. Never more than two messages following a no-show.
8. Frequency Guard and Reminder Audit
Trigger: runs as a gate on every outbound automated message, plus a scheduled audit every Monday at 07:30.
Conditions and branches: the gate classifies every message as Critical (24-hour and 2-hour reminders, booking confirmations, waitlist offers, cancellation acknowledgements) or Non-Critical (prep messages, confirmation nudges, no-show reason forms, post-appointment requests).
- Gate rule: a Non-Critical message is held if the contact has received any automated message in the last 24 hours, or four or more in the last 7 days. Held messages are dropped, not queued. A reminder that arrives two days late is worse than none.
- Apply message-cap-hit when the weekly ceiling is reached, and clear it on a rolling basis.
- Critical messages pass the gate always, but are logged when they pass over a cap so the audit can show it.
- Quiet hours: no message of any class sends outside 08:00-20:00 in the contact's Quiet Hours Zone. A 24-hour reminder that would land at 06:30 sends at 08:00 instead, which is still 22 hours of notice.
- Monday audit: compile messages sent per contact across all sequences, flag any contact who received more than four in the week, and list every instance of two sequences messaging the same contact on the same day. This cross-sequence collision is the usual cause of opt-outs and it is invisible without this report.
- Compile opt-out counts by sequence and by message position. An opt-out spike at a specific message is a copy problem with a precise address.
- Email the owner. Subject: "Reminder audit: {{optout_count}} opt-outs, {{collision_count}} message collisions".
- Wait 10 minutes, then create a task for each flagged collision: "Two sequences hit {{contact.full_name}} on the same day: check sequence overlap", due that week.
Exit: the gate exits per message; the audit exits on send. The audit reports measured platform data only.