# Recurring events and RRULE reference

Source: https://developer.nylas.com/docs/v3/calendar/recurring-events/

You can use the [Nylas Events API](/docs/reference/api/events/) to schedule recurring events. This page explains how to create recurring events, what to expect when using them, and how to use `RRULE` with Nylas.

## How recurring events work

Nylas offers two ways to work with recurring events: using the [Import Events endpoint](/docs/reference/api/events/import-events/) to get all events from the specified calendar; or passing `RRULE` and `EXDATE` values in your calls to the [Events endpoints](/docs/reference/api/events/).

A series of recurring events is made up of two parts: the parent event, and the events in the series. Events in a recurrence series are scheduled to take place periodically at a set time, on specific days of the week (for example, a one-on-one meeting that takes place every Tuesday at 2:00p.m.).

```json
{
  "title": "One-on-one",
  "recurrence": [
    "RRULE:FREQ=WEEKLY; BYDAY=TU",
    "EXDATE:20211011T000000Z"
  ],
  "when": {
    "date": "2020-01-01"
  }
  ...
}
```

You might also see events that are in a recurrence series, but are scheduled for a different time or have a different title than what the parent event defines. These are considered overrides, and they represent events in a recurrence series that have been modified (for example, if your one-on-one conflicts with another meeting, so you reschedule the specific occurrence for later in the day).

Nylas formats `RRULE` and `EXDATE` values according to the [RFC 5545 specification](https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.5).

