Skip to content
Shiny.NET

Calendar

NuGet package Shiny.AppDeviceBridge.Calendar

The calendar bridge lists the device’s calendars and their events in a date range, and creates, updates and deletes events.

Terminal window
dotnet add package Shiny.AppDeviceBridge.Calendar
builder.UseAppDeviceBridge(
bridge => bridge.AddCalendarBridge(),
webApp => { /* … */ }
);
Client C# TypeScript
ICalendarBridge AddCalendarBridgeClient() CalendarBridge
Topic Details
Platforms Android, iOS, Mac Catalyst, macOS and Windows.
Access POST calendar/access takes ReadWrite (the default), ReadOnly or WriteOnly. Write-only access (iOS 17+) is reported as Restricted.
Listing GET calendar/events requires start and end, at most 366 days apart. Paging works like contacts.
Reminders reminderMinutes counts minutes before the start.
Read-only fields attendees, the organizer and recurrence can be read but not written.
Read-only calendars writing to one returns 403 read_only, and so does writing to a system calendar on Windows, which only allows writes to app-owned calendars.
Deleting DELETE calendar/events/{id}?series=true removes the rest of a recurring series rather than one occurrence.
Mac Catalyst, sandboxed macOS also need the com.apple.security.personal-information.calendars entitlement. Without it, access is denied without a prompt.

Every route answers 403 access_denied until access has been granted.

import { CalendarBridge } from "@shinyorg/appdevicebridge";
const calendar = new CalendarBridge();
await calendar.requestAccess({ access: "ReadWrite" });
const week = await calendar.getEvents(new Date(), new Date(Date.now() + 7 * 864e5), { limit: 50 });
for (const e of week.items) console.log(e.start, e.title);
const created = await calendar.createEvent({
title: "Dentist",
start: "2026-10-12T15:00:00-04:00",
end: "2026-10-12T16:00:00-04:00",
reminderMinutes: [30]
});
await calendar.updateEvent(created.id!, { location: "123 Main St" });

Event lists page with offset and limit; hasMore says whether to ask again.

GET/_bridge/calendar#

Where calendar access stands, without asking.

C#ICalendarBridge.GetAccessAsyncTSCalendarBridge.getAccess
Response
200 CalendarAccessResult
CalendarAccessResult · 1 field
accessAccessState
GET/_bridge/calendar/calendars#

C#ICalendarBridge.GetCalendarsAsyncTSCalendarBridge.getCalendars
Response
200 CalendarList
CalendarList · 1 field
calendarsCalendarInfo[]
GET/_bridge/calendar/events#

Events overlapping a range of at most 366 days, a page at a time, in start order.

C#ICalendarBridge.GetEventsAsyncTSCalendarBridge.getEvents
ParameterInType
startqueryDate | stringrequired
endqueryDate | stringrequired
calendarIdquerystring | nulloptional
searchquerystring | nulloptional
offsetquerynumberoptional
limitquerynumberoptional
Response
200 CalendarEventPage
CalendarEventPage · 4 fields
itemsCalendarEvent[]
offsetnumber
limitnumber
hasMoreboolean
POST/_bridge/calendar/events#

Creates an event. A read-only calendar fails with read_only.

C#ICalendarBridge.CreateEventAsyncTSCalendarBridge.createEvent
Body
application/json · NewCalendarEvent
NewCalendarEvent · 10 fields
titlestringAt most 1,000 characters.
startDate | string
endDate | stringNot before start.
calendarIdstring | nullThe calendar to create it in; the device's default calendar when null.
descriptionstring | nullAt most 10,000 characters.
locationstring | null
isAllDayboolean
availabilityEventAvailability
urlstring | nullAn absolute http or https URL.
reminderMinutesnumber[] | nullWhole minutes before the start; at most 10, each up to four weeks.
Response
200 CalendarEvent
CalendarEvent · 15 fields
idstring | null
calendarIdstring | null
titlestring
descriptionstring | null
locationstring | null
startstring
endstring
isAllDayboolean
availabilityEventAvailability
urlstring | null
isRecurringboolean
recurrenceRulestring | null
reminderMinutesnumber[]
attendeesCalendarAttendee[]
organizerCalendarAttendee | null
GET/_bridge/calendar/events/{id}#

