From 188aaa18573f733a60065a5317449de38c0312d9 Mon Sep 17 00:00:00 2001 From: Gaurav Kumar Date: Sun, 5 Jul 2026 15:53:53 +0530 Subject: [PATCH] fix(emails): send METHOD:CANCEL in ICS for cancelled bookings (#29708) Fixes an issue where cancellation emails were sending ICS files with METHOD:REQUEST instead of METHOD:CANCEL. As per RFC 5546, calendar clients only process cancellations correctly when METHOD:CANCEL is used, otherwise cancelled events remain in attendee calendars with active reminders. - Use METHOD:CANCEL for cancelled booking ICS files - Keep METHOD:REQUEST for non-cancelled booking invitations - Preserve STATUS:CANCELLED and sequence handling - Add tests to verify the generated ICS method for cancellations --- packages/emails/lib/generateIcsFile.ts | 2 +- packages/emails/lib/generateIcsString.test.ts | 3 +++ packages/emails/lib/generateIcsString.ts | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/emails/lib/generateIcsFile.ts b/packages/emails/lib/generateIcsFile.ts index 9047b23499..7e0f79be0d 100644 --- a/packages/emails/lib/generateIcsFile.ts +++ b/packages/emails/lib/generateIcsFile.ts @@ -36,6 +36,6 @@ export default function generateIcsFile({ status, t, }), - method: "REQUEST", + method: status === "CANCELLED" ? "CANCEL" : "REQUEST", }; } diff --git a/packages/emails/lib/generateIcsString.test.ts b/packages/emails/lib/generateIcsString.test.ts index a023254803..bd673d5c4d 100644 --- a/packages/emails/lib/generateIcsString.test.ts +++ b/packages/emails/lib/generateIcsString.test.ts @@ -46,6 +46,9 @@ const testIcsStringContains = ({ } expect(icsString).toEqual(expect.stringContaining(`DTEND:${DTEND}`)); expect(icsString).toEqual(expect.stringContaining(`STATUS:${status}`)); + expect(icsString).toEqual( + expect.stringContaining(`METHOD:${status === "CANCELLED" ? "CANCEL" : "REQUEST"}`) + ); // Getting an error expected icsString to deeply equal stringMatching // for (const attendee of event.attendees) { // expect(icsString).toEqual( diff --git a/packages/emails/lib/generateIcsString.ts b/packages/emails/lib/generateIcsString.ts index 658fb35cdf..d327b55227 100644 --- a/packages/emails/lib/generateIcsString.ts +++ b/packages/emails/lib/generateIcsString.ts @@ -61,7 +61,7 @@ const generateIcsString = ({ const location = getVideoCallUrlFromCalEvent(event) || event.location; // Taking care of recurrence rule - let recurrenceRule: string | undefined = undefined; + let recurrenceRule: string | undefined; const icsRole: ParticipationRole = "REQ-PARTICIPANT"; if (event.recurringEvent?.count) { // ics appends "RRULE:" already, so removing it from RRule generated string @@ -107,7 +107,7 @@ const generateIcsString = ({ : []), ], location: location ?? undefined, - method: "REQUEST", + method: status === "CANCELLED" ? "CANCEL" : "REQUEST", status, ...(event.hideCalendarEventDetails ? { classification: "PRIVATE" } : {}), busyStatus: "BUSY",