Generate Outlook Calendar Events with PHP and iCalendar

The internal web application at my company tends to send out a lot of email notifications. Some of them are reminders about various deadlines. Recently I got a feature request to allow people to add these reminders as events to their Outlook calendar.

Outlook supports two calendaring formats vCalendar and iCalendar. I opted to go with the later one because it is an open standard and at least in theory should work with other calendaring applications. The apps which support iCalendar standard can export their events as .ics files which can be seamlessly exchanged between the applications. So my task was to generate such a file on the fly and either link to it, or attach it to the reminder emails.

You can actually serve the ics file directly from a php page by telling the browser you are sending it a text/Calendar mimetype. You do this by sending appropriate headers:

header("Content-Type: text/Calendar");
header("Content-Disposition: inline; filename=calendar.ics");

This will force the browser to interpret the input as a ics file and will show the user the dialog asking him to open the file in an external application. Then you simply need to output the appropriate data in the iCalendar format, which is relatively simple. Here is a very simple event you can copy and paste into your PHP file:

header("Content-Type: text/Calendar");
header("Content-Disposition: inline; filename=calendar.ics");
echo "VERSION:2.0\n";
echo "PRODID:-//Foobar Corporation//NONSGML Foobar//EN\n";
echo "METHOD:REQUEST\n"; // requied by Outlook
echo "BEGIN:VEVENT\n";
echo "UID:".date('Ymd').'T'.date('His')."-".rand()."\n"; // required by Outlok
echo "DTSTAMP:".date('Ymd').'T'.date('His')."\n"; // required by Outlook
echo "DTSTART:20080413T000000\n"; 
echo "SUMMARY:TEST\n";
echo "DESCRIPTION: this is just a test\n";
echo "END:VEVENT\n";

This will generate an event called TEST to happen on April 13 2008. Note the fields which I marked as required by Outlook. This is important. They are not required by the iCalendar spec so some examples out there skip them. If you do however you will get this lovely error message when you open the file in Outlook:

Microsoft Office Outlook cannot import vCalendar file. This error can appear if you have attempted to save a recurring Lunar appointment in iCalendar format. To avoid this error, set the appointment option to Gregorian instead of Lunar.

Gotta love Microsoft and their through, meaningful error messages. In this case the message is absolutely meaningless and the problem has nothing to do with “Lunar appointments” whatever the hell those are. You are simply missing the METHOD, DTSTAMP and UID fields.

I used REQUEST for the METHOD field but you can also try PUBLISH and few others. As far as I can tell the difference here is how the calendaring application will present the opened file to the user. If you use REQUEST it will give them a button to Accept or Reject the event. If you use PUBLISH they will be asked to save or cancel. Either way is acceptable by me.

The DTSTAMP field is, as the name suggest the date stamp when this event request was generated. As you can see I’m generating it on the fly.

The UID is supposed to be a unique identifier for this event, for some internal use. It is recommend that the right hand side of the UID be the host name or some unique identifier for the domain. I’m not exactly sure what the significance of this field is – I suspect it may be meaningful in certain situations, where the calendaring app is internally sorting the data. As you can see I use the date stamp on the right, followed by a randomly generated number, followed by host name which is pretty much what they advocate in the spec. It should yield UID’s that are sufficiently unique for each event. Some examples out there just use 0 or 1 here which doesn’t seem to be following the specification, but nevertheless it works just fine when you test it. However I figured it’s better to do it right, rather than run into weird issues much, much later.

I think the rest of the fields are pretty much self explanatory, with exception of PRODID. That’s the funny one. It is supposed to uniquely identify the product which has generated this event. In other words it is the place to pimp out your company and your software. And yet, all the online examples use the same 2 or 3 strings, showing blatantly that most people just copy and paste a working example into their production code and then blog about it without ever actually bothering to read the spec itself.

This is how I did it. I’m sure there are more elegant ways to accomplish this, but my email reminders will simply link to an appropriate php page which will fetch relevant data from the database and build appropriate ics file. I hope someone out there will find this writeup useful. And if I see “Foobar Corporation” in the future writeups, I will know you copied from me, and didn’t even bother reading the rest of the post. ;)

