Sending Smart Invites

Required plan: Emerging

The majority of email clients will automatically add calendar invites to a user’s calendar if you follow these instructions. This will also mean that, where supported, the user will be presented with the Accept/Decline helpers in their email client.

The two key things to do are:

  1. Format the ICS correctly.
  2. Attach the ICS to the email in the correct ways.

Of course, Smart invites created through the Cronofy Smart Invite API Endpoint will be in the correct format. When it comes to attaching the ICS file to the email that’s something your app will need to handle.

We’ve provided some sample code to show how the attachments should be added below. In short you need to attach the file twice. Once inline with 7bit encoding, the other as a standard attachment but with Base64 encoding.

For more detailed best practices, we recommend following the iMIP Best Practices for sending invites. This will guarantee the widest support.

# Using Rails

class SmartInviteMailer < ActionMailer::Base
  def invite(smart_invite)
    event_ics = smart_invite.attachments.icalendar

    method = 'REQUEST' # method=CANCEL in the case of cancellations

    attachments.inline["invite.ics"] = {
      content_type: "text/calendar; charset=UTF-8; method=#{method}",
      encoding: '7bit',
      content: event_ics,
    }

    attachments["invite.ics"] = {
      content_type: "application/ics; charset=UTF-8; method=#{method}",
      encoding: 'base64',
      content: Base64.encode64(event_ics),
    }

    invite_mail = mail(
      to: smart_invite.recipient.email,
      subject: "Smart Invite Example",
      body: "Here is your invite."
    )

    # In Rails >= 6, we need to change ActionMailers default inline attachment content-type
    # to match the iMIP best practices for sending calendar invites, so that Outlook can show
    # the Accept/Tentative/Decline buttons in line.
    invite_mail
      .parts
      .select { |part| contains_calendar_subpart?(part) }
      .each { |part| replace_multipart_type(part) }
  end

  private def contains_calendar_subpart?(part)
    part.parts.any? { |subpart| subpart.content_type.start_with? "text/calendar" }
  end

  private def replace_multipart_type(part)
    new_content_type = part.content_type.sub(%r{multipart/[\w]+}, "multipart/alternative")
    part.content_type = new_content_type
    part
  end
end
// Using the https://github.com/PHPMailer/PHPMailer script

require '../vendor/autoload.php';

$mail = new PHPMailer();

$mail->setFrom('application@example.com');
$mail->addAddress($invite->recipient->email);
$mail->Subject = 'Smart Invite Example';
$mail->Body = 'Here is your invite';

$ics = $invite->attachments->icalendar;

$method = 'REQUEST' // method=CANCEL in the case of cancellations

$mail->addAttachment($ics, 'invite.ics', '7bit', "text/calendar; charset=UTF-8; method=$method", 'inline');
$mail->addAttachment(base64_encode($ics), 'invite.ics', 'base64', "application/ics; charset=UTF-8; method=$method", 'attachment');

$mail->send();
// Using the https://nodemailer.com/about/ library

const nodemailer = require('../lib/nodemailer');

// TODO configure transport as required
let transporter = nodemailer.createTransport({});

let ics = invite.attachments.icalendar;

let method = 'REQUEST'; // method=CANCEL in the case of cancellations

let message = {
    to: invite.recipient.email,
    subject: 'Smart Invite Example',
    text: 'Here is your invite',
    attachments: [
        {
            filename: 'invite.ics',
            content: ics,
            contentType: `text/calendar; charset=UTF-8; method=${method}`,
            contentDisposition: 'inline'
        },
        {
            filename: 'invite.ics',
            content: Buffer.from(ics, 'base64'),
            contentType: `application/ics; charset=UTF-8; method=${method}`,
            encoding: 'base64'
        },
    ]
};

transporter.sendMail(message, (error, info) => {
    transporter.close();
});
var message = new MailMessage("application@example.com", invite.Recipient.Email);
message.Subject = "Smart Invite Example";
message.Body = "Here is your invite";

var ics = invite.Attachments.ICalendar;

var method = "REQUEST"; // method=CANCEL in the case of cancellations

var inlineAttachment = new Attachment(
    new MemoryStream(Encoding.UTF8.GetBytes(ics)),
    "invite.ics",
    $"text/calendar; charset=UTF-8; method={method}"
);
inlineAttachment.ContentDisposition.Inline = true;

var attachment = new Attachment(
    new MemoryStream(Encoding.UTF8.GetBytes(ics)),
    "invite.ics",
    $"application/ics; charset=UTF-8; method={method}"
);

message.Attachments.Add(inlineAttachment);
message.Attachments.Add(attachment);