RFC 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.
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.
Add a sequence integer column to the bookings table, increment it on every modification, and pass the stored value to the ICS generator in src/lib/bookings.ts:
async function modifyBooking(bookingId: string, changes: Partial<Booking>) {
const booking = await db.bookings.findUnique({ where: { id: bookingId } })
const updated = await db.bookings.update({
where: { id: bookingId },
data: { ...changes, sequence: (booking.sequence ?? 0) + 1 },
})
const icsData = await generateBookingICS(updated, booking.host)
await emailQueue.add('send-modification-email', {
bookingId,
customerEmail: booking.customer_email,
icsAttachment: icsData,
})
}
The generateBookingICS function in src/lib/calendar.ts must read booking.sequence and set it as the SEQUENCE field — not hardcode 0.
ID: booking-notifications-reminders.calendar-invites.ics-modified-sequence
Severity: high
What to look for: Find the booking modification code path. When a booking's date, time, or location changes, check whether: (1) A new .ics is generated and sent — search the modification handler for calendar generation calls. (2) The UID remains the same as the original (verify the same stable UID derivation is used). (3) A SEQUENCE field is incremented — look for a sequence column in the booking model/table and check it is incremented on modification. The SEQUENCE value in the .ics must match the stored value. (4) The updated .ics is attached to or linked from a modification email sent to the customer.
Pass criteria: Enumerate all 4 conditions. ALL 4 must be met (minimum 4 of 4): (1) .ics is regenerated on modification — quote the function call. (2) UID is the same stable value — confirm the same derivation. (3) SEQUENCE is stored in the database and incremented by at least 1 per modification — quote the column and increment code. A missing SEQUENCE column does NOT count as pass. (4) Updated .ics is sent to the customer via email. Report the count: "4 of 4 conditions met" even on pass.
Fail criteria: No .ics sent on modification. UID changes (customer sees a new event instead of an update). SEQUENCE not tracked or not incremented. Updated .ics generated but not sent to customer.
Skip (N/A) when: Calendar invites not implemented (same criteria as ics-complete-uid skip), OR booking modifications are not supported (no modification handler found).
Detail on fail: For each of the 4 conditions, state "MET" or "NOT MET" with evidence. Example: "(1) .ics regenerated: MET — generateBookingICS() called in modifyBooking(). (2) UID stable: MET — same booking.id@domain. (3) SEQUENCE incremented: NOT MET — no sequence column in bookings table, SEQUENCE always 0. (4) .ics sent: MET — attached to modification email.".
Remediation: Track SEQUENCE in the database and increment on every modification:
// src/lib/bookings.ts
async function modifyBooking(bookingId: string, changes: Partial<Booking>) {
const booking = await db.bookings.findUnique({ where: { id: bookingId } })
// Increment SEQUENCE
const updated = await db.bookings.update({
where: { id: bookingId },
data: { ...changes, sequence: (booking.sequence ?? 0) + 1 }
})
// Generate updated .ics with same UID, new SEQUENCE
const icsData = await generateBookingICS(updated, booking.host)
// Send to customer
await emailQueue.add('send-modification-email', {
bookingId,
customerEmail: booking.customer_email,
icsAttachment: icsData,
})
}
Cross-reference: Booking Flow & Lifecycle audit checks modification state transitions. Booking Calendar Availability audit checks time slot recalculation. Database Audit checks schema migration for the sequence column.