[tags]ics, icalendar, php, ical, calendar, calendar events, outlook events[/tags]

This entry was posted in programming and tagged . Bookmark the permalink.

36 Responses to Generate Outlook Calendar Events with PHP and iCalendar

  1. jambarama UNITED STATES Mozilla Firefox Windows Terminalist says:

    Clever – nicely done. I know you don’t get as many comments on these more specific technical implementations, but I love reading them, please don’t stop posting them.

    Reply  |  Quote
  2. Luke Maciak UNITED STATES Mozilla Firefox Windows Terminalist says:

    Thanks! Glad to hear someone is reading them. :)

    Sometimes the more technical things get comments after a while – once they get indexed by Google and they gravitate towards first page for specific query strings. People are then just stumbling in from Google looking for a particular solution or answer to a problem and they sometimes leave a comment then.

    Reply  |  Quote
  3. prettypretty HONG KONG Internet Explorer Windows says:

    Many thanks. That is exactly what I am looking for.
    I changed this line “Foobar Corporation”. LOL~~~

    Reply  |  Quote
  4. Gene Z UNITED STATES Mozilla Firefox Windows says:

    UID problem
    I’m trying to send a new meeting invite via email from PHP, similar to the post, and things nearly work. When I open the ics attachment, Outlook tells me “This meeting is not in the Calendar; it may have been moved or deleted.”

    If I were to eliminate the UID, the attachment doesn’t work. If I use the post’s example, a random string, MS formatted random GUID, 0, or other shots in the dark, I get that “not in Calendar” problem.

    Sounds to me like my message is attempting to reference a meeting that does not yet exist – but I want this to create a new Outlook appointment.

    Any thoughts on how to accomplish that?
    (and sure enough, Google indexing of a specific tech problem led me here)

    Reply  |  Quote
  5. Nancy Blackwell UNITED STATES Internet Explorer Windows says:

    I am working on the same problem – sending email invites to Outlook via our java application. I came across the same solution that you have (the format and required fields for Outlook). My problem is with recurring events, I can’t seem to get Outlook to pick up the fact that my application send a request for a recurring meeting. It will still just show the first meeting with an accept/reject button, no information about the recurrence. Have you tried this or come up with a solution?

    Reply  |  Quote
  6. Luke Maciak UNITED STATES Mozilla Firefox Ubuntu Linux Terminalist says:

    @Nancy – I haven’t tried implementing it, but have you tried using the RRULE field? Also read up on SEQUENCE and RECURRENCE-ID fields – I suspect that Outlook needs a way to create unique references for each recurring instance and the key is in these fields.

    But, again I haven’t tested it. If you figure it out, please let us know the solution!

    Reply  |  Quote
  7. Wolf BELGIUM Mozilla Firefox Windows says:

    @Gene Z
    Can you post your mail function out here..
    I’m looking for this MS Outlook hack for few weeks.
    finaly google gave me back some result.

    I need to send out meeting request to persons. one at a time.

    Perfect Post :)

    Reply  |  Quote
  8. Gene Z UNITED STATES Mozilla Firefox Windows says:

    Sure, for what it’s worth. The code below indeed sent a meeting invite, but it was not linked to an actual appointment in Outlook. As fate would have it, I had to switch to Java anyway, so never advanced the PHP code any further. What I needed was to send invites for an appointment on our Exchange server, so to do that I need a package to interface with Exchange (no way I’m tackling a MS API directly).

    Hope this helps in some way:

      $to   = "";
      $from = "Gene Z";
      $subj = "my test subject";
      $msg  = "this is the email body";
      $file_att_type = "text/calendar; method=REQUEST";
      $file_att_name = "att.ics";
      $mime_boundary = "==Multipart_Boundary_x{" . md5(mt_rand()) . "}x";
      $header = "From: " . $from . "\r\n" .
                "MIME-Version: 1.0\r\n" .
                "Content-Type: multipart/mixed; boundary=\"{$mime_boundary}\"";
      $txt_msg = "this is my text message, to be attached\r\n" .
                 "not much to it, eh.";
      //Various ways to creat a new, random UID
      $cal_uid = "{" . uniqid(mt_rand(), true) . "}";
      $cal_uid = "{" . date('Ymd').'T'.date('His')."-".rand()."}";
      $cal_uid = com_create_guid();
      $msg .= "\nwith uid of $cal_uid\n";
      $cal_msg =
    PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN
    UID:" . $cal_uid . "
    LOCATION:my meeting location
    DESCRIPTION:meeting description, line 1 of 2.\nline 2 of 2.\n\n
    SUMMARY:meeting subject
      $msg = "This is a multi-part message in MIME format.\n\n" .
             "--{$mime_boundary}\n" . 
             "Content-Type: text/plain; charset=\"iso-8859-1\"\n" . 
             "Content-Transfer-Encoding: 7bit\n\n" . 
             $msg .
             "\n\n--{$mime_boundary}\n" . 
             "Content-Type: $file_att_type; name=$file_att_name\n" . 
             "Content-Disposition: attachment; filename=$file_att_name\n" .
             "Content-Transfer-Encoding: base64\n\n" . 
             chunk_split(base64_encode($cal_msg)) . "\n\n" . 
      if (mail($to, $subj, $msg, $header, "")) {
        echo "test email sent";
      } else {
        echo "test email failed";
    Reply  |  Quote
  9. Gene Z UNITED STATES Mozilla Firefox Windows says:

    hm, some characters were dropped when the code sample got posted. When you see a stray “r” or “n”, assume it should have a backslash in front of it. Also, the value of “from” should have an email address in angle brackets after the text name.

    Reply  |  Quote
  10. Terry S UNITED KINGDOM Internet Explorer Windows says:

    Thanks for the info, does anyone know how to create these ics files so that they can be assigned to another user?

    I have developed a PHP app and we use Outlook with the shared calendar system. We book in appointments for our staff but was wondering whether it would be possible to automatically load the appointments into the users calendar rather than my own.

    I need an answer to this fairly quickly, please please help!!!

    Reply  |  Quote
  11. Luke Maciak UNITED STATES Mozilla Firefox Ubuntu Linux Terminalist says:

    No clue. What happens when you specify these users as attendees for your event? Does it show up on their calendars?

    If not then I have no clue. Also I have no shared calendar setup to test it with so good luck with that.

    Reply  |  Quote
  12. dm UNITED STATES Internet Explorer Windows says:

    is there a way you can push this to receiver’s calendar?

    Reply  |  Quote
  13. Terry S UNITED KINGDOM Internet Explorer Windows says:

    I think further testing on my side needs to be done!

    Reply  |  Quote
  14. Kris Anderson INDIA Internet Explorer Windows says:

    Hi ,

    I need to send meeting requests(outlook) to different people through PHP.
    I figure sending a .ics file as attachment to an email will do the job.
    i am stuck, my implementation will send a mail, with the contents of the .ics file as part of the body of the mail. If somebody has got this working, can you please help out?

    Reply  |  Quote
  15. Terry S UNITED KINGDOM Internet Explorer Windows says:

    Hi Kris

    I managed to get my php app working with a downloadable ics file. I am only struggling with assigning this to a person.

    I would suggest you read the rfc2445 and the wikipedia on icalendar files. Which part are you struggling with exactly? I will try my best to point you in the right direction.

    Reply  |  Quote
  16. Sunderland COSTA RICA Mozilla Firefox Windows says:

    The ics file directly, not like attach !!!

    Reply  |  Quote
  17. Kobus Bensch UNITED KINGDOM Mozilla Firefox Windows says:


    I have been creating vcal .vcs files. the one problem I have is that the users use outlook. when i create a weblink to the vcs file and they use ie to browse to it it opens in outlook and creates the entry, BUT if they use ff then it opens the vcs in a seperate browser window. Is there anyway I can force it that when they browse to the vcs file that it opens outlook and create the cal entry and not open ff?

    Thanks in advance.


    Reply  |  Quote
  18. Alex INDIA Mozilla Firefox Windows says:


    I am trying to add more then 2 Events in the same posted example. But unfortunately getting only one Event in the result.

    I think we can add multiple Event in it and can view it by clicking on the “Next Item” and “Prev Item”.

    Can any one please help me

    Reply  |  Quote
  19. dhinesh INDIA Mozilla Firefox Windows says:

    Hi to All,

    I have successfully created the iCal file, now I want to insert the event timings to the user’s outlook calendar, please let me know is there any way in programmatically?

    Expecting your reply

    Reply  |  Quote
  20. GeneZ UNITED STATES Mozilla Firefox Windows says:

    Same posting, but new problem.

    I’m sending an invite to a normal person and a non-person Outlook user that has an auto-accept agent in place. This is a communal calendar that is referenced by many, but nobody logs on as that user.

    The appointment to the person shows up just fine, but the appointment for the non-person has no reminder or description, and the subject looks like this:
    Another oddity is that the organizer appears twice in the list of attendees that the non-person sees.

    The auto-accept agent itself seems functional, because the event organizer is also an auto-accepting non-person, and the event shows up correctly there. So it’s the vcalendar content that is suspect. The content of the invitation attachment is below.

    Any ideas out there as to what is going wrong?

    PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN
    DESCRIPTION:My description\n

    Reply  |  Quote
  21. Andy Schuler UNITED STATES Mozilla Firefox Windows says:

    I’m going to give this a try in a bit, but I stumbled in from google and just wanted to thanks for the article.

    Reply  |  Quote
  22. Thank you very much! I have tested it with Google Calendar and works great! Also since it is meant for Outlook I’m sure it will work there too

    Reply  |  Quote
  23. Karen UNITED STATES Mozilla Firefox Mac OS says:

    I’m going a little crazy with Outlook and time zones, and even though it’s only tangentially related to your post I thought I’d comment in case you can point me in the right direction to look for help. (Maybe you know of an appropriate forum.)

    I have a little script that outputs .ics files, and I serve it from the web as you do. It references the Olson time zone and has every event in the local time, which works great with calendaring applications other than Outlook. Outlook seems not to recognize that I’ve taken daylight savings time into account for all the event times, and so during DST pushes each event forward an hour, which of course makes them wrong by one hour.

    This is because I am in blatant disregard for the standard, which requires lines about DAYLIGHT START in the beginning of the ICS file. But surely I don’t have to put lines in the beginning of the file that specify exactly when daylight savings time is. Can’t I specify somewhere that I’ve taken it all into account, and each time should just be recognized as local, and no further adjustments for daylight savings time are necessary?

    Anyway thanks for your great post (and the next one on women, which I’m about to read), and if you know of a forum where I should ask this please let me know.

    Reply  |  Quote
  24. dbemowsk UNITED STATES Mozilla Firefox Linux says:

    I realize this is an older post, but I found some other information that states the timestamps should be in the format: YYYMMDDxHHMMSSz format (x being a T, z being Z). These can be formatted by simply using:
    echo "DTSTAMP:".date('Ymd\THis\Z')."\n";
    which uses only one date() function rather than 2 along with other concatenations.
    echo "DTSTAMP:".date('Ymd').'T'.date('His')."Z\n";

    I have not fully tested this yet, but this may cause some issues with exchange servers.

    Reply  |  Quote
  25. This is very useful detail. now I am facing one issue. In the description , I have some content which need new line. I was trying \n as well tag. but both are not working. I am using PHP as a programming language.

    Any help will be appreciated.

    Reply  |  Quote
  26. Vamsi Praveen K INDIA Internet Explorer Windows says:

    Hi All, This is the best place where i found good info about iCal.

    I will be very thankful, if you can resolve my issue.

    I am creating .iCal (/.ics) file using C#. and sending the same to the attendee as a mail attachment and the mediatype of attachment as “text/calendar”. because of this the attachment is shown as direct invitation to the meeting rather as an attachment. But here i am loosing the decsription data. Everything else is good. What could be going wrong? Below is the basic iCal File.

    PRODID:-//Microsoft Corporation//Outlook 14.0 MIMEDIR//EN
    DESCRIPTION:You are scheduled to interview a candidate.\n\nCandidate: KV P\nOffice: Hyderabad\nPosition: Product Developer\nHiring Manager: \n\nYou can view the schedule and other candidate information by visiting http://iswebdeva01/Recruiting/Interview/InterviewView.aspx?InterviewID =33547\n\nIf you’re not available to interview the candidate, please contact Vamsi Praveen Karanam.
    SUMMARY:Interview – KV P

    Thanks in advance

    Reply  |  Quote
  27. dipa INDIA Mozilla Firefox Windows says:

    @ Vamsi Praveen K:
    Please skip “:” after “candiadate”, office” by “\:”

    Reply  |  Quote
  28. Jeff UNITED STATES Google Chrome Windows says:

    Great discussion here. I realize this is an older post, but hopefully someone has seen the issue I am experiencing. I am sending an ICS file as an email attachment from our java web app. Everything works fine if I send the email to an Outlook user that is not on an Exchange server. Their email has the correct “Accept/Reject” buttons and their calendar gets updated accordingly.

    If the user is on an Exchange server, however, this does not happen. They get a meeting invite, but it lists them as the organizer and they cannot change the meeting details. Then they are unable to save the meeting to their calendar.

    Has anyone else experienced this? Any ideas or suggestions are appreciated.

    Reply  |  Quote
  29. Ben UNITED STATES Internet Explorer Windows says:

    @ Jeff:

    Jeff –

    I have this exact same issue. Did you ever find a solution?


    Reply  |  Quote
  30. foxvor MEXICO Google Chrome Windows says:

    Someone asked about line breaks.

    The correct format is:
    BACKSLASH (US-ASCII decimal 92) followed by a LATIN CAPITAL LETTER N (US-ASCII decimal 78)

    as noted in this documentation:

    It also says that small “n” works, but it doesn’t (at least not with the code here presented), I tried it with capital N and it works nicely.

    Thanks by the way to the author, this was really helpful, I hope I can give back something with this reply.

    Reply  |  Quote
  31. Marc UNITED KINGDOM Mozilla Firefox Windows says:

    The script gave me inspiration for the outlook calendar I need to create for my sailing club ( Keep the good work.

    Reply  |  Quote
  32. Hamid Mozilla Firefox Mac OS says:

    Thanks to all of you.
    I’ve been searching for that for a while.

    Reply  |  Quote
  33. Mika Andrianarijaona Google Chrome Windows says:

    Thank you very much, your post was really helpful, I missed the “METHOD:REQUEST” parameter and the event wasn’t correctly displayed as an invitation in IE

    Reply  |  Quote
  34. Image 23. Fire. For many reasons this group are the ones that eat of
    out boxes and bags.

    Reply  |  Quote
  35. Kuku Google Chrome Windows says:

    Thanks for this. Here I found another example of sending meeting request to multiple participants in Outlook:…g-request-php/ and for more options VCalendar has a reference on wiki :

    Reply  |  Quote
  36. MG Mozilla Firefox Ubuntu Linux says:

    I am trying to create calender event for outlook to achieve the same I am using
    with Yii, PHPMailer and above code but, in reply in oulook inbox I am getting reply like this and it is not showing attached file as well.



    $header = ‘MIME-Version: 1.0’ . “\n”;
    $header .= ‘Content-type: text/html; charset=utf-8’ . “\n”;
    $header .= “Content-Type: text/Calendar”;
    $header .= “Content-Disposition: inline; filename=calender.ics”;

    $message = “BEGIN:VCALENDAR\n”;
    $message .= “VERSION:2.0\n”;
    $message .= “PRODID:-//Foobar Corporation//NONSGML Foobar//EN\n”;
    $message .= “METHOD:REQUEST\n”; // requied by Outlook
    $message .= “BEGIN:VEVENT\n”;
    $message .= “UID:”.date(‘Ymd’).’T’.date(‘His’).”-“.rand().”\n”; // required by Outlok
    $message .= “DTSTAMP:”.date(‘Ymd’).’T’.date(‘His’).”\n”; // required by Outlook
    $message .= “DTSTART:20080413T000000\n”;
    $message .= “SUMMARY:TEST\n”;
    $message .= “DESCRIPTION: this is just a test\n”;
    $message .= “END:VEVENT\n”;
    $message .= “END:VCALENDAR\n”;

    Please help me to resolve this issue

    Reply  |  Quote

Leave a Reply

Your email address will not be published. Required fields are marked *