For the complete documentation index, see llms.txt. This page is also available as Markdown.

Managing schedules

Create, update, query, and delete JSCalendar-aligned schedules compatible with RFC 5545 (iCalendar).

Schedules in Business Data Repository (BDR) define time-based rules for your fleet operations, maintenance windows, work hours, restrictions, and more.

The schedule data structure follows JSCalendar (RFC 8984), a JSON format for calendar data, and converts to RFC 5545 (iCalendar) and back without losing information. If you've worked with iCalendar concepts like RRULE, EXDATE, and VTIMEZONE, the model will be familiar: the field names and JSON structure come from JSCalendar, while the repeat rules behave as in iCalendar. If you haven't, that's fine. This guide explains everything you need.

This guide walks you through creating, configuring, updating, and deleting schedules.

Prerequisites

To work with a schedule, you need your workspace's ID. It comes with your access credentials and is carried in your access token. See Authentication for how tokens work and where the workspace ID comes from.

How schedule data works

A schedule consists of metadata (title and workspace) and calendar data stored in the scheduleData field, which accepts a value of ScheduleData, a structured JSON object validated on every write.

Top-level fields

The JSON structure follows the JSCalendar conventions:

Field
Required
Description

timeZone

IANA timezone identifier (e.g., Europe/Berlin, America/New_York, UTC). Defines how the API interprets all date-time values in timed events.

events

Non-empty array of time slots, each with a start time, end time or duration, and an optional recurrence rule

active

Boolean. When false, the schedule is disabled without deleting it. Defaults to true.

description

Free-text description. The schedule's display name is the entity title, not this field.

How timezone works

All date-time values in events (start, end, excludedDates, additionalDates, and recurrenceRule.until) are local time with no UTC offset or Z suffix. The timeZone field tells the API how to interpret them.

For example, for "timeZone": "Europe/Berlin":

  • "start": "2025-01-06T06:00:00" means 6:00 AM Berlin time.

  • The API handles daylight saving time (DST) changes automatically, so a recurring event at 06:00:00 stays at 6 AM local time year-round.

All-day events (where showWithoutTime: true) use date strings instead of date-times (2025-06-10, not 2025-06-10T00:00:00) and are timezone-independent.

Event fields

Each event in the events array can include:

Field
Required
Description

start

Yes

Start date-time for timed events (2025-01-06T06:00:00) or date for all-day events (2025-01-06). Seconds required. No fractional seconds. No offset or Z.

end

Conditional

End date-time (or date for all-day). Can't be combined with duration. A timed event needs exactly one of end or duration. An all-day event may omit both.

duration

Conditional

Duration in ISO 8601 format (e.g., PT9H, PT30M, P1D). Can't be combined with end. Must be positive. For all-day events, must be whole days (e.g., P1D, P2W) with no time component. Preferred over end for recurring events because it stays stable across DST changes.

showWithoutTime

No

When true, the event is all-day: start and end are dates, and the event ignores timeZone. All-day recurrence rules must not use byHour, byMinute, or bySecond.

recurrenceRule

No

Recurrence rule defining the repeat pattern. Absent means a single occurrence at start.

excludedDates

No

Array of local date-times to exclude from recurrence (iCalendar EXDATE). Values must exactly match generated occurrence date-times, including the time component.

additionalDates

No

Array of local date-times to add as one-off occurrences (iCalendar RDATE).

uid

No

Stable string identifier for the slot. Useful when tracking individual events across updates.

title

No

Display label for this event slot (maps to VEVENT SUMMARY).

Recurrence rule fields

The recurrenceRule property supports these fields:

Field
Type
Description

frequency

String

Required. One of: secondly, minutely, hourly, daily, weekly, monthly, yearly. Lowercase.

interval

Integer

Repeat every N periods. Must be ≥ 1. Defaults to 1.

count

Integer

Stop after N occurrences. Can't be combined with until.

until

String

Stop after this local date-time. Must be ≥ start. Can't be combined with count.

byDay

Object[]

Days of the week. Each entry is { "day": "<mo|tu|we|th|fr|sa|su>", "nthOfPeriod": <integer> }. The optional nthOfPeriod sets an ordinal (e.g., { "day": "mo", "nthOfPeriod": 1 } = first Monday of the period) and is only valid for monthly or yearly frequency.

byMonth

Integer[]

Months: 1–12.

byMonthDay

Integer[]

Days of the month: 1–31, or -31 to -1 from the end (-1 = last day).

byYearDay

Integer[]

Days of the year: 1–366, or -366 to -1 from the end.

byWeekNo

Integer[]

Weeks of the year: 1–53, or -53 to -1. Valid only with yearly frequency.

byHour

Integer[]

Hours: 0–23. Must not be used on all-day events.

byMinute

Integer[]

Minutes: 0–59. Must not be used on all-day events.

bySecond

Integer[]

Seconds: 0–60 (60 is valid for leap seconds). Must not be used on all-day events.

firstDayOfWeek

String

First day of the week for week calculations. One of mosu. Defaults to mo. (iCalendar WKST.)

Validation

scheduleData is validated on every write against the following rules:

  • required fields

  • pairs that can't be combined (end/duration, count/until)

  • date-time format (local time only, no offset or Z)

  • recurrence constraints (byWeekNo only with yearly, ordinal byDay only with monthly/yearly, no byHour/byMinute/bySecond on all-day events)

  • field types

