No-show follow-up sent only after no-show status is explicitly set on the booking record
Why it matters
A 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.
Severity rationale
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.
Remediation
Add NO_SHOW to the booking status enum and expose an explicit host-action endpoint that sets it. Trigger the follow-up email from the status-change handler, not from a time-based cron:
// prisma/schema.prisma
enum BookingStatus {
PENDING
CONFIRMED
COMPLETED
NO_SHOW
CANCELLED
}
// src/app/api/bookings/[id]/no-show/route.ts
export async function POST(req: Request, { params }: { params: { id: string } }) {
const booking = await db.bookings.update({
where: { id: params.id },
data: { status: 'NO_SHOW', marked_no_show_at: new Date() },
})
await emailQueue.add('send-no-show-followup', {
bookingId: booking.id,
customerEmail: booking.customer_email,
hostEmail: booking.host_email,
})
return Response.json({ ok: true })
}
Remove any cron job that infers no-show status from appointment time alone.
Detection
-
ID:
no-show-follow-up -
Severity:
medium -
What to look for: Find no-show handling in the codebase. Search for: (1) A
no_showorno-showstatus value in the booking status enum or status field. (2) The mechanism that sets this status — it should be a manual action by the host/admin (API endpoint, admin UI button) or a webhook from a check-in system, NOT an automatic timeout. (3) A no-show follow-up email that is ONLY triggered whenstatus = 'no_show'is set, not when the appointment time passes. Trace the email trigger to confirm it is event-driven (status change), not time-driven (cron that checks past appointments). -
Pass criteria: Enumerate all booking statuses (at least 4 distinct values expected). ALL of the following: (1)
no_showis an explicit, distinct status in the booking model — list all status values and confirm no_show is among them. (2) No-show status is set by a manual action (admin endpoint, host UI) — quote the handler. An automatic timeout that sets no-show does NOT count as pass. (3) Follow-up email is triggered by the status change, not by a time-based check — quote the trigger. -
Fail criteria: No-show follow-up sent automatically after appointment time passes (even completed bookings would receive it). No distinct
no_showstatus (system only has confirmed/cancelled). No-show follow-up does not exist. Follow-up triggered by a cron job that checks "appointment time < now AND status = confirmed" — this catches completed-but-not-updated bookings. -
Skip (N/A) when: No no-show handling found in the codebase — searched for "no_show", "no-show", "noshow" in status fields, route handlers, and email templates.
-
Detail on fail: Describe the no-show mechanism found (or "none found"). State how the follow-up is triggered. Example:
"No explicit no_show status — status enum at prisma/schema.prisma:15 has only 'pending', 'confirmed', 'cancelled', 'completed'. A cron job at src/jobs/no-show-check.ts:5 runs hourly and sends follow-up emails to all bookings where start_time < now AND status = 'confirmed' — this incorrectly sends follow-ups for appointments that completed but were not marked as 'completed'.". -
Remediation: Add an explicit no-show status and trigger follow-up only on status change:
// prisma/schema.prisma enum BookingStatus { PENDING CONFIRMED COMPLETED NO_SHOW // Explicit no-show status CANCELLED } // src/app/api/bookings/[id]/no-show/route.ts — host marks no-show export async function POST(req: Request, { params }: { params: { id: string } }) { const booking = await db.bookings.update({ where: { id: params.id }, data: { status: 'NO_SHOW', marked_no_show_at: new Date() } }) // Follow-up triggered by status change, not by time await emailQueue.add('send-no-show-followup', { bookingId: booking.id, customerEmail: booking.customer_email, hostEmail: booking.host_email, }) return Response.json({ ok: true }) } -
Cross-reference: Booking Flow & Lifecycle audit checks the booking status state machine. Database Audit checks the status enum integrity. Email/SMS Compliance audit checks follow-up email compliance.
External references
- iso-25010:2011 · functional-suitability.functional-correctness
Taxons
History
- 2026-04-18·v1.0.0·Initial import from booking-notifications-reminders·automated