Mark Andrews
  • training courses
  • book
  • publications
  • presentations
  • notes
  • about

Sending individualized Outlook emails using R

email
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.

Author

Mark Andrews

Published

December 19, 2024

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:

library(Microsoft365R)

outlook <- get_business_outlook() 

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 firstname and grade to create a personalized email message. It does this using the glue command from glue R package, which is for string interpolation. The glue command will substitute {firstname} and {grade} with the values of firstname and grade that are provided as input arguments.
  • Creates the email object with the email recipient being the value of the email input argument, and the body being a html formatted version of msg. For this, we use the markdownToHTML function from the markdown package. It is not necessary to do this, but the message will usually look better if it is converted into html. More generally, because the markdownToHTML will 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 feedback using the add_attachment function.
  • 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.

Back to top

Reuse

CC BY-SA 4.0

Citation

BibTeX 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}
}
For attribution, please cite this work as:
Andrews, Mark. 2024. “Sending Individualized Outlook Emails Using R.” December 19, 2024. https://www.mjandrews.org/notes/outlook_mail_using_R/.

© Mark Andrews 2021-2025. CC BY-SA 4.0.