If the input breaks any of these rules, the API rejects the whole write with a validation error that names the problem field. The rules apply only to writes. Reading is never validated, so schedules saved before a rule existed can still be read.

Example scenario: Fleet maintenance schedule

TransLog GmbH needs to schedule weekly maintenance for their vehicle fleet. The maintenance provider works every Monday from 6:00 to 10:00 (Europe/Berlin timezone). Over time, requirements will change: holidays need to be excluded, the contract has an end date, and the maintenance window gets split to accommodate a break.

1

Create the schedule

Start with a weekly recurring event. The scheduleData field requires a timeZone and at least one event with a start and either end or duration.

version is optional in all mutations. If you leave it out, an update always applies, even when someone else changed the record after you last read it. Include it to catch such conflicts, as the examples in this scenario do. See Optimistic locking for details.

Run this mutation:

Note the following:

  • timeZone is Europe/Berlin. All date-times in this schedule are Berlin local time: no Z, no offset.

  • start: "2025-01-06T06:00:00" is a Monday, which matches the byDay: [{ day: "mo" }] rule. Always make start match your repeat rule, otherwise the first occurrence may not land where you expect.

  • duration: "PT4H" defines a 4-hour window (06:00–10:00). Using duration instead of end keeps the window stable across DST changes.

  • frequency: "weekly" with byDay: [{ day: "mo" }] means the event repeats every Monday.

The response confirms creation:

Save the id and version. You'll need them for updating the schedule.

2

Verify the schedule

Query the schedule to confirm it was created correctly:

The scheduleData field returns the full JSON structure you provided. Use it to verify the configuration before making further changes.

3

Exclude holidays

The maintenance provider doesn't work on public holidays. Several holidays in the year fall on Mondays. Add these as exception dates using excludedDates. This requires updating the schedule with scheduleUpdate.

An entry in excludedDates must exactly match the date and time of the occurrence it removes, not just the date. This event's occurrences start at 06:00:00, so every excluded date ends with the same T06:00:00.

Run this mutation:

For all-day events (showWithoutTime: true), use date-only values in excludedDates, for example, "2025-04-21" instead of "2025-04-21T06:00:00".

The response shows the incremented version:

4

Set an end date

The maintenance contract runs through December 31, 2025. Add an until date to the recurrence rule so the schedule stops repeating after that date.

until is inclusive: the last occurrence can fall on this date. It must be ≥ start and can't be combined with count. The schedule's version is now 3.

5

Split the schedule into two windows

The maintenance team requests a break from 8:00 to 8:30. Replace the single 4-hour event with two events: 6:00–8:00 and 8:30–10:00.

Each event needs its own excludedDates array with times matching that event's start. Run this mutation:

The schedule's version is now 4.

6

Delete the schedule

When the contract ends and you no longer need the schedule, run this mutation to delete it:

Response:

Including version ensures you don't accidentally delete a schedule that someone else has modified. If the version doesn't match, you'll receive a conflict error. See Optimistic locking for details on when to omit it.

Listing schedules

To retrieve all schedules for a workspace:

For details on pagination, see Pagination.

Handling version conflicts

If you include version in your mutation and the entity has been modified since you last fetched it, the API returns a conflict error:

To resolve this:

  1. Query the schedule to get the current version and data

  2. Merge your changes with the current state

  3. Retry the mutation with the correct version

For more details on version conflicts, see Optimistic locking.

Common patterns

The snippets below show the recurrenceRule object in isolation. In practice, each belongs inside an event in the events array alongside start and end or duration.

Single-parameter patterns

Every weekday (standard work hours):

Every other week on Monday (bi-weekly team meetings):

First and fifteenth of each month (payroll processing):

Last day of each month (monthly reports deadline):

First Monday of each month (monthly fleet review):

Multi-parameter patterns

Every hour during weekday business hours (hourly check-ins):

byHour, byMinute, and bySecond work with any frequency. Their only restriction is that an all-day event (showWithoutTime: true) must not use them.

Every Monday in January, April, July, and October (quarterly inspections):

Complete examples

Warehouse work hours

Standard weekday schedule with a lunch break, excluding company holidays:

Refrigerated truck temperature monitoring

Different temperature thresholds for day and night operation:

Equipment rental periods

Non-recurring schedule for specific rental dates:

Attaching schedules to other entities

A schedule on its own is just calendar data. To put it to work, link it to the entity it governs, such as the asset it defines maintenance windows for. The link is a REFERENCE custom field on the asset type, with refEntityTypeCode: "schedule" fixing what the field may point at. The same pattern links entities to tags and catalog items — only the refEntityTypeCode changes.

1

Define the reference field

Add the field definition to the asset type. See Implementing custom fields for the full definition workflow:

The response returns the type with its incremented version, as in the earlier update examples.

2

Attach the schedule to an asset

Set the field on the asset, using the schedule's ID as the value. isPrimary is required on every reference value, so state it explicitly:

The response confirms the update with the asset's new version.

3

Read the schedule back from the asset

Select the field and use inline fragments, first on the value type, then on Schedule:

Response:

refs is always a list, and a single-value field returns one element. An element is null when the referenced schedule was deleted after being linked, so handle that case when reading.

To detach the schedule, remove the field with unset: ["cf_maintenance_schedule"] in assetUpdate, the same way device fields are unlinked in Working with assets.

See also

Last updated

Was this helpful?