Skip to content

Direct Booking Deposit Chaser

Example prompt: "Every weekday at 10am, check our 'Bookings' Google Sheet for any direct booking with an unpaid deposit. Draft a friendly heads-up 3 days before it's due, a clear reminder on the due date, polite chases at +3 and +7 days overdue, and a final notice at +10. Don't auto-cancel — only draft a cancellation email for host sign-off once the final notice has been sent, the configured cancellation_window has elapsed, and the deposit is still unpaid."

The Problem

A direct booking with an unpaid deposit is a room that might be paid for and might not be, and the longer it sits in that state the worse the position gets. By the time the host notices on a Friday afternoon that the deposit for next weekend has not arrived, it is already too late to release the room without an awkward phone call. The other failure mode is the over-eager chase that lands three days early and reads as a marketing email — guests stop replying to those altogether. And the worst of the lot is the workflow that combines a final-notice email with a cancellation email, so a guest who pays five minutes after the final notice goes out has already been pencilled in as cancelled.

How GloriaMundo Solves It

We build a workflow that runs every morning, reads the bookings sheet, and walks the rung ladder threshold-based for every Pending Deposit row — selecting the next-in-sequence overdue rung that has not yet been sent, never skipping a rung that was missed because the workflow ran late, and never drafting a second copy of a rung the host has already drafted but not yet sent (an existence check on booking+rung guards against duplicate drafts; an unsent draft is surfaced in the daily Slack summary instead). All threshold checks happen in the property's local timezone. An LLM step composes a Gmail draft at the right tone for the right rung — warm at T-3, clear on the due date, honest at +7, a fair final notice at +10 that names the cancellation deadline computed as max(deposit_due_date + cancellation_window, deposit_due_date + 10 days) so the date the guest sees is never in the past relative to the final notice, rather than treating itself as the cancellation. Cancellation is a separate eligibility check: it only fires once the final notice has actually been sent and the cancellation_deadline has been reached with the deposit still unpaid, and the resulting cancellation email always sits as a Gmail draft for host sign-off — never auto-sent, because cancelling a booking is the kind of thing a person should do. Glass Box preview shows the rung selection, the drafts, the pending unsent drafts, and any cancellation candidates before anything moves.

