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 "BEGIN:VCALENDAR\n"; 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()."-example.com\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"; echo "END:VCALENDAR\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.
Related Posts:

April 14th, 2008 at 3:43 pm (8773) [Quote]
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.
Posted usingApril 14th, 2008 at 6:51 pm (8774) [Quote]
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.
Posted usingJune 5th, 2008 at 5:32 am (9254) [Quote]
Many thanks. That is exactly what I am looking for.
Posted usingI changed this line “Foobar Corporation”. LOL~~~
July 2nd, 2008 at 4:15 pm (9518) [Quote]
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?
Posted using(and sure enough, Google indexing of a specific tech problem led me here)
August 11th, 2008 at 9:42 am (9820) [Quote]
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?
Posted usingAugust 11th, 2008 at 10:01 am (9821) [Quote]
@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!
Posted usingAugust 26th, 2008 at 9:46 am (9964) [Quote]
@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
Posted usingAugust 26th, 2008 at 10:29 am (9967) [Quote]
@Wolf
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:
August 26th, 2008 at 10:51 am (9968) [Quote]
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.
Posted usingOctober 31st, 2008 at 10:45 am (10544) [Quote]
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!!!
Posted usingOctober 31st, 2008 at 12:26 pm (10549) [Quote]
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.
Posted usingOctober 31st, 2008 at 1:00 pm (10551) [Quote]
is there a way you can push this to receiver’s calendar?
Posted usingOctober 31st, 2008 at 3:17 pm (10555) [Quote]
I think further testing on my side needs to be done!
Posted usingNovember 8th, 2008 at 10:29 am (10635) [Quote]
Hi ,
I need to send meeting requests(outlook) to different people through PHP.
Posted usingI 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?
November 10th, 2008 at 3:35 am (10642) [Quote]
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.
Posted using