Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions docs/api/calendar-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
---
sidebar_position: 6
---

# Calendar Export API

The Calendar Export API provides endpoints for exporting calendar events as iCalendar (`.ics`) files and managing calendar feed subscriptions. These endpoints are part of the v4 API and are served by the `CalendarExportController`.

## Endpoints Overview

| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| `v4/CalendarExport/ExportICalFile` | GET | Bearer token | Download a single event as `.ics` |
| `v4/CalendarExport/ExportDepartmentICalFeed` | GET | Bearer token | Download full department calendar as `.ics` |
| `v4/CalendarExport/GetCalendarSubscriptionUrl` | GET | Bearer token | Get or activate the calendar subscription URL |
| `v4/CalendarExport/RegenerateCalendarSubscriptionUrl` | POST | Bearer token | Regenerate the subscription URL (invalidates old) |
| `v4/CalendarExport/CalendarFeed/{token}` | GET | Anonymous | Fetch the iCal feed via encrypted token |

## Export Single Event

```
GET /api/v4/CalendarExport/ExportICalFile?calendarItemId={id}
Authorization: Bearer {access_token}
```

Returns a single calendar event as a `.ics` file with content type `text/calendar`.

### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `calendarItemId` | int | Yes | The ID of the calendar item to export |

### Response

- **Content-Type:** `text/calendar`
- **Content-Disposition:** attachment with `.ics` filename
- Body contains a valid iCalendar file with a single `VEVENT`

## Export Department Calendar

```
GET /api/v4/CalendarExport/ExportDepartmentICalFeed
Authorization: Bearer {access_token}
```

Returns all calendar events for the authenticated user's department as a single `.ics` file.

### Response

- **Content-Type:** `text/calendar`
- Body contains a valid iCalendar file with multiple `VEVENT` entries

## Get Calendar Subscription URL

```
GET /api/v4/CalendarExport/GetCalendarSubscriptionUrl
Authorization: Bearer {access_token}
```

Returns the user's calendar subscription URL. If the user has not yet activated calendar sync, it is activated automatically and a new subscription URL is generated.

### Response

```json
{
"Data": {
"SubscriptionUrl": "https://api.resgrid.com/api/v4/CalendarExport/CalendarFeed/{encrypted_token}",
"WebCalUrl": "webcal://api.resgrid.com/api/v4/CalendarExport/CalendarFeed/{encrypted_token}",
"IsActive": true
},
"Status": "success"
}
```

## Regenerate Calendar Subscription URL

```
POST /api/v4/CalendarExport/RegenerateCalendarSubscriptionUrl
Authorization: Bearer {access_token}
```

Generates a new sync key, invalidating all previously issued subscription URLs for this user.

### Response

```json
{
"Data": {
"SubscriptionUrl": "https://api.resgrid.com/api/v4/CalendarExport/CalendarFeed/{new_encrypted_token}",
"WebCalUrl": "webcal://api.resgrid.com/api/v4/CalendarExport/CalendarFeed/{new_encrypted_token}",
"IsActive": true
},
"Status": "success"
}
```

:::warning
Regenerating the subscription URL immediately invalidates the previous URL. Any external calendar application using the old URL will no longer receive updates.
:::

## Calendar Feed (Anonymous)

```
GET /api/v4/CalendarExport/CalendarFeed/{token}
```

This endpoint does **not** require a Bearer token. Authentication is performed via the encrypted token embedded in the URL. This is the endpoint that external calendar applications (Google Calendar, Outlook, Apple Calendar) call to fetch events.

### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `token` | string | Yes | URL-safe Base64-encoded encrypted token |

### Response

- **200 OK** — Content-Type `text/calendar` with the full department iCal feed
- **401 Unauthorized** — Token is invalid, expired, or the sync key has been regenerated

### Token Format

The encrypted token contains `{departmentId}|{userId}|{calendarSyncToken}` encrypted via the system's `IEncryptionService`. The token is made URL-safe using Base64 URL-safe encoding (`+` → `-`, `/` → `_`, trailing `=` trimmed).

On each request, the system:
1. Decodes and decrypts the token
2. Extracts the department ID, user ID, and sync GUID
3. Loads the user profile and validates that `CalendarSyncToken` matches the embedded GUID
4. If valid, generates and returns the department calendar feed

### Feature Flag

The feed endpoint respects the `CalendarConfig.ICalFeedEnabled` configuration flag. When disabled, the endpoint returns an error response.

