I'm using iCalendar to schedule events for Gmail users. However, I want to be able to send updates, if/when the event changes. I read that I must use METHOD:REQUEST in order to allow updates.
However, when I add the METHOD:REQUEST to my ics file, Gmail stops recognizing it as a calendar invite.
Here is the working example WITHOUT "METHOD:REQUEST"
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Cratic//Cratic Huddle//EN
NAME:Cratic Huddle
X-WR-CALNAME:Cratic Huddle
TIMEZONE-ID:Europe/Berlin
X-WR-TIMEZONE:Europe/Berlin
BEGIN:VEVENT
UID:[email protected]
SEQUENCE:0
DTSTAMP:20210208T130601Z
DTSTART:20210219T133059Z
DTEND:20210219T140059Z
SUMMARY:Cratic Huddle
DESCRIPTION:This is the information
ORGANIZER;CN="Izzi":mailto:[email protected]
ATTENDEE;ROLE=REQ-PARTICIPANT:MAILTO:[email protected]
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER:-PT15M
DESCRIPTION:Cratic Huddle
END:VALARM
BEGIN:VALARM
ACTION:AUDIO
TRIGGER:-PT15M
ATTACH;VALUE=URI:Basso
END:VALARM
URL;VALUE=URI:cratic.ai
END:VEVENT
END:VCALENDAR
The above works perfectly; Gmail pops up with a button asking if I want to accept.
Here is the NOT working example WITH "METHOD:REQUEST"
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Cratic//Cratic Huddle//EN
METHOD:REQUEST
NAME:Cratic Huddle
X-WR-CALNAME:Cratic Huddle
TIMEZONE-ID:Europe/Berlin
X-WR-TIMEZONE:Europe/Berlin
BEGIN:VEVENT
UID:[email protected]
SEQUENCE:0
DTSTAMP:20210208T125937Z
DTSTART:20210219T133059Z
DTEND:20210219T140059Z
SUMMARY:Cratic Huddle
DESCRIPTION:This is the information
ORGANIZER;CN="Izzi":mailto:[email protected]
ATTENDEE;ROLE=REQ-PARTICIPANT:MAILTO:[email protected]
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER:-PT15M
DESCRIPTION:Cratic Huddle
END:VALARM
BEGIN:VALARM
ACTION:AUDIO
TRIGGER:-PT15M
ATTACH;VALUE=URI:Basso
END:VALARM
URL;VALUE=URI:cratic.ai
END:VEVENT
END:VCALENDAR
This generates a blank email in google, with the .ics file attached. It does not appear in the calendar or anywhere else.
These two iCalendar files are identical in every other way. But for some reason adding the METHOD:REQUEST is killing the ics file / google's ability to read it.
Why??
Ok - After many hours of reading RFC5546, Stackoverflow, and many other blogs, I finally have the answers.
Scope:
There are two fundamental principles that are critical to making this work:
The contents of the .ics file must very closely match the .ics file of the Gmail / Outlook community, this includes the parameters and the order of the parameters.
The way the .ics file is attached to the email transmission is odd but critical: (a) the .ics file must be turned into 'base64', and (b) the headers of the file need to give a type: 'text/calendar;method=REQUEST;name="file.ics"'
Below I will give examples of each, which I hope save others time on this process.
.ics Content Example:
let iCal =
`BEGIN:VCALENDAR
PRODID:-//Cratic//_Scheduler//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
DTSTART:${DTStart}
DTEND:${DTEnd}
DTSTAMP:${DTStamp}
ORGANIZER;CN=name:mailto:[email protected]
UID:${event._id}@url.com
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;[email protected];X-NUM-GUESTS=0:mailto:[email protected]
CREATED:${DTStamp}
DESCRIPTION:${event.body}
LAST-MODIFIED:${DTStamp}
LOCATION:${location}
SEQUENCE:${sequence}
STATUS:CONFIRMED
SUMMARY:${event.title}
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR`
Please note: the above ordering is very important! And you must have no dead spaces around the text, even one extra space before or after will disrupt the email client. Also, this is a reusable dynamic iCal event template, as long as I keep a record of the event._id, I can use that _id to send revisions to all my clients.
Attaching .ics to Email Client:
// convert the invite to be base64 for attachment
const buf = Buffer.from(iCal.toString(), 'utf-8');
const base64Cal = buf.toString('base64');
// build the email
const sendEmail = async () => {
const res = await client.transmissions.send({
recipients: [{ address: '[email protected]' }],
content: {
from: '[email protected]',
subject: req.body.a.title,
text: req.body.a.body,
attachments: [
{
name: 'invite.ics',
type: 'text/calendar;method=REQUEST;name=\"invite.ics\"',
data: base64Cal,
},
],
},
options: { sandbox: false },
});
}
// send the email
sendEmail();
Please note: The attachment type is critical for Outlook to recognize the event.
Although I'm sure there are other / better ways to integrate with these services, I found the above steps allowed me to accomplish my scope.