Nylas sends [event notifications](/docs/reference/notifications/#event-notifications) for primary events only, for each participant. This can still generate a _lot_ of notifications.

## Create a recurring event

When you make a [Create Event request](/docs/reference/api/events/create-event/), you can include the `recurrence` object to set a repeating schedule for the event. The `recurrence` array can include an `RRULE` string, which sets the recurrence schedule (for example, `WEEKLY`) and any `EXDATE` values, which set dates that are exceptions to the schedule.

```bash
curl --request POST \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events?calendar_id=<CALENDAR_ID>' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "title": "One-on-one",
    "busy": true,
    "participants": [{
      "name": "Leyah Miller",
      "email": "leyah@example.com"
    }],
    "description": "Weekly one-on-one.",
    "when": {
      "start_time": 1674604800,
      "end_time": 1722382420,
      "start_timezone": "America/New_York",
      "end_timezone": "America/New_York"
    },
    "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=TU"]
  }'
```

```json [group_1-Response (JSON)]
{
  "request_id": "1",
  "data": {
    "busy": true,
    "calendar_id": "<CALENDAR_ID>",
    "created_at": 1661874192,
    "description": "Weekly one-on-one.",
    "hide_participants": false,
    "grant_id": "<NYLAS_GRANT_ID>",
    "html_link": "<EVENT_URL>",
    "id": "<EVENT_ID>",
    "object": "event",
    "organizer": {
      "email": "nyla@example.com",
      "name": "Nyla"
    },
    "participants": [
      {
        "email": "leyah@example.com",
        "name": "Leyah Miller",
        "status": "maybe"
      }
    ],
    "read_only": false,
    "reminders": {
      "use_default": false,
      "overrides": [
        {
          "reminder_minutes": 10,
          "reminder_method": "email"
        }
      ]
    },
    "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=TU"],
    "status": "confirmed",
    "title": "One-on-one",
    "updated_at": 1661874192,
    "visibility": "public",
    "when": {
      "start_time": 1674604800,
      "end_time": 1722382420,
      "start_timezone": "America/New_York",
      "end_timezone": "America/New_York"
    }
  }
}
```

Recurring events in Nylas do _not_ support the `EXRULE` or `RDATE` parameters.

> **Info:** 
> **If you don't specify a time zone for a recurring event, Nylas creates the event in UTC**. Participants in other timezones receive the event invitation with the time zone listed, but participant calendars render the event in their local time zone.

When a calendar provider receives a create-event request from Nylas, it creates the events in the series at the specified intervals and times. All occurrences in the series have the same information, including the `title`, `description`, and other settings. They also share a `master_event_id` which the provider uses to identify events in the sequence.

## Edit a recurring event

Recurring events can behave differently depending on how you edit them. You can update [individual occurrences](#edit-a-single-occurrence) (similar to "Edit this event only" in a calendar UI), [part of a sequence](#edit-part-of-a-sequence) (similar to "Edit this and following events"), or the [entire sequence from start to end](#edit-a-whole-sequence) (similar to "Edit all events").

### Edit a single occurrence

Make an [Update Event request](/docs/reference/api/events/put-events-id/) with the event ID for a specific occurrence to change its date or time. Nylas updates only the information you include in your request.

```bash
curl --request PUT \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>?calendar_id=<CALENDAR_ID>' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "title": "Updated: One-on-one",
    "when": {
      "start_time": 1674820800,
      "end_time": 1674822600,
      "start_timezone": "America/New_York",
      "end_timezone": "America/New_York"
    }
  }'
```

```json [group_2-Response (JSON)]
{
  "request_id": "1",
  "data": {
    "busy": true,
    "calendar_id": "<CALENDAR_ID>",
    "created_at": 1661874192,
    "description": "Weekly one-on-one.",
    "hide_participants": false,
    "grant_id": "<NYLAS_GRANT_ID>",
    "html_link": "<EVENT_URL>",
    "id": "<EVENT_ID>",
    "master_event_id": "<EVENT_ID>",
    "object": "event",
    "organizer": {
      "email": "nyla@example.com",
      "name": "Nyla"
    },
    "participants": [
      {
        "email": "leyah@example.com",
        "name": "Leyah Miller",
        "status": "maybe"
      }
    ],
    "read_only": false,
    "reminders": {
      "use_default": false,
      "overrides": [
        {
          "reminder_minutes": 10,
          "reminder_method": "email"
        }
      ]
    },
    "status": "confirmed",
    "title": "Updated: One-on-one",
    "updated_at": 1674216000,
    "visibility": "public",
    "when": {
      "start_time": 1674820800,
      "end_time": 1674822600,
      "start_timezone": "America/New_York",
      "end_timezone": "America/New_York"
    },
    "original_start_time": 1674604800
  }
}
```

When you update a single occurrence of a recurrence series, Nylas sends only one [`event.updated` notification](/docs/reference/notifications/events/event-updated/) for the parent event. For convenience, Nylas includes the following arrays in the notification:

- `occurrences`: A list of event IDs for the affected occurrences.
- `cancelled_occurrences`: A list of event IDs for cancelled occurrences.

The `occurrences` and `cancelled_occurrences` fields are supported by Google and Microsoft Graph accounts _only_.

If you make an [Import Events request](/docs/reference/api/events/import-events/) after you update a single event in a recurrence series, Nylas returns the parent event and any overrides.

```bash
curl --compressed --request GET \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events/import?calendar_id=<CALENDAR_ID>&start=<TIMESTAMP>&end=<TIMESTAMP>' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json'

```

```json [group_3-Response (JSON)]
{
  "request_id": "1",
  "data": [
    {
      "busy": true,
      "calendar_id": "<CALENDAR_ID>",
      "created_at": 1661874192,
      "description": "Weekly one-on-one.",
      "hide_participants": false,
      "grant_id": "<NYLAS_GRANT_ID>",
      "html_link": "<EVENT_URL>",
      "id": "<EVENT_ID>",
      "object": "event",
      "organizer": {
        "email": "nyla@example.com",
        "name": "Nyla"
      },
      "participants": [
        {
          "email": "leyah@example.com",
          "name": "Leyah Miller",
          "status": "maybe"
        }
      ],
      "read_only": false,
      "reminders": {
        "use_default": false,
        "overrides": [
          {
            "reminder_minutes": 10,
            "reminder_method": "email"
          }
        ]
      },
      "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=TU"],
      "status": "confirmed",
      "title": "One-on-one",
      "updated_at": 1661874192,
      "visibility": "public",
      "when": {
        "start_time": 1674604800,
        "end_time": 1722382420,
        "start_timezone": "America/New_York",
        "end_timezone": "America/New_York"
      }
    },
    {
      "busy": true,
      "calendar_id": "<CALENDAR_ID>",
      "created_at": 1661874192,
      "description": "Weekly one-on-one.",
      "hide_participants": false,
      "grant_id": "<NYLAS_GRANT_ID>",
      "html_link": "<EVENT_URL>",
      "id": "<EVENT_ID>",
      "master_event_id": "<EVENT_ID>",
      "object": "event",
      "organizer": {
        "email": "nyla@example.com",
        "name": "Nyla"
      },
      "participants": [
        {
          "email": "leyah@example.com",
          "name": "Leyah Miller",
          "status": "maybe"
        }
      ],
      "read_only": false,
      "reminders": {
        "use_default": false,
        "overrides": [
          {
            "reminder_minutes": 10,
            "reminder_method": "email"
          }
        ]
      },
      "status": "confirmed",
      "title": "Updated: One-on-one",
      "updated_at": 1674216000,
      "visibility": "public",
      "when": {
        "start_time": 1674820800,
        "end_time": 1674822600,
        "start_timezone": "America/New_York",
        "end_timezone": "America/New_York"
      },
      "original_start_time": 1674604800
    }
  ]
}
```

### Edit part of a sequence

Often, you'll want to edit a sequence of events after a few occurrences have passed, when you find that the time, date, or frequency of the event doesn't work well for its intended purpose. This is the equivalent to editing an event in the provider UI and selecting "Edit this event and all following".

To edit part of a sequence, edit the original parent event to end it before your chosen occurrence. Then, delete the edited event occurrence and all following events in the sequence, and create an event sequence with the new date and time, and a new `master_event_id`.

To edit a recurring event starting from the _first_ event in the sequence, delete the whole original sequence and re-create it as a new sequence with a new `master_event_id`.

For each participant, you receive an [`event.updated` notification](/docs/reference/notifications/events/event-updated/) for the original primary event and an [`event.created` notification](/docs/reference/notifications/events/event-created/) for the new parent event.

### Edit a whole sequence

To change an entire sequence of events, including occurrences that have already passed, edit the parent event only. You should receive an [`event.updated` notification](/docs/reference/notifications/events/event-updated/) for each participant.

## Delete a recurring event

Similar to editing recurring events, you can delete [individual occurrences](#delete-a-single-occurrence), [part of a sequence](#delete-part-of-a-sequence), or the [entire sequence from start to end](#edit-a-whole-sequence).

### Delete a single occurrence

Make an [Delete Event request](/docs/reference/api/events/delete-events-id/) with the event ID for a specific occurrence to delete it from your calendar.

```bash
curl --compressed --request DELETE \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events/<EVENT_ID>?calendar_id=<CALENDAR_ID>' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json'

```

```json [group_4-Response (JSON)]

{
  "request_id": "1"
}


```

If you make an [Import Events request](/docs/reference/api/events/import-events/) after you delete a single event in a recurrence series, Nylas returns the parent event and includes an `EXDATE` representing the deleted event.

> **Warn:** 
> **Because of provider limitations, Google handles deleted events in a recurrence series differently**. Instead of returning only the parent event with an updated `EXDATE`, Nylas returns the parent event and the deleted event. The deleted event specifies its `status` is `cancelled`.

```bash
curl --compressed --request GET \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/events/import?calendar_id=<CALENDAR_ID>&start=<TIMESTAMP>&end=<TIMESTAMP>' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json'

```

```json [group_5-Response (JSON)]
{
  "request_id": "1",
  "data": [
    {
      "busy": true,
      "calendar_id": "<CALENDAR_ID>",
      "created_at": 1661874192,
      "description": "Weekly one-on-one.",
      "hide_participants": false,
      "grant_id": "<NYLAS_GRANT_ID>",
      "html_link": "<EVENT_URL>",
      "id": "<EVENT_ID>",
      "object": "event",
      "organizer": {
        "email": "nyla@example.com",
        "name": "Nyla"
      },
      "participants": [
        {
          "email": "leyah@example.com",
          "name": "Leyah Miller",
          "status": "maybe"
        }
      ],
      "read_only": false,
      "reminders": {
        "use_default": false,
        "overrides": [
          {
            "reminder_minutes": 10,
            "reminder_method": "email"
          }
        ]
      },
      "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=TU", "EXDATE:20211011T000000Z"],
      "status": "confirmed",
      "title": "One-on-one",
      "updated_at": 1661874192,
      "visibility": "public",
      "when": {
        "start_time": 1674604800,
        "end_time": 1722382420,
        "start_timezone": "America/New_York",
        "end_timezone": "America/New_York"
      }
    }
  ]
}
```

### Delete part of a sequence

Sometimes, you might want to end a recurring event after a few occurrences have passed. To do this, make an [Update Event request](/docs/reference/api/events/put-events-id/) for the parent event, and update the `RRULE` with a `COUNT` value that reflects the total number of occurrences in the sequence (for example, `RRULE:FREQ=DAILY;COUNT=10`.)

To delete a recurring event starting from the _first_ event in the sequence, delete the whole original sequence. You will receive an [`event.deleted` notification](/docs/reference/notifications/events/event-deleted/) for each participant.

### Delete a whole sequence

To delete an entire sequence of events, including occurrences that have already passed, make a [Delete Event request](/docs/reference/api/events/delete-events-id/) with the ID of the parent event. You should receive an [`event.deleted` notification](/docs/reference/notifications/events/event-deleted/) for each participant.

## `RRULE` syntax reference

`RRULE` uses a specific syntax for recurring events in calendars. The formatting described below is for the request body of an Events API request, or for the `recurrence` object in the Nylas SDKs.

| Parameter  | Type   | Description                                                                                                                                    |
| ---------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `rrule`    | array  | An array of `RRULE` and `EXDATE` strings. See [RFC-5545](https://tools.ietf.org/html/rfc5545#section-3.8.5) for more information.              |
| `timezone` | string | An [IANA time zone database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)-formatted string (for example, `America/New_York`). |

The `rrule` parameter is an array of two strings. The first value in the array represents the recurring event patterns. It begins with `RRULE` and separates option-value pairs with semicolons (`;`). The second string in the array is the optional `EXDATE` value, which excludes dates from the pattern. For example, the following array represents a sample recurring event.

```json
rrule: ["RRULE: OPTIONS=VALUE;OPTIONS=VALUE", "EXDATE: 2023-01-31"], timezone: "America/New_York"
```

### Exclude dates from a recurrence schedule

You can omit specific dates from a recurring event by using the optional `EXDATE` property in an event's `rrule` array. Only [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)-formatted dates are valid.

## `RRULE` configuration options

The following table shows common options for `RRULE` configurations.

| Option     | Values                                           | Description                                                                                                                                  |
| ---------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `FREQ`     | `YEARLY`, `MONTHLY`, `WEEKLY`, `DAILY`, `HOURLY` | The event's recurrence frequency.                                                                                                            |
| `BYDAY`    | `MO`, `TU`, `WE`, `TH`, `FR`, `SA`, `SU`         | The day of the week on which the recurring event occurs. If not specified, Nylas uses the date and time values from the original event.      |
| `BYMONTH`  | `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]`        | The month, specified by its corresponding number (for example, October is `10`).                                                             |
| `COUNT`    | integer                                          | The number of times the event recurs.                                                                                                        |
| `INTERVAL` | integer                                          | The interval between each recurrence (for example, a `FREQ` of `MONTHLY` with an `INTERVAL` of `2` means the event recurs every two months). |

Events inherit their time zone from the [`when` object](/docs/reference/api/events/create-event/). Nylas recommends that you use the `when` object to specify the start time and the end time.

For more information about `RRULE` standards, see the following documentation:

- [iCalendar.org Recurrence Rule](https://icalendar.org/iCalendar-RFC-5545/3-3-10-recurrence-rule.html)
- [Internet Engineering Task Force (IETF) Recurrence Rule](https://datatracker.ietf.org/doc/html/rfc2445#section-4.8.5.4)

Each option in the `rrule` array corresponds to a pattern. These examples show the relationship between patterns and `RRULE` formatting:

- **Occurs every two years**: `FREQ=YEARLY`, `INTERVAL=2`
- **Occurs on Monday, Wednesday, and Friday**: `BYDAY=MO,WE,FR`
- **Occurs in February, April, June, and September**: `BYMONTH=2,4,6,9`
- **Occurs 75 times total**: `COUNT=75`
- **Does not occur on November 20, 2021 at 5:20:47 GMT**: `"EXDATE:20211120T0420470000"`

## Best practices

The following sections cover various `RRULE` behaviors that you might experience with Nylas and other providers. This information is helpful to keep in mind when creating and changing recurring events.

### Default settings for recurring events

Because `RRULE` formatting doesn't allow for changes to the event time, each instance of a recurring event occurs at the same time of day as the original. If you want to change the time that an individual event occurs, you can [update the occurrence](#edit-a-single-occurrence).

If you don't include `BYDAY` in the `RRULE` string for weekly events (those with `FREQ=WEEKLY` set), Nylas defaults to information from the parent event's date and time values to set the recurrence schedule.

## Google vs Microsoft recurring event behavior

The table below compares how various providers support recurring events.

| Description                                                                           | Google behavior                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Microsoft behavior                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Modifying the recurrence of a series, such as changing an event from weekly to daily. | Overrides remaining unchanged if they are still part of the recurrence.                                                                                                                                                                                                                                                                                                                                                                                                                          | Overrides are removed.                                                                                                                                                                                                                                                        |
| Changing the busy status on the main event for a series.                              | Busy status updates for all events in the series, including overrides.                                                                                                                                                                                                                                                                                                                                                                                                                           | Busy status doesn't update for overrides that were created through Nylas. It does update for overrides created through Microsoft's native calendar interface.                                                                                                                 |
| Deleting a single occurrence from a series within the provider's native calendar UI.  | Google marks deleted occurrences as cancelled events and creates a hidden master event to track them. This primary event has a different ID from the individual occurrences. The deleted occurrences aren't visible in the UI, but you can retrieve them by making a [Get all Events](/docs/reference/api/events/get-all-events/) request with the `show_cancelled=true` query parameter. Google doesn't reflect cancelled events in the `EXDATE` of the primary event for the recurring series. | Microsoft entirely removes the deleted occurrences from the system instead of marking them as cancelled. As a result, you can't retrieve them using the Nylas API. Microsoft doesn't reflect deleted occurrences in the EXDATE of the primary event for the recurring series. |
| Creating a recurring event in the provider's UI with an end date for the recurrence.  | The end date shows up as `23:59:59` after the last occurrence in the account's local timezone using the Nylas API.                                                                                                                                                                                                                                                                                                                                                                               | The end date shows up as the start time of the last occurrence in the series using the Nylas API.                                                                                                                                                                             |

### Microsoft provider limitations

Microsoft accounts have a few additional limitations for recurring events.

If a recurring event has an associated `EXDATE`, you cannot recover or undo the removed occurrence. You must either create a separate event to represent a recovered `EXDATE`, or use the other event properties like `status` to recover the occurrence. These options allow you to change the event information at another time.

Currently, Microsoft accounts don't support creating an event that occurs monthly on multiple days (for example, `RRULE:FREQ=MONTHLY;BYDAY=1TH,3TH`). The index property is on a monthly recurrence object, not the day-of-the-week object. Microsoft accounts don't support different indices.

Microsoft Exchange also doesn't allow you to reschedule an instance of a recurring event that is going to fall on the day of or the day before the previous instance. You cannot overlap recurring events. For more information, see [Microsoft's official documentation](https://docs.microsoft.com/en-us/graph/api/event-update?view=graph-rest-1.0&tabs=http#response).

## Unsupported properties

If you edit the `EXDATE` value after creating a recurring event, Nylas returns a [`200` response](/docs/api/errors/200-response/), but doesn't remove or delete events. You need to make a [Delete Event request](/docs/reference/api/events/delete-events-id/) to remove an individual event from a recurrence schedule.

Virtual calendars don't support `DTSTART` and `TZID`.