Example Workflow Steps

  1. Trigger (schedule): Every weekday at 10am.
  2. Step 1 (integration): Read the 'Bookings' tab for rows where status is 'Pending Deposit'.
  3. Step 2 (integration): Read the 'Settings' tab for the deposit amount, the payment link, the 'cancellation_window' value, and the property's IANA timezone (the canonical timezone for every timestamp and every 'today' comparison in this workflow). Read the 'Deposit Reminders' tab; the schema is booking_reference, rung, draft_created_at, draft_link, sent_at, append_seq. The rung column accepts the five rung labels and the sentinel value cancellation_draft — cancellation_draft rows live in the same sheet with rung='cancellation_draft', not in a separate marker column. draft_created_at and sent_at are always full ISO 8601 timestamps in the property timezone with milliseconds (e.g. 2026-05-15T10:23:47.512+01:00); any date read from 'Bookings' (deposit_due_date) is normalised to property-timezone before any comparison. append_seq is a monotonic per-booking integer assigned at append time (the workflow looks up MAX(append_seq) for the booking and uses MAX+1, or 1 if no prior row) — stored as a value on the row so it survives manual sorts of the sheet.
  4. Step 3 (integration, optional): For any 'Deposit Reminders' row whose draft_created_at is set but sent_at is empty, check Gmail's Sent folder for the outgoing message (matched on draft_link or message id); if found, populate sent_at on that row. This lets the workflow self-reconcile when the host sent a draft outside the sheet.
  5. Step 4 (conditional): For each booking, walk the rung ladder in order (T-3, due, +3, +7, +10) and select the next-in-sequence rung whose threshold has been reached (using property-timezone today) AND that has not yet been sent (sent_at populated in 'Deposit Reminders'); a draft sitting unsent does not advance the ladder, so the workflow will not move past an unsent +7 even if today is +10. The +10 rung is the final notice only; it does not cancel.
  6. Step 5 (conditional, duplicate-draft guard): Before drafting, check 'Deposit Reminders' for any unsent row with the same booking_reference and the selected rung. Apply the canonical-draft dedup rule: if more than one unsent row exists for the same booking+rung pair, the row with the most recent draft_created_at is canonical; if two or more rows tie on draft_created_at (extremely rare with millisecond precision in property-timezone), break the tie using the highest append_seq value — append_seq is monotonic per booking at write time, so the highest append_seq is unambiguously the most recently appended row, and the tie-breaker therefore preserves recency rather than picking by URL or row order. All non-canonical unsent rows are stale duplicates — ignore them for the existence check, the daily summary, and any redraft logic. If a canonical unsent draft exists, do NOT compose a new email and do NOT append a new row — carry the canonical draft_link forward to Step 9's pending-drafts list. Only when no unsent draft already exists for that booking+rung pair do we proceed to Step 6.
  7. Step 6 (llm): Compose a Gmail draft at the right tone for the selected rung, with the payment link, amount, booking reference, and arrival date. For the +10 final notice, include the actual cancellation deadline computed as cancellation_deadline = max(deposit_due_date + cancellation_window, deposit_due_date + 10 days) — the max(...) protects against properties with a short cancellation_window so the deadline shown in the email is never in the past relative to the final-notice send date. Never quote the raw cancellation_window value to the guest.
  8. Step 7 (integration): Save the draft in Gmail addressed to the guest. Append a row to 'Deposit Reminders' populating booking_reference, rung, draft_created_at (full ISO 8601 timestamp in the property timezone with milliseconds, captured at the moment of append — not date-only), draft_link, append_seq (MAX(append_seq) for this booking + 1, or 1 if no prior row), and leaving sent_at empty.
  9. Step 8 (conditional): Run the cancellation-eligibility check — for every booking where the +10 row in 'Deposit Reminders' has sent_at populated (the final notice was actually sent, not just drafted), AND today (in the property timezone) is on or after cancellation_deadline = max(deposit_due_date + cancellation_window, deposit_due_date + 10 days) (inclusive >= — exactly on the deadline day counts; the max(...) prevents cancellation from being eligible before the final notice has had a chance to go out when cancellation_window < 10), AND the deposit is still unpaid, AND no UNSENT cancellation_draft row exists for this booking (apply the canonical-draft dedup rule with the same draft_created_at + append_seq tie-breaker; a previously-sent cancellation_draft does NOT block a new cancellation if the booking is somehow still pending — only an unsent draft does): draft a cancellation Gmail to the guest (never auto-send), post a Slack flag in #bookings for host sign-off so the room can be freed in the channel manager by hand, and append a 'cancellation_draft' row to 'Deposit Reminders' (rung='cancellation_draft') with its own draft_created_at, draft_link, and append_seq (MAX(append_seq) for this booking + 1); sent_at left empty until the host sends.
  10. Step 9 (integration): Post a daily summary in #bookings on Slack. The summary uses canonical rows (the row that wins the dedup tie-breaker on each booking+rung pair); sent/unsent is a filter applied on top of the canonical set, not a precondition for being canonical, so a booking that has been sent at a rung still appears in that rung's distribution. The summary reports: per-rung counts — for each rung label, the number of distinct bookings with a canonical row at that rung regardless of whether sent_at is populated (the rung-distribution snapshot); drafted-not-sent counts per rung — the subset of per-rung counts where the canonical row has sent_at empty; +7 attention list — bookings whose canonical +7 row has sent_at empty; new cancellation drafts awaiting host sign-off — bookings with a canonical cancellation_draft row whose sent_at is empty; and 'Pending drafts awaiting send' list — one line per unsent canonical draft across all rungs (booking_reference, rung, draft_created_at age, draft_link). The host gets both 'how many bookings are at each rung state' and 'how many of those still need my action'.

Integrations Used

  • Google Sheets — the bookings register, the reminders log, and the settings for amounts and links
  • Gmail — the draft chasers and the cancellation drafts, always saved for review
  • Slack — the daily summary and the host sign-off prompt for cancellations

Who This Is For

B&B owners, small hotel managers, and short-let hosts who take direct bookings (not only OTA channels) and who set a deposit at booking time, who are losing one or two bookings a month either to forgetting to chase or to chasing too aggressively, and who would like a fair, consistent ladder applied automatically.

Time & Cost Saved

Chasing a deposit takes ten to fifteen minutes per booking when you include looking up the booking, writing the right tone, and remembering what stage it is at. Across a property doing twenty direct bookings a month with a few stragglers, that is two to three hours a week. The harder-to-measure gain is the deposit that gets paid because the T-3 heads-up landed politely instead of the chase landing twelve days late, and the room that gets released cleanly at +10 instead of staying blocked on the calendar for nothing.