Three cleaning touchpoints
The cleaning workflow keys everything off the departure date (not arrival) because cleaning happens after the guest leaves. Three touchpoints, designed to escalate progressively as departure approaches.
Touchpoint 1: Heads-up
Fires immediately on workflow spawn (runCleaningHeadsUp in
booking-flag.ts).
Purpose: tell the cleaner a booking exists so they can plan.
Send rules:
- Email always sent
- SMS only if days-to-arrival < 10 days
- Last-minute (<2 days): aggressive — both channels + escalation watch
Idempotent: skips if cleanings.heads_up_sent_at is set AND
arrival/departure dates haven’t changed since the prior send.
Body:
New booking at {Property}. Stay: {arrival} → {departure}. Next arrival on this unit: {next_arrival} OR No next booking yet. Cleaning needed after guest departs {departure}. Tap to confirm: {clean-ack URL}
Touchpoint 2: T-2 reminder
Fires at departure − 2 days, 16:00Z (10am MT). Email + SMS always.
Purpose: 2 days before the cleaner needs to show up — reminder that this booking is coming up.
Skip rule: 5-day-recent-ack rule. If the cleaner ack’d any prior touchpoint within 5 days, skip — they’re aware.
Touchpoint 3: Time-commit
Fires at departure-eve, 16:00Z (10am MT).
Purpose: get a specific time commitment from the cleaner — what time will the unit be ready?
Two variants based on turnover_days:
- Same-day turnover (next guest arrives the same day): show a
time-picker on
/clean-ackwith 5 options (12pm / 1pm / 2pm / 3pm / 4pm). The cleaner must commit to a specific time so Bill can manage guest expectations. - Gap-day (1+ days before next guest): show a simple “I’ll do it” confirm button. Time pressure is lower.
Skip rule: same 5-day-recent-ack.
State columns
All in the cleanings D1 table:
| Column | Set by |
|---|---|
heads_up_sent_at | Workflow on heads-up send |
heads_up_ack_at | /clean-ack POST action=confirm-heads-up |
t2_reminder_sent_at | Workflow on T-2 send |
t2_reminder_ack_at | /clean-ack POST action=confirm-t2 |
time_commit_sent_at | Workflow on time-commit send |
eta_at | /clean-ack POST action=ack (same-day picker) |
ack_at | /clean-ack POST action=ack or commit-flexible |
complete_at | /clean-ack POST action=complete |
issue_summary | /forms/clean POST |
Feature flags
cleaner-sms— currentlyshadow_mode=1. SMSes route to Bill instead of the cleaner with a[shadow → name phone]prefix. Flip to 0 once cleaners are ready to receive directly.cleaner-email— same pattern; redirects to Bill’s email.
These let Bill see exactly what each cleaner would receive before flipping live.
Source
src/workflows/booking-flag.ts—runCleaning*methodssrc/lib/cleanings.ts— D1 CRUDsrc/routes/clean-ack.ts— the 6-state page + POST handlers- Tests:
test/routes/clean-ack.test.ts(39 cases)