Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send Google Calendar invites from node.js using a sendgrid template?

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:

invite screenshot


The flow I need is the following (on the client-side):

  1. In my frontend app User 1 presses the schedule event button;
  2. User 2 presses accept button.
  3. The event gets automatically scheduled and appears in both users google calendars (without any OAuth 2.0 stuff or anything like that. Just 2 button presses).
  4. At the same time the users get emails with event details and the .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 .icss 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
like image 611
Denis Yakovenko Avatar asked Sep 20 '25 09:09

Denis Yakovenko


1 Answers

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",
    },
  ],
});

like image 80
Abir Taheer Avatar answered Sep 22 '25 09:09

Abir Taheer