The event model
The one event shape every provider returns, and what each field means.
On the wire, a Google event and an Outlook event have little in common: different ids, different time formats, different names for the same status. calsdk turns both into one CalendarEvent, so the rest of your code only learns one shape. Here is one read from Outlook:
{
id: 'AAMkAGI2TG93AAA=',
calendarId: 'primary',
kind: 'single',
title: 'Design review',
description: 'Walk through the new onboarding flow.',
location: 'Room 4',
start: { dateTime: '2026-11-03T10:00:00+05:30', timeZone: 'Asia/Kolkata' },
end: { dateTime: '2026-11-03T11:00:00+05:30', timeZone: 'Asia/Kolkata' },
status: 'confirmed',
showAs: 'busy',
organizer: { email: 'ana@example.com', name: 'Ana', self: true },
attendees: [
{ email: 'raj@example.com', name: 'Raj', responseStatus: 'accepted' },
],
conference: { provider: 'teams', joinUrl: 'https://teams.microsoft.com/l/meetup-join/...' },
etag: 'W/"DwAAABYAAAA="',
htmlLink: 'https://outlook.office365.com/owa/?itemid=...',
createdAt: '2026-10-28T09:12:00Z',
updatedAt: '2026-10-30T14:03:00Z',
providerMetadata: { microsoft: { importance: 'normal', showAs: 'busy' } },
}Read the same meeting from Google and you get the same shape, with the title, times and attendees in the same places. What belongs to Google still differs: the id, the etag, the links and the metadata.
Identity
Every event carries the provider's own id and the calendarId it was read from. Pass both back to getEvent, updateEvent and deleteEvent. Ids are strings with no fixed format, and each provider shapes them its own way, so store them as they are rather than parsing them.
kind tells you what you're looking at: a single event, a recurring series, one occurrence of a series, or an exception, an occurrence someone moved or edited. Occurrences and exceptions also carry seriesId, the series they belong to, and originalStart, where the series' rule placed them. Recurring events covers how to work with them.
When
start and end are either both timed, { dateTime, timeZone }, or both all-day, { date }. An all-day event's end date is exclusive, so a one-day event on 3 November ends on the 4th. Time zones explains why timed events keep their zone.
A series keeps its rule in recurrence, as RFC 5545 lines such as ['RRULE:FREQ=WEEKLY;BYDAY=MO', 'EXDATE:20261110T100000Z'].
Status and availability
status is confirmed, tentative or cancelled. You'll rarely see cancelled: it's for events that still have their details, such as an Outlook meeting the organizer cancelled. Deleted events aren't returned at all, and sync reports them as delete changes.
showAs is busy or free, and decides whether the event blocks time in free/busy. Providers with more states, such as Outlook's out of office, keep the original in providerMetadata. visibility is default, public or private.
People
The organizer is a { email, name?, self? }, where self is true when it's the signed-in user. attendees have the same fields plus responseStatus (needsAction, accepted, declined or tentative), optional and organizer.
Some providers leave attendees out of listings because reading them costs a request per event. Fetch the event with getEvent when you need them. The provider pages say which.
Extras
conference is the video link, { provider, joinUrl, id? }, where provider is google-meet, teams, zoom or other. reminders is { useDefault, overrides? }, and each override is a { method: 'popup' | 'email', minutesBefore }. color is a hex color like '#RRGGBB', and htmlLink opens the event in the provider's own app.
etag is a version tag: pass it back as ifMatch and an update only lands if nobody changed the event since you read it. Deletes take it too, though Outlook can miss a change made just before the delete. createdAt and updatedAt are ISO 8601 timestamps. providerMetadata holds whatever only one provider has, keyed by provider id; Provider options covers it.
Every field except id, calendarId, kind, start, end, status and showAs is optional, because a provider leaves out what it doesn't have. A Cal.com booking has no color, and an ICS feed has no etag.
Writing events
You don't write a CalendarEvent back. createEvent takes an EventInput, a smaller shape with only the fields you can set: title, description, location, start, end, recurrence, showAs, visibility, attendees, createConference, reminders and color. updateEvent takes the same fields as a partial EventPatch, where null clears a field. See Types for both.