All 20 checks with why-it-matters prose, severity, and cross-references to related audits.
When a customer books an appointment, the confirmation email is the first trust signal they receive. A silent failure — a network hiccup that drops the send, an SMTP timeout that never retries — leaves the customer uncertain whether the booking went through. That uncertainty generates support tickets, duplicate bookings, and chargebacks. CWE-391 (failure to handle exceptional conditions) and ISO 25010 reliability.fault-tolerance both flag exactly this gap: a delivery path with no retry is not a delivery path, it's a hope. Without at least 3 retry attempts on exponential backoff, any transient email provider outage silently voids confirmation for everyone who booked during that window.
Why this severity: Critical because a single transient provider error silently drops the confirmation, leaving customers with no record of their booking and businesses exposed to disputes and no-shows.
booking-notifications-reminders.confirmation-email.confirmation-sent-60sSee full patternIncomplete confirmation emails drive no-shows, double-bookings, and support tickets. When a customer gets an email missing the timezone, they join a 3:00 PM call at the wrong hour because they assumed their own timezone. Missing cancel links force them into account-login flows just to cancel, so they ghost instead — and the host loses the slot with no warning. A missing reference ID makes every support exchange a detective hunt. Content-integrity failures here feed directly into the no-show rate, the metric that kills booking-based businesses.
Why this severity: High because missing fields cause wrong-hour arrivals, abandoned cancellations, and revenue-destroying no-shows at scale.
booking-notifications-reminders.confirmation-email.confirmation-email-contentSee full patternSending transactional email from a Gmail or Yahoo address violates DKIM-RFC6376, SPF-RFC7208, and DMARC-RFC7489 — the three DNS standards that inbox providers use to decide whether your mail is legitimate. Without all three records configured on a custom domain, confirmation emails land in spam or are silently rejected. Once DMARC policy is set to `p=none`, enforcement is disabled and spoofing your From address becomes trivial: an attacker can impersonate your booking system to phish your customers with zero DNS friction. Operators who skip domain authentication also undermine their own deliverability over time as spam-complaint rates accumulate against the shared sending IP.
Why this severity: High because misconfigured or absent domain authentication causes systematic inbox delivery failures and leaves the From address spoofable for phishing attacks against customers.
booking-notifications-reminders.confirmation-email.auth-domain-spf-dkim-dmarcSee full patternA cancelled booking affects two parties: the customer loses their appointment and the host loses revenue. Notifying only the customer leaves the host scrambling — they may have already prepared, turned away other clients, or allocated staff. Notifying only the host violates the customer's reasonable expectation of a cancellation record. CWE-391 and ISO 25010 reliability.fault-tolerance apply to both sends: a retry-less single attempt means a provider outage at cancellation time silently strands one or both parties. Businesses handling volume bookings compound this into systematic host blindness whenever email infrastructure degrades.
Why this severity: High because failing to notify the host of a cancellation causes real operational harm — wasted preparation, no-show assumptions, and revenue disputes — that a simple retry-equipped dual send prevents.
booking-notifications-reminders.confirmation-email.cancellation-email-60sSee full patternTCPA (47 U.S.C. § 227) imposes statutory damages of $500–$1,500 per message sent to a consumer who has opted out via STOP. GDPR Article 21 and CCPA § 1798.120 impose parallel opt-out obligations in their jurisdictions. An in-memory opt-out store survives only until the process restarts; a persistent database table that is never read before sends is functionally equivalent to no opt-out at all. The failure mode is silent: every SMS send succeeds from the carrier's perspective while each one adds to legal exposure. A single batch run against a list that includes opted-out numbers can generate thousands of violations before anyone notices.
Why this severity: Critical because sending SMS to opted-out numbers after a STOP violates TCPA and exposes the business to $500–$1,500 per-message statutory damages with no cap.
booking-notifications-reminders.sms.sms-optout-persistedSee full patternA 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.
Why this severity: 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.
booking-notifications-reminders.sms.optout-atomicSee full patternAn SMS confirmation sent 10 minutes after booking is effectively useless — customers check their phone immediately after completing a transaction to verify it went through. A cron job polling every 5 minutes introduces an average 2.5-minute delay and a maximum 5-minute delay, which is long enough for customers to attempt a duplicate booking or contact support. CWE-391 and ISO 25010 reliability.fault-tolerance flag the missing retry path: a single synchronous SMS send with no retry means a carrier timeout silently voids the only channel some customers monitor. Event-driven dispatch removes the timing gap and the retry gap in one change.
Why this severity: High because a polling-based or retry-less SMS confirmation produces delayed or silently dropped messages, undermining customer trust at the highest-anxiety moment in the booking flow.
booking-notifications-reminders.sms.sms-sent-60sSee full patternWhen all SMS retries are exhausted and nothing happens, the customer receives no notification about their booking. A `console.log` on failure is not a delivery log — it evaporates on process restart and cannot be queried for support investigations. CWE-391 (unhandled failure) and CWE-770 (uncontrolled resource consumption by uncapped retry state) both apply. The compound failure is silent: the booking system shows confirmed, the customer never hears anything, and there is no record to diagnose or alert on. ISO 25010 reliability.fault-tolerance requires that delivery failures be observable and that an alternative communication path exist.
Why this severity: High because exhausted SMS retries with no fallback leave the customer entirely uninformed and leave operations with no structured record to investigate or alert on.
booking-notifications-reminders.sms.sms-failure-fallbackSee full patternAn SMS that says "Your booking is confirmed" without the date, time, or a link to details forces the customer to open the app or website to find out when they need to show up. That friction is not cosmetic — customers forward confirmation SMSes to themselves as reminders, screenshot them, and share them with family. A generic message provides none of that utility. TCPA (47 U.S.C. § 227) requires an identification element in transactional SMS; a booking-specific link with a token also serves the dual purpose of providing one-click access to reschedule or cancel. A login-required URL collapses that utility for customers who are not currently authenticated.
Why this severity: Medium because missing booking specifics in the SMS body creates a poor customer experience and increases inbound support contacts, without meeting the transactional informational standard TCPA implies.
booking-notifications-reminders.sms.sms-contentSee full patternCalendar events that arrive without a stable UID create duplicate entries on every resend — a customer's calendar fills with phantom copies of the same appointment every time the booking system regenerates an invite. RFC 5545 mandates a persistent, globally unique UID for each VEVENT precisely to enable clients to match updates to existing events rather than creating new ones. Missing mandatory fields (ORGANIZER, DTEND, ATTENDEE) causes some calendar clients to silently reject the invite. A UID generated with `uuidv4()` on each call guarantees that every invite opens as a new event, making reschedules and cancellations invisible to the customer's calendar app.
Why this severity: High because a randomly generated or absent UID causes calendar clients to treat every invite as a new event, making updates and cancellations invisible and filling customer calendars with duplicate entries.
booking-notifications-reminders.calendar-invites.ics-complete-uidSee full patternRFC 5545 defines SEQUENCE as the mechanism calendar clients use to determine whether an incoming invite supersedes an existing one. When a modified booking is sent without an incremented SEQUENCE, clients that have already processed a SEQUENCE:0 invite ignore or flag the incoming update as stale — the customer's calendar retains the old time and location. The result is a no-show caused not by the customer but by the booking system's failure to follow the standard. Without a `sequence` column in the database, the increment cannot be persisted across calls, meaning the SEQUENCE will reset to 0 on each modification and every update will be treated as stale.
Why this severity: High because an absent or non-incrementing SEQUENCE causes calendar clients to silently discard reschedule notifications, producing no-shows caused by the system rather than the customer.
booking-notifications-reminders.calendar-invites.ics-modified-sequenceSee full patternWithout a .ics attachment, the booking lives only inside your email. Customers have to manually re-type the date, time, and timezone into their calendar — and half of them won't. The no-show rate for booking flows without calendar attachments is measurably higher than flows that attach .ics at confirmation time, because a calendar entry is the only thing that triggers the phone reminder ten minutes before the appointment. A text-only confirmation email is a user-experience failure that directly converts into lost revenue.
Why this severity: Medium because the gap increases no-show rate and rebooking cost, but the booking itself still exists server-side.
booking-notifications-reminders.calendar-invites.ics-attachedSee full patternWhen a booking is cancelled, the customer's calendar app needs a METHOD:CANCEL invite with the original UID to remove the existing event. Without it, the customer's calendar continues to show the appointment — they may still show up. Sending a METHOD:PUBLISH invite on cancellation makes the situation worse: the calendar client treats it as a new event confirmation rather than a removal. RFC 5546 defines METHOD:CANCEL specifically for this case. The failure is silent from the system's perspective (the email is sent, the queue job succeeds) but creates a confused customer experience and potential unnecessary no-shows.
Why this severity: Medium because without a METHOD:CANCEL invite, cancelled appointments persist on customer calendars, causing unnecessary arrivals and customer confusion that burdens support.
booking-notifications-reminders.calendar-invites.ics-cancelSee full patternA `setInterval` or `while(true)` polling loop that checks all bookings every N seconds keeps a database connection open and runs a full-table scan on every iteration — even when there are zero due reminders. Under load, this pattern burns CPU, database query budget, and queue worker threads continuously. CWE-770 (uncontrolled resource consumption) and ISO 25010 performance-efficiency.resource-utilization both capture this failure. A cron-based job queue fires only when reminders are actually due, scales horizontally without contention, and is restartable after a crash. Beyond efficiency, a polling loop running in a long-lived process cannot recover gracefully from crashes — in-flight checks are simply lost.
Why this severity: Critical because a polling loop consumes database and compute resources at all times regardless of reminder volume, and silently loses in-flight checks when the process crashes.
booking-notifications-reminders.reminders.reminder-scheduled-jobSee full patternJob queues provide at-least-once delivery guarantees — if a worker crashes after sending the reminder but before acknowledging the job, the queue will re-deliver it on restart. Without an idempotency guard that reads the `reminder_sent` flag before proceeding, crash recovery produces a duplicate send on every failure. CWE-362 (race condition) applies: two workers processing the same job concurrently (a known BullMQ failure mode under high load) can both pass a post-send flag check and send twice. ISO 25010 reliability.fault-tolerance requires that the system produce correct outputs even under failure and recovery. The fix — checking the flag before sending, not after — trades a low-probability missed send for a guaranteed no-duplicate guarantee.
Why this severity: Critical because without an idempotency guard, every worker crash during a reminder send produces a guaranteed duplicate notification on queue recovery, which erodes customer trust at scale.
booking-notifications-reminders.reminders.reminder-idempotentSee full patternA reminder that fires for a cancelled booking is the worst possible notification: the customer shows up for an appointment that no longer exists. A reminder that fires with the original time after a reschedule is functionally identical — the customer is told to show up at the wrong time. CWE-362 (race condition) applies to the in-flight case: a reminder job queued at 8:00 AM for a booking cancelled at 8:01 AM will still fire if the worker does not check current booking status before sending. ISO 25010 reliability.fault-tolerance requires that booking lifecycle changes propagate atomically to all derived state, including scheduled notifications.
Why this severity: High because sending reminders for cancelled or rescheduled bookings causes customers to show up at wrong times or for non-existent appointments, generating immediate operational and trust failures.
booking-notifications-reminders.reminders.reminder-cancellationSee full patternIf the host does not get a per-booking notification, bookings go unnoticed until the customer shows up to an empty room. Digest-only notifications fail the moment a booking is made less than 24 hours out — the host reads the digest the next morning, after the appointment slot. Host-side awareness is the difference between a booking system and a calendar-shaped black hole. No-show rates for hosts are just as business-critical as customer no-shows and they break trust with paying customers permanently.
Why this severity: Medium because missing host notifications cause host no-shows and customer refunds, though the booking record still exists.
booking-notifications-reminders.reminders.host-notification-per-bookingSee full patternA modification email that only shows the new time creates two failure modes. First, customers who don't remember the original booking cannot tell what changed and assume the email is spam or a duplicate confirmation. Second, customers who misread the new time cannot cross-check it against the old one — they show up at the original hour. Before/after comparison is the only way the customer can verify the reschedule matches what they requested, and its absence directly generates support tickets and missed appointments.
Why this severity: Medium because ambiguous reschedule emails cause wrong-hour arrivals and support volume, but the booking record itself is correct.
booking-notifications-reminders.reminders.modification-email-before-afterSee full patternA reminder that tells a customer their appointment is in 24 hours but requires them to log in to reschedule or cancel is functionally friction. Customers who cannot easily cancel end up as no-shows; hosts who cannot fill that slot lose revenue on both ends. OWASP A07 (Identification & Authentication Failures) applies in reverse: requiring authentication for a low-risk action (cancellation by the verified customer) increases abandonment and no-show rates rather than providing meaningful security. CWE-287 flags the correct design: a time-limited, scoped token grants the specific action to the specific customer without exposing the full account. Tokens that expire at or before appointment time are self-limiting.
Why this severity: Medium because login-gated action links in reminders block customer self-service at the point of highest intent, converting potential self-serve cancellations into no-shows and inbound support contacts.
booking-notifications-reminders.reminders.reminder-one-click-linkSee full patternA cron job that sends follow-up emails to all bookings where `start_time < now AND status = 'confirmed'` will send follow-ups to every completed booking that was never explicitly marked as completed — which in many booking systems is the majority. ISO 25010 functional-suitability.functional-correctness requires that no-show follow-ups fire only on verified no-shows, not on a time-based proxy. Without an explicit `NO_SHOW` status in the booking state machine, operators have no way to distinguish a completed appointment from a no-show, and any automation built on that distinction will misfire. Automated no-show detection without host confirmation also creates legal exposure if follow-up communications are construed as accusations.
Why this severity: Medium because a time-triggered no-show follow-up sends accusatory or unwanted messages to customers who attended their appointment but whose booking was never marked as completed.
booking-notifications-reminders.reminders.no-show-follow-upSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open Booking Notifications & Reminders Audit