library(Microsoft365R)
outlook <- get_business_outlook() Sending individualized Outlook emails using R
This note describes how to send emails through Outlook directly from R using the Microsoft365R package. In particular, it shows how you can easily send formatted personalized emails, including with attachments, using this package and a few other tools in R.
The R package Microsoft365R makes it easy to send email via your personal or business (e.g., university) Outlook email from inside R. For example, to send an Outlook email from my university account using R, I would first do the following:
The first time I run get_business_outlook function, it will open a web browser at my university’s usual Microsoft365 sign-in page, where I sign in as normal, which may include using two-factor authentication. Once authenticated, I can use the outlook object in R to send an email. As a very simple example, I could send a one-line email (to myself, in this case) as follows:
email <- outlook$create_email(
body = "This email is sent from R",
subject = "Message sent from R",
to = "mark.andrews@ntu.ac.uk"
)
email$send()This email is sent just as it would have been had you used the Outlook mail client. For example, if you go to your Sent folder in your Outlook client, if will have a copy of the email. This particular email will be plain text, while most emails nowadays are formatted html. Below, we will see how we can send formatted html emails just as easily as plain text ones.
If you have to send the same email to more than one recipient, you can use a vector of email address as the value of the to argument in create_email. But as you can imagine, because this is just regular R code, you also have endless options for sending separate individualized or personalized emails to multiple different recipients. As an example, I will show how you could send assessment feedback to a set of students. Obviously, these emails would need to be individualized; each student needs to be sent a separate email whose contents are specific to just that one student.
Although not necessary, let’s assume all the information about the students — names, email addresses, etc. — and the grades or other feedback that we want to send to them is in data frame. As a minimal example, we could have a tibble data frame named mailmerge_df like this:
mailmerge_df# A tibble: 7 × 5
firstname lastname email grade feedback
<chr> <chr> <chr> <chr> <chr>
1 Alice Ayers alice@uni.edu A+ alice_ayers.pdf
2 Bob Katt bob@uni.edu A+ bob_katt.pdf
3 Carol Singer carol@uni.edu A- carol_singer.pdf
4 Dorothy Oz dorothy@uni.edu B+ dorothy_oz.pdf
5 Jane Doe jane@uni.edu B jane_doe.pdf
6 Mary Betts mary@uni.edu B- mary_betts.pdf
7 Joe Bloggs joe@uni.edu C joe_bloggs.pdf
As you can see, the feedback column gives the name of a pdf document. This contains the detailed feedback to the student and we will attach this file to the email.
The easiest way, at least from my perspective, to iterate through the mailmerge_df data frame and send an email based on the details in each row is to use pwalk, which is a functional that is part of the purrr package (and is also loaded when we load tidyverse). To use pwalk in this way, we need supply a function that will use the values on each row and then create and send the email. For example, we could write a send_email function like this:
send_email <- function(firstname, grade, feedback, email, ...) {
# create a personalized email message
msg <- glue::glue('
Hi {firstname},
Your grade on the exam is {grade}.
best,
Mark
')
# create the email object
email <- outlook$create_email(
body = markdown::markdownToHTML(msg, fragment.only = TRUE),
subject = "Exam grade and feedback",
content_type = "html",
to = email
)
# add an attachment to the email
email$add_attachment(feedback)
# send it
email$send()
}When used with pwalk, this function will take the value of firstname, grade, feedback, and email on each row of mailmerge_df. With this information, it does the following things:
- It uses the value of
firstnameandgradeto create a personalized email message. It does this using thegluecommand fromglueR package, which is for string interpolation. Thegluecommand will substitute{firstname}and{grade}with the values offirstnameandgradethat are provided as input arguments. - Creates the email object with the email recipient being the value of the
emailinput argument, and thebodybeing a html formatted version ofmsg. For this, we use themarkdownToHTMLfunction from themarkdownpackage. It is not necessary to do this, but the message will usually look better if it is converted into html. More generally, because themarkdownToHTMLwill convert any markdown code to html, this allows us to use all the features of markdown to format the message. For example, we could add section labels, bulleted or enumerated list, hyper-links, bold or italics formatting, and so on. - Attaches the file given by the value of
feedbackusing theadd_attachmentfunction. - Finally, it sends the email.
Note that the send_email function has named arguments firstname, grade, feedback, and email, which are the only variables we need from mailmerge. But because mailmerge has more variables than just these three, we need to add an ellipsis to capture these. Nothing is done with these additional variables, but an error will be raised by pwalk if we don’t have a function that captures them.
Now we can send all the emails as follows:
library(purrr)
pwalk(mailmerge_df, send_email)And that’s it.
As a final point, a word of warning. When sending personalized emails, especially when the content is private or confidential, you don’t want to make a mistake and send anything to the wrong person. When writing and testing your code, it is probably wise to use your own email address as the value of to in the create_email function. This way, the emails will be all sent to you. When you are sure everything is working correctly, you can revert to to = email like we see in the send_email function above.
Reuse
Citation
@online{andrews2024,
author = {Andrews, Mark},
title = {Sending Individualized {Outlook} Emails Using {R}},
date = {2024-12-19},
url = {https://www.mjandrews.org/notes/outlook_mail_using_R/},
langid = {en}
}