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.

import email.MIMEText
import email.MIMEBase
from email.MIMEMultipart import MIMEMultipart
import smtplib
import datetime as dt
import icalendar
import pytz

# Imagine this function is part of a class which provides the necessary config data
def send_appointment(self, attendee_email, organiser_email, subj, description, location, start_hour, start_minute):
  # Timezone to use for our dates - change as needed
  tz = pytz.timezone("Europe/London")
  start = tz.localize(dt.datetime.combine(, dt.time(start_hour, start_minute, 0)))
  # Build the event itself
  cal = icalendar.Calendar()
  cal.add('prodid', '-//My calendar application//')
  cal.add('version', '2.0')
  cal.add('method', "REQUEST")
  event = icalendar.Event()
  event.add('attendee', attendee_email)
  event.add('organizer', organiser_email)
  event.add('status', "confirmed")
  event.add('category', "Event")
  event.add('summary', subj)
  event.add('description', description)
  event.add('location', location)
  event.add('dtstart', start)
  event.add('dtend', tz.localize(dt.datetime.combine(, dt.time(start_hour + 1, start_minute, 0))))
  event.add('dtstamp', tz.localize(dt.datetime.combine(, dt.time(6, 0, 0))))
  event['uid'] = self.get_unique_id() # Generate some unique ID
  event.add('priority', 5)
  event.add('sequence', 1)
  event.add('created', tz.localize(

  # Add a reminder
  alarm = icalendar.Alarm()
  alarm.add("action", "DISPLAY")
  alarm.add('description', "Reminder")
  # The only way to convince Outlook to do it correctly
  alarm.add("TRIGGER;RELATED=START", "-PT{0}H".format(reminder_hours))

  # Build the email message and attach the event to it
  msg = MIMEMultipart("alternative")

  msg["Subject"] = subj
  msg["From"] = organiser_email
  msg["To"] = atendee_email
  msg["Content-class"] = "urn:content-classes:calendarmessage"


  filename = "invite.ics"
  part = email.MIMEBase.MIMEBase('text', "calendar", method="REQUEST", name=filename)
  part.set_payload( cal.to_ical() )
  part.add_header('Content-Description', filename)
  part.add_header("Content-class", "urn:content-classes:calendarmessage")
  part.add_header("Filename", filename)
  part.add_header("Path", filename)

  # Send the email out
  s = smtplib.SMTP('localhost')
  s.sendmail(msg["From"], [msg["To"]], msg.as_string())

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 to 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 hope these code snippets and guidelines will prove helpful in your automation endeavours :)

Categories: None |

7 comments have been posted.

    March 24, 2017, 4:57 p.m. - LunarAnnon336  
    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?
    March 27, 2017, 6:08 a.m. - Andre  
    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: [](
    Oct. 16, 2013, 3:53 p.m. - Anonymous  
    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. Thanks
    Oct. 17, 2013, 6:27 a.m. - Andre  
    More information in the documentation for smtplib and MIME* modules
    Oct. 17, 2013, 6:26 a.m. - Andre  
    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.
    Oct. 16, 2013, 6:40 a.m. - Anonymous  
    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(). Thanks
    Oct. 16, 2013, 9:20 a.m. - Andre  
    Look at line 44: <code> msg["From"] = "{0}".format(self.creator) </code> Replace the right hand side with a call to your method.
Your email: we will send you a confirmation link to this address to confirm your identity and to prevent robot posting
Get in touch
Follow updates

Join our social networks and RSS feed to keep up to date with latest news and publications