Schedules
A Schedule is a representation of a recurrence rule, compatible with the iCalendar specification, designed to be extrapolated by the data user into individual occurrences.
A PartialSchedule, by contrast, must not be extrapolated by the data user, and is for information only.
Outputting a Schedule to a feed
Schedule to a feedA Schedule must be included in the SessionSeries feed for systems that display occurrences of a sessions directly from a recurrence rule (as opposed to first materialising them into a database).
Complementary ScheduledSessions
ScheduledSessionsIt is recommended that a ScheduledSession feed be published alongside any SessionSeries feed that contains Schedules, and that this ScheduledSession feed also includes remainingAttendeeCapacity. If implementing the Open Booking API, such a complementary feed is required.
When ScheduledSessions are included in a feed or subEvent, they must not include all occurrences that are generated from the recurrence rule, and instead must only include those events that either:
Have bookings associated (i.e. where
remainingAttendeeCapacity<maximumAttendeeCapacity)Are an exception to the recurrence rule defined in the
Schedule.Have any other properties that differ from those defined in
SessionSeries.
Such events would need to have been materialised in a database within the booking system already, so under no circumstances should new occurrence records be generated for the sole purpose of outputting them to an OpenActive ScheduledSessions feed.
This constraint is necessary to prevent recurrence rules from creating a high volume of redundant data in its ScheduledSession feed. For example: if a SessionSeries has a recurrence rule for a weekly event with an endDate in 2050, only the ScheduledSessions that have been booked at least once or have been manually edited would appear in the ScheduledSession feed.
Using templates
When such ScheduledSessions are included, the Schedule must also include an idTemplate, such as the below, which matches the pattern of the @id of the ScheduledSessions (noting the startDate placeholder must use a string format of YYYY-MM-DDThh:mm:ssZ (e.g. 1997-07-16T19:20:00Z):
"idTemplate": "https://api.example.org/session-series/123/{startDate}"A urlTemplate may also be included in the Schedule using the same startDate placeholder.
Note that if a single ScheduledSession that was previously generated by a Schedule is rescheduled to a different start time, its original @id must be retained (which contains the original start time), to ensure that it still hides the same generated occurrence (see below), and to ensure that Change of Logistics Notifications are still triggered.
Also note that if a Schedule is updated, it must include exceptDates for any overlaps with any explicitly defined occurrences created from previous Schedules, as they might have been rescheduled based on a previous @id.
Processing a Schedule found in a feed
Schedule found in a feedIn order to process a Schedule together with complimentary ScheduledSessions, the data user must do the following:
Generate all occurrences from a
Schedulein the future, taking into account theexceptDateproperty.Take the
scheduledEventType, property and use it for the@typeproperty of each occurrence.Use an RRULE library to calculate the
startDateof the occurrences based on the contents of theSchedule.DTSTARTmust be determined by using thestartDate,startTime, andscheduleTimezoneof theScheduletogether, for example:{ "@type": "Schedule", "startDate": "1997-09-02", "startTime": "09:00", "endTime": "10:00", "duration": "PT1H", "scheduleTimezone": "America/New_York", "repeatFrequency": "P1D", "repeatCount": 10 }DTSTART;TZID=America/New_York:19970902T090000 RRULE:FREQ=DAILY;COUNT=10For the avoidance of doubt: the
startDateandstartTimeof theScheduleare in "local time" based on thescheduleTimezone.Use the
durationof theScheduleto calculate theendDateof each occurrence.Render the calculated
startDateandendDatefor each occurrence to UTC using a string format ofYYYY-MM-DDThh:mm:ssZ(e.g.1997-07-16T19:20:00Z) for placeholder replacement.Take the
idTemplateproperty (if provided) and substitute thestartDateplaceholder with the calculated string value ofstartDate(and do the same withendDate). Use the resulting string as the value of the@idproperty for the occurrence.Take the
urlTemplateproperty (if provided) and substitute thestartDateplaceholder with the calculated string value ofstartDate(and do the same withendDate). Use the resulting string as the value of theurlproperty for the occurrence.
To account for any changes in the
Schedulesince the last time it was updated, store the generated occurrences as follows:Upsert all generated occurrences, marking them with the
modifiedRPDE timestamp associated with theSchedule. Ensure that any explicitly defined occurrences that may have been generated from a previous run of Step 3 (below) are not overwritten by this step.Delete all generated occurrences that do not have an older modified RPDE timestamp associated with the Schedule.
Ensure the generated occurrences are hidden by any matching explicitly defined occurrences (e.g.
ScheduledSessions found in asubEventorScheduledSessionfeed), using the explicitly defined occurrence in its entirety based on its@id.
Example
Extract from SessionSeries feed:
{
"@type": "SessionSeries",
...
"eventSchedule": [
{
"@type": "Schedule",
"repeatFrequency": "P1W",
"startDate": "2018-03-01",
"endDate": "2018-03-29",
"startTime": "08:30",
"endTime": "09:30",
"byDay": [
"https://schema.org/Thursday"
],
"duration": "PT1H",
"exceptDate": [
"2018-03-15T08:30:00Z",
],
"scheduleTimezone": "Europe/London",
"scheduledEventType": "ScheduledSession",
"idTemplate": "https://api.example.org/session-series/1402CBP20150217/{startDate}",
"urlTemplate": "https://example.org/session-series/1402CBP20150217/{startDate}"
}
]
}Extract from ScheduledSession feed:
{
"state": "updated",
"kind": "ScheduledSession",
"id": "C5EE1E55-2DE6-44F7-A865-42F268A82C63",
"modified": 1521565719,
"data": {
"@context": "https://openactive.io/",
"@type": "ScheduledSession",
"@id": "https://api.example.org/session-series/1402CBP20150217/2018-03-15T10:30:00Z",
"identifier": "C5EE1E55-2DE6-44F7-A865-42F268A82C63",
"superEvent": "https://example.com/api/session-series/1402CBP20150217",
"startDate": "2018-03-15T10:30:00Z",
"endDate": "2018-03-15T11:30:00Z",
"duration": "PT1H",
"eventStatus": "https://schema.org/EventScheduled",
"maximumAttendeeCapacity": 10,
"remainingAttendeeCapacity": 10,
"url": "https://example.org/session-series/1402CBP20150217/2018-03-15T10:30:00Z"
}
},
{
"state": "updated",
"kind": "ScheduledSession",
"id": "C5EE1E55-2DE6-44F7-A865-42F268A82C64",
"modified": 1521565719,
"data": {
"@context": "https://openactive.io/",
"@type": "ScheduledSession",
"@id": "https://api.example.org/session-series/1402CBP20150217/2018-03-22T08:30:00Z",
"identifier": "C5EE1E55-2DE6-44F7-A865-42F268A82C64",
"superEvent": "https://example.com/api/session-series/1402CBP20150217",
"startDate": "2018-03-22T08:30:00Z",
"endDate": "2018-03-22T09:30:00Z",
"duration": "PT1H",
"eventStatus": "https://schema.org/EventScheduled",
"maximumAttendeeCapacity": 10,
"remainingAttendeeCapacity": 3,
"url": "https://example.org/session-series/1402CBP20150217/2018-03-22T08:30:00Z"
}
}Illustration of resulting opportunities presented to the end user:

Last updated