Skip to content
C MakeCalendarLink

Creating an .ics calendar file

The .ics file is the universal fallback. Every calendar application on every platform reads it, which makes it the one format worth getting exactly right. RFC 5545 is 168 pages long; here is the part you actually need.

Rather not build the URL by hand? The generator does all of this, including the encoding.

Generate a link

The minimum viable file

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Your product//EN
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VEVENT
UID:unique-per-event@yourdomain.com
DTSTAMP:20260601T090000Z
SUMMARY:Q4 product webinar
DTSTART;TZID=Europe/Brussels:20260910T140000
DTEND;TZID=Europe/Brussels:20260910T150000
END:VEVENT
END:VCALENDAR

UID and DTSTAMP are required by the spec and skipped by most hand-rolled generators. Leaving them out mostly works, until a client decides to be strict about it.

The three rules that break files

  1. CRLF line endings. Every line ends with \r\n. A file with plain \n is technically invalid, and some Outlook versions reject it outright.
  2. Fold at 75 octets. Longer lines continue on the next line, which must start with a single space. Note octets, not characters: an emoji costs four.
  3. Escape text values. Backslash, semicolon and comma each get a leading backslash, and a newline becomes the two characters \n. Miss this and a description containing a comma silently truncates the rest of the field.

A stable UID matters

The UID identifies the event. If you generate a fresh random one every time the file is downloaded, someone who clicks twice gets two events. Derive it from the event details instead, so the same event always yields the same UID and a second download updates the existing entry rather than duplicating it.

Serving the file

Content-Type: text/calendar; charset=utf-8
Content-Disposition: attachment; filename="webinar.ics"
Cache-Control: public, max-age=31536000, immutable

The long cache is safe when the URL fully determines the contents, for example when the event is encoded in the query string. If your URL is a short ID pointing at a mutable record, do not cache it that hard.

Validating

Before you ship, open the file in Apple Calendar, Outlook desktop and Google Calendar's import. Those three disagree about enough edge cases that passing all of them means you are fine. Watch specifically for the event landing an hour off, which almost always means a TZID the client does not recognise.

Creating an .ics calendar file: questions

Do I need a VTIMEZONE block? +
Strictly, a TZID should be accompanied by a VTIMEZONE definition. In practice every modern client resolves IANA zone names on its own. Adding an X-WR-TIMEZONE line covers the few that do not, without carrying a full VTIMEZONE block per event.
Can one file hold several events? +
Yes. Repeat the VEVENT block inside the same VCALENDAR. Handy for a series of sessions people add in one go.
How do I cancel an event I already sent out? +
Send a second file with the same UID, METHOD:CANCEL, STATUS:CANCELLED and a higher SEQUENCE number.

Other calendars