NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Appointments for Laravel

Calendar export (ICS)

Generate RFC 5545 calendar text for one appointment or a collection. The package stays HTTP-free — it returns a string and you decide how to deliver it:

use RoundlyConsulting\Appointments\Facades\Appointments;

$ics = Appointments::for($appointment)->ics();                        // one event — or $appointment->toIcs()
$feed = Appointments::ics($user->appointments()->upcoming()->get());  // one VCALENDAR, many VEVENTs

return response($feed, 200, [
    'Content-Type' => 'text/calendar; charset=utf-8',
    'Content-Disposition' => 'attachment; filename="appointments.ics"',
]);

Sample output

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Roundly Consulting//Appointments for Laravel//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:[email protected]
DTSTAMP:20260615T120000Z
DTSTART:20260701T090000Z
DTEND:20260701T100000Z
SUMMARY:Clinic visit
STATUS:TENTATIVE
DESCRIPTION:Bring your previous test results
LOCATION:Clinic A
GEO:51.5074;-0.1278
ORGANIZER;CN=Front desk:mailto:[email protected]
ATTENDEE;CN=Alex Example:mailto:[email protected]
END:VEVENT
END:VCALENDAR

What goes into each event

PropertySource
UIDThe appointment’s stored random uuid at your app.url host (…@example.com) — unique across apps and stable across a re-seeded database.
DTSTART / DTENDstarts_at / ends_at in UTC; a missing end is derived from the duration.
SUMMARYname.
DESCRIPTIONdescription, when set.
STATUSTENTATIVE for pending; CONFIRMED for confirmed and completed; CANCELLED for cancelled, declined and no-show.
LOCATIONThe location column, falling back to meta.location.
GEOlatitude;longitude, when coordinates are set.
ORGANIZERThe appointment’s primary email contact; CN is the contact’s name, else its label.
ATTENDEEOne per participant whose model has a primary email; CN is the model’s name attribute.

Participants without a primary email are left out: an ATTENDEE must be a calendar address — in practice mailto: — and calendar clients have nothing to act on without one. Add HasContacts to participant models to include them (see Guest contacts).

Escaping and folding

Text values are escaped per RFC 5545 §3.3.11: any line break — a textarea’s CRLF, a lone CR or LF — becomes one escaped \n; backslashes, semicolons and commas are backslash-escaped; other control characters except tab are dropped. CN values lose double quotes and control characters and are quoted when they contain a colon, semicolon or comma. Long lines fold at 75 octets without splitting a multi-byte UTF-8 character, and every line ends in CRLF.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.