## iCal Output Format

All iCal output conforms to **RFC 5545** and is generated using the `Ical.Net` library.

### Event Mapping

| Calendar Item Field | iCal Property | Notes |
|---------------------|---------------|-------|
| Title | `SUMMARY` | |
| Description | `DESCRIPTION` | |
| Location | `LOCATION` | |
| Start | `DTSTART` | `VALUE=DATE` format when all-day |
| End | `DTEND` | `VALUE=DATE` format when all-day; exclusive (day after last event day) |
| IsAllDay | `DTSTART`/`DTEND` format | Date-only vs. date-time |
| Reminder | `VALARM` | `TRIGGER` based on `GetMinutesForReminder()` |
| Attendees | `ATTENDEE` | Only if populated |

### All-Day and Multi-Day Events

- **All-day events** use `VALUE=DATE` format (no time component) per RFC 5545
- **Multi-day all-day events** set `DTEND` to one day after the last event day (iCal standard uses exclusive end dates for all-day events)
- **Timed events** use full date-time format with UTC offset

### Recurrences

Each materialized recurrence instance is emitted as a separate `VEVENT`. No `RRULE` properties are used because Resgrid pre-expands recurrences in the database.

### PRODID

The `PRODID` value is configurable via `CalendarConfig.ICalProductId` (default: `-//Resgrid//Calendar//EN`).

## Calendar Items API Enhancement

The existing `GetAllCalendarItems` v4 API endpoint response now includes an `IsMultiDay` boolean property on each calendar item, indicating whether the event spans multiple days (where `Start.Date != End.Date`).

### Updated Response Fields

| Field | Type | Description |
|-------|------|-------------|
| `IsMultiDay` | boolean | `true` if the event spans more than one day |
| `IsAllDay` | boolean | `true` if the event is an all-day event (existing field) |

## FullCalendar JSON Enhancement

The `GetV2CalendarEntriesForCal` endpoint (used by the web calendar view) now includes:

| Field | Type | Description |
|-------|------|-------------|
| `allDay` | boolean | `true` for all-day events; enables FullCalendar banner rendering |

For all-day events, the `end` date is set to one day after the last event day to match FullCalendar's exclusive end-date convention.
12 changes: 12 additions & 0 deletions docs/api/information.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,15 @@ Here you can find some core information, mainly how to authenticate, about the R
The Resgrid API Exposes our Swagger documentation to showcase every call and operation you can perform against the Resgrid API. You can view the documentation by navigating to the Resgrid API Swagger page.

<https://api.resgrid.com/index.html>

## Key API Areas

| Area | Description |
|------|-------------|
| **Authentication** | JWT/OpenID Connect token management |
| **Statuses** | Personnel and unit status operations |
| **Calls** | Dispatch call management |
| **Calendar** | Calendar events, types, and RSVP |
| **Calendar Export** | iCal export, department feed subscriptions, and sync token management (see [Calendar Export API](calendar-export)) |
| **Contact Verification** | Email and phone verification |
| **Workflows** | Automated workflow management |
6 changes: 5 additions & 1 deletion docs/configuration/calendar-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,16 @@ When creating or editing a calendar event, you select a type from the dropdown.

Calendar events also support:
- **Title and Description** — event details
- **Start/End Times** — with time zone support
- **All-Day Events** — span full days without requiring time selection; times auto-normalize to midnight boundaries
- **Start/End Times** — with time zone support (hidden when All Day is checked)
- **Multi-Day Events** — events spanning multiple days display as continuous banners on the calendar
- **Location** — with geocoding for map display
- **Recurrence** — none, weekly, monthly, or yearly
- **Signup Type** — for RSVP-based events
- **Entity Targeting** — target specific groups or the whole department
- **Required/Optional Attendees** — for attendance tracking
- **Reminders** — automated reminders before the event
- **iCal Export** — download individual events as `.ics` files or subscribe to the full calendar feed

## How Calendar Types Connect to Other Features

Expand All @@ -71,6 +74,7 @@ Calendar events also support:
| Reports | Events can be analyzed by type for operational reporting |
| Notifications | New calendar events trigger notifications to targeted entities |
| Permissions | Calendar event creation is controlled by the Create Calendar Entry permission |
| Calendar Sync | Exported iCal feeds include event type information |

## Common Errors and Resolutions

Expand Down
Loading