Use calendars in your app
Store events with recurrence and time zones, import and export iCalendar, and show free/busy without showing details.
Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.
Stability The promise an interface ships with.
- Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
- Beta Usable in production; may change before it becomes stable.
- Experimental May change or go away; needs an explicit opt-in in main.yaml.
You will add calendars to your app: a team calendar with a weekly series that keeps its local time through daylight saving, a one-off edit, an iCalendar import and a free/busy answer. It takes about 20 minutes.
You need
- A MainSDK app with a
main.yamland adbsection (see Thirty-minute path). - Users that have an id. The calendar checks
accessfor each caller.
1. Declare the calendar
calendar:
store: db
max-window: 366dBuild a Calendar once from the experimental subpath, with your stores, tenant and subject:
import { Calendar } from "mainsdk/experimental/calendar";
type CalendarDeps = ConstructorParameters<typeof Calendar>[0];
declare const config: Record<string, unknown>; // the calendar section of main.yaml
declare const stores: CalendarDeps["stores"]; // the region SQL stores your db uses
declare const tenant: CalendarDeps["tenant"];
declare const currentUser: { id: string };
const calendar = new Calendar({ config, stores, tenant, subject: () => ({ id: currentUser.id }) });
await calendar.createCalendar({ id: "team", name: "Team" });2. Add a weekly series
import type { Calendar } from "mainsdk/experimental/calendar";
declare const calendar: Calendar;
await calendar.create("team", {
title: "Stand-up", start: "2026-10-26T09:00", end: "2026-10-26T09:15",
timeZone: "America/New_York", rrule: "FREQ=WEEKLY;BYDAY=MO",
});The time is stored as local time plus the zone, so 26 October is 13:00Z and 2 November is 14:00Z.
3. List a window
import type { Calendar } from "mainsdk/experimental/calendar";
declare const calendar: Calendar;
const { items } = await calendar.list({ calendars: ["team"], from: "2026-10-25", to: "2026-11-30" });Each occurrence carries UTC instants and the local time.
4. Edit one occurrence
import type { Calendar } from "mainsdk/experimental/calendar";
declare const calendar: Calendar;
declare const eventId: string;
declare const items: { occurrence: string }[];
await calendar.update(eventId, { start: "2026-11-09T11:00", end: "2026-11-09T11:15" }, {
scope: "this", occurrence: items[2].occurrence, version: 1,
});Use following to split the series and all to change every occurrence. Pass the version you read; a stale one fails with MAIN-CAL-0008.
5. Import, export and free/busy
import type { Calendar } from "mainsdk/experimental/calendar";
declare const calendar: Calendar;
declare const icsText: string;
await calendar.import("team", icsText); // events with the same UID update
const text = await calendar.export("team");
const { busy } = await calendar.freeBusy({ calendars: ["team"], from: "2026-11-02T00:00", to: "2026-11-03T00:00" });Free/busy returns merged intervals and never a title, place or attendee.
Check it worked
pnpm mainsdk checkpasses, with noMAIN-CAL-0001orMAIN-CAL-0002finding in your seed data.- The weekly series lists at 09:00 local on both sides of the November change.
- A person who may only see free/busy gets intervals with no detail.
If it fails
MAIN-CAL-0001: an unknown zone or an end before the start. Use an IANA zone such asAmerica/New_York.MAIN-CAL-0002: a recurrence part that is not supported, such asBYWEEKNO. The error names the part.MAIN-CAL-0003: the window is wider thanmax-window. Ask for a narrower one.
Prompt for your coding agent
Add calendars to my app with MainSDK's
calendarinterface. Store events in an IANA zone with weekly recurrence, let people edit one occurrence or the following ones with a version check, import and export iCalendar, and answer free/busy without titles.
Was this page helpful?