Delivery Rules — Email + SMS Timing
For agents: These are the timing rules that govern when automated email and SMS sends actually fire. They apply to the initial audit delivery email, every email + SMS in the post-audit drip sequence (follow-up-sequence), and the makeover-delivery sequence (delivery-sequence). Internal-facing channels (Slack notifications, HelpScout drafts) are NOT gated by these rules.
Initial audit delivery
- Minimum 4-hour delay between when analysis completes (Gate 2 passes) and when the audit ships.
- If the 4-hour mark falls outside the time-of-day window below, delivery is pushed forward to the next valid window slot.
- Admin override: “Deliver Now” bypasses the delay AND the window. Use sparingly.
Time-of-day window
- Sends only between 8:04 AM Eastern and 6:00 PM Eastern.
- Window opens at 8:04 AM (not 8:00) so sends don’t land on round 5-minute marks.
- Within the day, sends fire at random seconds inside each tick to look less mechanical.
Days the system never sends
- Sunday
- New Year’s Day (January 1)
- Independence Day (July 4)
- Thanksgiving (4th Thursday of November)
- Christmas Eve (December 24)
- Christmas Day (December 25)
Minimum 4-hour gap between same-medium sends
Shipped 2026-05-04. Prevents email/SMS bunching when blocked-day deferrals collide with natural-day rows.
- Rule: at least 4 hours must pass between any two same-medium sends to the same church (email-to-email, or SMS-to-SMS).
- Applies to all sequence types:
main,comms_dept,confirmation,makeover_delivery. - Mechanism: gate runs in
processRow(functions/src/sequences/processQueue.js) between the v2-eligibility check and the delivery-window check. Reads most-recent same-medium send from the church doc’s existingemailsSent[]/smsSent[]arrays — no extra Firestore reads. When the gap is too small,scheduledForis pushed forward to(lastSent + 4h)andlastDeferredReason: 'min_gap_per_medium'+lastDeferredAtare stamped. - Idempotent: before the new
scheduledFormatures, the cron simply doesn’t pick the row up. After it matures, the gap check passes and normal flow resumes. - Tunable via constant
MIN_GAP_PER_MEDIUM_MSat the top ofprocessQueue.js. - Test mode bypasses the gate — 14-min compressed validation runs need all messages to fire end-to-end.
- Behavioral implication for follow-up-sequence: the “7-day” label is illustrative — Sundays/holidays + the 4h gap can stretch elapsed time slightly past 7 calendar days.
bonusExpiresAt = deliveredAt + 7ddoes NOT stretch; bonus-deadline copy remains accurate to the original 7-day commitment. In rare worst-case scenarios (multiple consecutive blocked days), Email 6/7 could fire after bonus expiry; copy in those steps shouldn’t reference the deadline as “tomorrow” in absolute terms.
Test mode
- Submitting an audit with
?test=1bypasses the 4-hour delay AND the time-of-day window — the audit ships immediately so you can verify the full flow without waiting. - The post-audit follow-up sequence timeline also compresses 720× in test mode (a 7-day sequence runs in ~14 minutes).
- The makeover-delivery sequence has its own
testMode: trueflag that compresses its 7-day timeline 720× — see delivery-sequence § Test Mode. - Caveat: while convenient for end-to-end testing, test mode means you can’t verify the timing rules themselves on a test submission. To verify timing, submit a real audit and watch the schedule.
Scope — what’s gated, what isn’t
| Channel / surface | Gated by these rules? |
|---|---|
| Initial audit delivery email | Yes |
| Post-audit drip emails (1–7 of follow-up-sequence) | Yes |
| Post-audit drip SMS (1–3 of follow-up-sequence) | Yes |
| The Comms Dept branch emails / SMS (Part Three of follow-up-sequence) | Yes |
| Makeover-delivery emails (1–6 of delivery-sequence) | Yes |
| Makeover-delivery SMS (1–3 of delivery-sequence) | Yes |
| Slack notifications (admin-facing) | No — fire immediately |
| HelpScout drafts (admin-facing) | No — fire immediately |
Source of truth
- Time-of-day window + 4-hour delivery delay + blackout days:
functions/src/audit/deliveryWindow.js - 4-hour minimum gap between same-medium sends:
functions/src/sequences/processQueue.js(constantMIN_GAP_PER_MEDIUM_MS, helperlastSendMsForMedium, gate inprocessRow)
When the runtime rules change, update this doc to match.
Related
- follow-up-sequence — post-audit conversion sequence (subject to these rules)
- delivery-sequence — makeover delivery sequence (subject to these rules)
- README — makeover funnel overview