I'm trying to send ics
calendar invites to users from node.js server with the goal of getting email clients (e.g. Gmail/Outlook etc) to render them as actual invites and not just usual file attachments.
This is very similar to what Calend.ly does.
So, basically, I'm trying to get something like this in Gmail:
The flow I need is the following (on the client-side):
schedule event
button;accept
button..ics
file attachment. But the invites should already be in their calendars.How can I do that?
If I need to use Google Calendar API for this, then how should I at least approach this if I can't have any OAuth 2.0 stuff for my users?
What I'm currently doing is I'm generating .ics
files and sending them using SendGrid
. However, with .ics
s I can't achieve a result like in the image above. These .ics
files are not invites, they are merely attachments.
So I was wondering how should I approach this at all? Is using Google Calendar API the right way to implement this? If yes, then how can it be done server-side-only without making users authenticate?
I know it's possible because Calendly does exactly this. Users just enter their emails into the input field, press submit
and the event invites automatically appear in their Google calendars.
How could this be implemented?
Maybe I don't get something, but generating .ics
files doesn't seem to do the trick, and at the same time Google Calendar API does not appear to be the solution as well because of OAuth2 authentication.
In their docs they say:
Your application must use OAuth 2.0 to authorize requests. No other authorization protocols are supported.
Here's the code I'm using to send emails with .ics
attachments (there's also a template on SendGrid
side, hence the dynamicTemplateData
prop):
const SendGrid = require("@sendgrid/mail");
const attachment = {
filename: 'invite.ics',
name: 'invite.ics',
content: Buffer.from(data).toString('base64'),
disposition: 'attachment',
contentId: uuid(),
type: 'text/calendar; method=REQUEST',
};
SendGrid.send({
attachments: [attachment],
templateId,
from: {
email: config.emailSender,
name: config.emailName,
},
to: user.email,
dynamicTemplateData: {
...rest,
user,
},
headers: {
'List-Unsubscribe': `<mailto:unsubscribe.link`,
},
});
And here's how my .ics
attachment files look like:
BEGIN:VCALENDAR
PRODID:-//Organization//Organization App//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
DTSTART:20210420T180000Z
DTEND:20210420T190000Z
DTSTAMP:20210418T201735Z
ORGANIZER;CN=Denis Yakovenko:MAILTO:[email protected]
UID:25bb4d3e-b69d-46b0-baea-489c71c48c88
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Denis Yakovenko;X-NUM-GUESTS=0:MAILTO:[email protected]
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=John Smith;X-NUM-GUESTS=0:MAILTO:[email protected]
CREATED:20210418T201735Z
DESCRIPTION:my description
LAST-MODIFIED:20210418T201735Z
LOCATION:https://virtual.location.com
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:my summary
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
I referenced this issue on the sendgrid repository as well as converted the solution from the ruby version to javascript.
The following worked for me successfully and Google generated the event preview.
const ics = require("ics");
const sendgrid = require("@sendgrid/mail");
const event = {
start: [2018, 5, 30, 6, 30],
duration: { hours: 6, minutes: 30 },
title: "Bolder Boulder",
description: "Annual 10-kilometer run in Boulder, Colorado",
location: "Folsom Field, University of Colorado (finish line)",
url: "http://www.bolderboulder.com/",
geo: { lat: 40.0095, lon: 105.2669 },
categories: ["10k races", "Memorial Day Weekend", "Boulder CO"],
status: "CONFIRMED",
busyStatus: "BUSY",
organizer: { name: "Admin", email: "[email protected]" },
attendees: [
{
name: "Adam Gibbons",
email: "[email protected]",
rsvp: true,
partstat: "ACCEPTED",
role: "REQ-PARTICIPANT",
},
],
};
const { value } = ics.createEvent(event);
sendgrid.setApiKey(process.env.SENDGRID_API_KEY);
sendgrid.send({
to: "[email protected]",
from: "[email protected]",
subject: "This is an example email 3",
content: [
{
type: "text/plain",
value: "Plain Content",
},
{
type: "text/html",
value: "HTML Content",
},
{
type: "text/calendar; method=REQUEST",
value: value,
},
],
attachments: [
{
content: Buffer.from(value).toString("base64"),
type: "application/ics",
name: "invite.ics",
filename: "invite.ics",
disposition: "attachment",
},
],
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With