Opted-out numbers never contacted even within the same processing batch
Why it matters
A batch reminder job that fetches the recipient list at the top of the function and then loops through sends creates a TCPA race window: a customer can send STOP between the initial query and their position in the loop. CWE-362 (race condition) is the exact failure mode — the opt-out write and the in-flight batch read operate on shared state without synchronization. Under TCPA (47 U.S.C. § 227), intent to comply is not a defense; the message that arrives after a STOP still carries the per-message penalty. A pre-cached Set of opted-out numbers becomes stale the moment the STOP is processed, which on a high-volume system can be milliseconds into the batch run.
Severity rationale
Critical because the race window between recipient list fetch and per-recipient send can result in TCPA violations even when an opt-out record exists in the database.
Remediation
Replace any cached opt-out list with a fresh database query per recipient inside the send loop. The extra round-trip per booking is negligible compared to the per-message TCPA exposure:
// src/jobs/reminders.ts
async function sendReminders() {
const dueBookings = await db.bookings.findMany({
where: { reminder_due_at: { lte: new Date() }, reminder_sent: false },
})
for (const booking of dueBookings) {
// Fresh DB query per recipient — never a cached list
const optOut = await db.smsOptOut.findUnique({
where: { phone_number: booking.customer_phone },
})
if (optOut?.opted_out_at && !optOut.opted_in_at) {
await db.bookings.update({ where: { id: booking.id }, data: { reminder_sent: true } })
continue
}
await smsQueue.add('send-reminder', { bookingId: booking.id })
}
}
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Booking Notifications & Reminders Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- external · TCPA-47-USC-227 — Telephone Consumer Protection Act — 47 U.S.C. § 227 (batch opt-out compliance)
- cwe · CWE-362 — Race Condition (concurrent opt-out check and send)
- gdpr · Art. 21 — Right to object — must be honoured without delay
Taxons
History
- 2026-04-18·v1.0.0·Initial import from booking-notifications-reminders·automated