C#ICalendarBridge.GetEventAsyncTSCalendarBridge.getEvent
ParameterInType
idpathstringrequired
Response
200 CalendarEvent
CalendarEvent · 15 fields
idstring | null
calendarIdstring | null
titlestring
descriptionstring | null
locationstring | null
startstring
endstring
isAllDayboolean
availabilityEventAvailability
urlstring | null
isRecurringboolean
recurrenceRulestring | null
reminderMinutesnumber[]
attendeesCalendarAttendee[]
organizerCalendarAttendee | null
PUT/_bridge/calendar/events/{id}#

Changes only what changes sets. An event cannot move between calendars.

C#ICalendarBridge.UpdateEventAsyncTSCalendarBridge.updateEvent
ParameterInType
idpathstringrequired
Body
application/json · CalendarEventChanges
CalendarEventChanges · 9 fields
titlestring | null
startDate | string | null
endDate | string | null
descriptionstring | null
locationstring | null
isAllDayboolean | null
availabilityEventAvailability | null
urlstring | null
reminderMinutesnumber[] | null
Response
200 CalendarEvent
CalendarEvent · 15 fields
idstring | null
calendarIdstring | null
titlestring
descriptionstring | null
locationstring | null
startstring
endstring
isAllDayboolean
availabilityEventAvailability
urlstring | null
isRecurringboolean
recurrenceRulestring | null
reminderMinutesnumber[]
attendeesCalendarAttendee[]
organizerCalendarAttendee | null
DELETE/_bridge/calendar/events/{id}#

Deletes an event — or, with series, the rest of the recurring series it belongs to.

C#ICalendarBridge.DeleteEventAsyncTSCalendarBridge.deleteEvent
ParameterInType
idpathstringrequired
seriesquerybooleanoptional
Response
204 No content

This bridge raises no events — everything it does is a request and its response.

JSON property names are camelCase and enums travel as their names. A ? marks a property the payload may omit;| null one that may be null.

AccessStateenum#

Where a permission stands. Reported by every bridge that needs one; the names match Shiny's own.

"Unknown""NotSupported""NotSetup""Disabled""Restricted""Denied""Available"
CalendarAccessTypeenum#

The access to ask for. Write-only is iOS 17 and later; elsewhere it is treated as read-write.

"ReadOnly""WriteOnly""ReadWrite"
CalendarInfoobject#
idstring | null
namestring
colorstring | null
isReadOnlyboolean
accountstring | null
CalendarEventPageobject#
itemsCalendarEvent[]
offsetnumber
limitnumber
hasMoreboolean
CalendarEventobject#

An event as the device holds it. Attendees, the organizer and recurrence can be read but not written.

idstring | null
calendarIdstring | null
titlestring
descriptionstring | null
locationstring | null
startstring
endstring
isAllDayboolean
availabilityEventAvailability
urlstring | null
isRecurringboolean
recurrenceRulestring | null
reminderMinutesnumber[]
attendeesCalendarAttendee[]
organizerCalendarAttendee | null
EventAvailabilityenum#
"Busy""Free""Tentative""Unavailable"
AttendeeRoleenum#
"Required""Optional""Resource""Unknown"
AttendeeStatusenum#
"Unknown""Pending""Accepted""Declined""Tentative"
NewCalendarEventobject#

An event to create. title, start and end are required.

titlestringAt most 1,000 characters.
startDate | string
endDate | stringNot before start.
calendarId?string | nullThe calendar to create it in; the device's default calendar when null.
description?string | nullAt most 10,000 characters.
location?string | null
isAllDay?boolean
availability?EventAvailability
url?string | nullAn absolute http or https URL.
reminderMinutes?number[] | nullWhole minutes before the start; at most 10, each up to four weeks.
CalendarEventChangesobject#

Changes to an event. Anything left null stays as it is; an empty reminderMinutes clears them, and an empty url removes it.

title?string | null
start?Date | string | null
end?Date | string | null
description?string | null
location?string | null
isAllDay?boolean | null
availability?EventAvailability | null
url?string | null
reminderMinutes?number[] | null

These routes sit under AppDeviceBridgePolicies.Bridges like every other bridge: callers on this device by default. See Security.