Sending Outlook appointments with Python

Python allows you to send calendar appointments (invitations / events) directly from your code. It is quite easy to create a new appointment in the standard iCalendar format (ics). You can do it by hand or use a convenient icalendar open source module. In order to convince Outlook, however, to present the iCalendar events as native calendar appointments, you will need to make some effort. Fortunately, other people have already collected all the necessary pieces. Below is a working example for sending Outlook-friendly invitation from Python. In principle, you could use some of the information here to send similar invitation using different API, for example .NET.

Note, that you need to supply the relevant timezone information with your dates, otherwise the result might be different from what you expect.

  1. import email.MIMEText
  2. import email.MIMEBase
  3. from email.MIMEMultipart import MIMEMultipart
  4. import smtplib
  5. import datetime as dt
  6. import icalendar
  7. import pytz
  9. def sendAppointment(self, subj, description):
  10.   # Timezone to use for our dates - change as needed
  11.   tz = pytz.timezone("Europe/London")
  12.   reminderHours = 1
  13.   startHour = 7
  14.   start = tz.localize(dt.datetime.combine(, dt.time(startHour, 0, 0)))
  15.   cal = icalendar.Calendar()
  16.   cal.add('prodid', '-//My calendar application//')
  17.   cal.add('version', '2.0')
  18.   cal.add('method', "REQUEST")
  19.   event = icalendar.Event()
  20.   event.add('attendee', self.getEmail())
  21.   event.add('organizer', "")
  22.   event.add('status', "confirmed")
  23.   event.add('category', "Event")
  24.   event.add('summary', subj)
  25.   event.add('description', description)
  26.   event.add('location', "Room 101")
  27.   event.add('dtstart', start)
  28.   event.add('dtend', tz.localize(dt.datetime.combine(, dt.time(startHour + 1, 0, 0))))
  29.   event.add('dtstamp', tz.localize(dt.datetime.combine(, dt.time(6, 0, 0))))
  30.   event['uid'] = getUniqueId() # Generate some unique ID
  31.   event.add('priority', 5)
  32.   event.add('sequence', 1)
  33.   event.add('created', tz.localize(
  35.   alarm = icalendar.Alarm()
  36.   alarm.add("action", "DISPLAY")
  37.   alarm.add('description', "Reminder")
  38.   #alarm.add("trigger", dt.timedelta(hours=-reminderHours))
  39.   # The only way to convince Outlook to do it correctly
  40.   alarm.add("TRIGGER;RELATED=START", "-PT{0}H".format(reminderHours))
  41.   event.add_component(alarm)
  42.   cal.add_component(event)
  44.   msg = MIMEMultipart("alternative")
  46.   msg["Subject"] = subj
  47.   msg["From"] = "{0}".format(self.creator)
  48.   msg["To"] = self.getEmail()
  49.   msg["Content-class"] = "urn:content-classes:calendarmessage"
  51.   msg.attach(email.MIMEText.MIMEText(description))
  53.   filename = "invite.ics"
  54.   part = email.MIMEBase.MIMEBase('text', "calendar", method="REQUEST", name=filename)
  55.   part.set_payload( cal.to_ical() )
  56.   email.Encoders.encode_base64(part)
  57.   part.add_header('Content-Description', filename)
  58.   part.add_header("Content-class", "urn:content-classes:calendarmessage")
  59.   part.add_header("Filename", filename)
  60.   part.add_header("Path", filename)
  61.   msg.attach(part)
  63.   s = smtplib.SMTP('localhost')
  64.   s.sendmail(msg["From"], [msg["To"]], msg.as_string())
  65.   s.quit()

Cancelling an existing event

You can send a cancel for an existing appointment as well. In order to do this, change method in both places in the code above from REQUEST to CANCEL, and set status</status> to <code>CANCELLED. You must ensure you use the same id as you used to create your original event.

No-response events

If you don't want the appointment participant's Outlook to send you a reply when he or she accepts, you can use the following code:



I would like to use the script but I am new to python. Could you add and example on how to use it. I am not sure how to specify the sender's email. I tried to setup a structure appointment and add to it the time which seems to work, but not sure how to add the email of the sender that is called using self.getEmail().


Look at line 44:

  1. msg["From"] = "{0}".format(self.creator)

Replace the right hand side with a call to your method.

My bad I meant to say the recipient email not the sender. I am not sure if there is an elegant way I can pass a list of recipients. Based on the code there is self.getEmail(). but I am not sure if getEmail() is something that exists or I have to create it.


Ok, look at line 62 - the function which sends the e-mail expects a list of strings. So you can pass it in whichever way you like. My version of the script has only one recipient and it is indeed is passed through the class method getEmail(), but you can implement it the most convenient way for your specific case.

More information in the documentation for smtplib and MIME* modules

Hi Andre, I'm making a Python script that runs in a Ubunto server.
My goal is to read events on a outlook callendar and send that info to a SQL DB.

I have a script working with google API. But I don't want to use google anymore and I'm having trouble to get info on Outlook API.
Can you send me some hints how to make a connection with outlook callendar?

Sounds as a non-trivial exercise. You essentially want to connect to your Microsoft Exchange server from Linux. This might be either hard or next to impossible, especially if you want to use LDAP authentication. If I were you, I would write this utility in .NET and run it on Windows, as a service if need be. All this based on the information in your comment, of course.

Your particular version of Exchange might happen to expose some RESTful API or you can probably access some folders via IMAP, but once again, authentication might prove the biggest hurdle.

Update: it looks like someone has created a Python API for Exchange - see if it fits the bill:

Add new comment