We value your privacy

We use cookies to enhance your browsing experience, serve personalised ads or content, and analyse our traffic. By clicking "Accept All", you consent to our use of cookies.

CustomiseReject AllAccept All

Powered by [Visit CookieYes website](https://www.cookieyes.com/product/cookie-consent/?ref=cypbcyb&utm_source=cookie-banner&utm_medium=fl-branding)

Customise Consent Preferences

We use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below.

The cookies that are categorised as "Necessary" are stored on your browser as they are essential for enabling the basic functionalities of the site. ... Show more

NecessaryAlways Active

Necessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data.

No cookies to display.

Functional

Functional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features.

- Cookie

\_octo

- Duration

1 year

- Description

No description available.

- Cookie

logged\_in

- Duration

1 year

- Description

No description available.

- Cookie

VISITOR\_PRIVACY\_METADATA

- Duration

6 months

- Description

YouTube sets this cookie to store the user's cookie consent state for the current domain.

- Cookie

\_\_Secure-YNID

- Duration

6 months

- Description

YouTube cookie used to protect user security and prevent fraud, especially during the login process.

- Cookie

\_\_Secure-ROLLOUT\_TOKEN

- Duration

6 months

- Description

YouTube sets this cookie to manage feature rollout and experimentation. It helps Google control which new features or interface changes are shown to users as part of testing and staged rollouts, ensuring consistent experience for a given user during an experiment.

- Cookie

\_\_Secure-YEC

- Duration

past

- Description

Description is currently not available.

- Cookie

\_gh\_sess

- Duration

session

- Description

GitHub sets this cookie for temporary application and framework state between pages like what step the user is on in a multiple step form.

- Cookie

YSC

- Duration

session

- Description

YSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages.

- Cookie

VISITOR\_INFO1\_LIVE

- Duration

6 months

- Description

A cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface.

- Cookie

ytidb::LAST\_RESULT\_ENTRY\_KEY

- Duration

Never Expires

- Description

The cookie ytidb::LAST\_RESULT\_ENTRY\_KEY is used by YouTube to store the last search result entry that was clicked by the user. This information is used to improve the user experience by providing more relevant search results in the future.

Analytics

Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.

- Cookie

\_ga

- Duration

1 year 1 month 4 days

- Description

Google Analytics sets this cookie to calculate visitor, session and campaign data and track site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognise unique visitors.

- Cookie

\_ga\_\*

- Duration

1 year 1 month 4 days

- Description

Google Analytics sets this cookie to store and count page views.

Performance

Performance cookies are used to understand and analyse the key performance indexes of the website which helps in delivering a better user experience for the visitors.

No cookies to display.

Advertisement

Advertisement cookies are used to provide visitors with customised advertisements based on the pages you visited previously and to analyse the effectiveness of the ad campaigns.

No cookies to display.

Uncategorised

Other uncategorised cookies are those that are being analysed and have not been classified into a category as yet.

No cookies to display.

Reject AllSave My PreferencesAccept All

Powered by [Visit CookieYes website](https://www.cookieyes.com/product/cookie-consent/?ref=cypbcyb&utm_source=cookie-banner&utm_medium=sl-branding)

[Skip to main content](/content/docs/scheduling/defining-availability#__docusaurus_skipToContent_fallback/index.html)

On this page

Beta

Medplum Scheduling APIs are currently in [beta](/content/docs/compliance/alpha-beta/index.html).

This guide covers how to configure availability using the `SchedulingParameters` extension — at both the actor level (per Schedule) and the service type level (via HealthcareService). It covers scheduling constraints, field-level inheritance, override behavior, timezone handling, and multi-resource scheduling patterns.

Parameters may be defined on a HealthcareService and shared amongst all Schedules that book that type
of appointment. Each Schedule may also override these parameters to define behaviors specific to that
schedule as needed.

A few constraints trip people up most often — see [Common Pitfalls](/content/docs/scheduling/defining-availability#common-pitfalls/index.html) for a quick reference before you start.

The diagram below shows how availability can be defined at both

- The [actor level](/content/docs/scheduling/defining-availability#actor-level-availability/index.html) (via Schedule)
- The [service level](/content/docs/scheduling/defining-availability#service-level-availability/index.html) (via HealthcareService)

inherited by

inherited by

Practitioner

\*Dr. Smith\*

Schedule

\*Dr. Smith's Schedule\*

**Actor-level overrides**

ex. _availability: Mon–Wed only_

HealthcareService

\*Initial Visit Defaults\*

**Service-level defaults**

ex. _1hr slot duration, 10min buffer_

_Mon–Fri 9am–5pm availability_

Schedule

\*Dr. Johnson's Schedule\*

**no overrides**

_uses all service defaults_

Practitioner

\*Dr. Johnson\*

Slot

\*status: busy\*

Slot

\*status: busy-unavailable\*

Slot

\*status: busy\*

Slot

\*status: busy-unavailable\*

Appointment 1

\*status: booked\*

Appointment 2

\*status: booked\*

## The Scheduling Parameters Extension [​](/content/docs/scheduling/defining-availability\#the-scheduling-parameters-extension "Direct link to The Scheduling Parameters Extension"/index.html)

All scheduling constraints are managed through a single consolidated extension: `SchedulingParameters`. This extension can appear on both [HealthcareService](/content/docs/api/fhir/resources/healthcareservice/index.html) (for shared configuration) and [Schedule](/content/docs/api/fhir/resources/schedule/index.html).

To use scheduling APIs for a Schedule and HealthcareService, at least one of them must define the `duration` attribute (used to set how long the scheduled appointment will last). There must be a `timezone` attribute, which may also be defined on the Schedule's actor. (See [Timezone Resolution](/content/docs/scheduling/defining-availability#timezone-resolution/index.html))

When using scheduling APIs to interact with multiple `Schedule` resources at once, they must be configured with matching `duration`, `alignmentInterval`, `alignmentTimezone`, and `alignmentOffset` parameters. For this reason, Medplum recommends that these parameters only be set on `HealthcareService` resources.

#### Extension Fields [​](/content/docs/scheduling/defining-availability\#extension-fields "Direct link to Extension Fields"/index.html)

| Url | Type | Default Value | Description | `HealthcareService` usage notes | `Schedule` usage notes |
| --- | --- | --- | --- | --- | --- |
| `duration` | [Duration](/content/docs/api/fhir/datatypes/duration/index.html) | _none_ | Determines how long the time increments for a Slot are |  | Recommended to prefer setting this on `HealthcareService` |
| `timezone` | Code | _none_ | Specifies the timezone (IANA timezone identifier, e.g., `America/New_York`) for interpreting availability. When not set, falls back to the `Schedule.actor`'s timezone. |  |  |
| `bufferBefore` | [Duration](/content/docs/api/fhir/datatypes/duration/index.html) | 0 minutes (no buffer needed) | Sets prep-time needed before appointment start. It must be free at booking time, and will be reserved with a Slot. |  |  |
| `bufferAfter` | [Duration](/content/docs/api/fhir/datatypes/duration/index.html) | 0 minutes (no buffer needed) | Sets cleanup time needed after appointment end. It must be free at booking time, and will be reserved with a Slot. |  |  |
| `alignmentInterval` | [Duration](/content/docs/api/fhir/datatypes/duration/index.html) | 60 minutes (appointments start on-the-hour) | Start times must align to this interval (e.g., every 15 minutes) |  | Recommended to prefer setting this on `HealthcareService` |
| `alignmentOffset` | [Duration](/content/docs/api/fhir/datatypes/duration/index.html) | 0 minutes | Shifts allowed start times by this offset (e.g., with a 15-minute alignmentInterval and a 5-minute alignmentOffset, valid starts are :05, :20, :35, :50) |  | Recommended to prefer setting this on `HealthcareService` |
| `alignmentTimezone` | Code | 'Etc/UTC' | Anchors the alignment grid to local midnight of the given timezone, keeping start times stable across DST transitions. |  | Recommended to prefer setting this on `HealthcareService` |
| `service` | `Reference(HealthcareService)` | _none_ | Pointer to the `HealthcareService` that these parameters should override. | Not permitted |  |
| `availability` | [Nested Extension](/content/docs/scheduling/defining-availability#availability-extension/index.html) | Always available | Weekly recurring availability windows. When set, appointments must fit inside these windows. | Not permitted (use `HealthcareService.availableTime` instead) |  |

Example of the `SchedulingParameters` extension on a `HealthcareService`

```ts
{

url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',

// set duration / alignmentInterval / alignmentTimezone HERE; all schedules booked together must match on these

extension: [\
\
    // Recommended: duration determines how long the time increments for a Slot are.\
\
    // If not set here, must be defined on all related Schedules. To book on multiple\
\
    // schedules at once, they must all match in this dimension.\
\
    {\
\
      url: 'duration',\
\
      valueDuration: { value: 1, unit: 'h' },\
\
    },\
\
    // Recommended: Time alignment interval (appointment start time boundaries)\
\
    // To book on multiple schedules at once, they must all match in this dimension.\
\
    {\
\
      url: 'alignmentInterval',\
\
      valueDuration: { value: 15, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
    },\
\
    // Recommended: Time alignment offset (shift from interval boundaries)\
\
    // To book on multiple schedules at once, they must all match in this dimension.\
\
    {\
\
      url: 'alignmentOffset',\
\
      valueDuration: { value: 0, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
    },\
\
    // Recommended: Timezone for anchoring the alignment grid to local midnight\
\
    // To book on multiple schedules at once, they must all match in this dimension.\
\
    // This parameter is Independent of `timezone`, which controls availability window interpretation\
\
    {\
\
      url: 'alignmentTimezone',\
\
      valueCode: 'America/New_York',\
\
    },\
\
    // Optional: specify time zone for availability interpretation\
\
    // Falls back to Schedule's actor time zone if not specified\
\
    {\
\
      url: 'timezone',\
\
      valueCode: 'America/Los_Angeles',\
\
    },\
\
    // Optional: Buffer time required before appointment\
\
    {\
\
      url: 'bufferBefore',\
\
      valueDuration: { value: 15, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
    },\
\
    // Optional: Buffer time required after appointment\
\
    {\
\
      url: 'bufferAfter',\
\
      valueDuration: { value: 10, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
    },\
\
  ],

};
```

Example of the `SchedulingParameters` extension on a `Schedule`

```ts
{

url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',

extension: [\
\
    // Required on Schedule: you must specify what type of appointment these parameters apply to\
\
    {\
\
      url: 'service',\
\
      valueReference: {\
\
        reference: 'HealthcareService/5d02acfd-fbe8-4537-84e4-31f5116be105',\
\
        display: 'Bariatric Surgery',\
\
      },\
\
    },\
\
    // Optional: specify time zone for availability interpretation\
\
    // Falls back to Schedule's actor time zone if not specified\
\
    {\
\
      url: 'timezone',\
\
      valueCode: 'America/Los_Angeles',\
\
    },\
\
    // Optional: duration determines how long the time increments for a Slot are.\
\
    // If not set here, must be defined on the related HealthcareService\
\
    {\
\
      url: 'duration',\
\
      valueDuration: { value: 1, unit: 'h' },\
\
    },\
\
    // Recurring availability (Schedule only)\
\
    {\
\
      url: 'availability',\
\
      extension: [\
\
        {\
\
          url: 'availableTime',\
\
          extension: [\
\
            { url: 'daysOfWeek', valueCode: 'mon' },\
\
            { url: 'daysOfWeek', valueCode: 'wed' },\
\
            { url: 'daysOfWeek', valueCode: 'fri' },\
\
            { url: 'availableStartTime', valueTime: '09:00:00' },\
\
            { url: 'availableEndTime', valueTime: '17:00:00' },\
\
          ],\
\
        },\
\
      ],\
\
    },\
\
    // Buffer time before appointment\
\
    {\
\
      url: 'bufferBefore',\
\
      valueDuration: { value: 15, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
    },\
\
    // Buffer time after appointment\
\
    {\
\
      url: 'bufferAfter',\
\
      valueDuration: { value: 10, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
    },\
\
    // Time alignment interval (appointment start time boundaries)\
\
    // Recommended to be set on `HealthcareService` and inherited: to book multiple schedules at once,\
\
    // they must all match in this dimension.\
\
    {\
\
      url: 'alignmentInterval',\
\
      valueDuration: { value: 15, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
    },\
\
    // Time alignment offset (shift from interval boundaries)\
\
    // Recommended to be set on `HealthcareService` and inherited: to book multiple schedules at once,\
\
    // they must all match in this dimension.\
\
    {\
\
      url: 'alignmentOffset',\
\
      valueDuration: { value: 0, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
    },\
\
    // Timezone for anchoring the alignment grid to local midnight\
\
    // Recommended to be set on `HealthcareService` and inherited: to book multiple schedules at once,\
\
    // they must all match in this dimension.\
\
    {\
\
      url: 'alignmentTimezone',\
\
      valueCode: 'America/New_York',\
\
    },\
\
  ],

};
```

### Alignment grid [​](/content/docs/scheduling/defining-availability\#alignment-grid "Direct link to Alignment grid"/index.html)

Medplum Scheduling APIs generate possible appointments by projecting a repeating daily grid. These parameters control that grid:

| Parameter | Description | Default |
| --- | --- | --- |
| `alignmentInterval` | How frequently slot start times occur | 60 minutes |
| `alignmentOffset` | Shifts slot start times by this amount | 0 minutes |
| `alignmentTimezone` | What timezone the grid is anchored to | `Etc/UTC` |

#### `alignmentInterval` [​](/content/docs/scheduling/defining-availability\#alignmentinterval "Direct link to alignmentinterval"/index.html)

Sets how frequently appointments may begin. For back-to-back scheduling without gaps, set this value to match the `duration` parameter.

#### `alignmentOffset` [​](/content/docs/scheduling/defining-availability\#alignmentoffset "Direct link to alignmentoffset"/index.html)

Example: to align your appointments starting at 9:15, 10:15, ..., set `alignmentOffset` to 15 minutes (with a 60-minute `alignmentInterval`).

#### `alignmentTimezone` [​](/content/docs/scheduling/defining-availability\#alignmenttimezone "Direct link to alignmenttimezone"/index.html)

When clocks change for DST, slots appear to shift by an hour in local time — for example, a 9:00am slot may appear at 8:00am or 10:00am. Setting `alignmentTimezone` anchors the grid to local midnight instead, keeping slot times consistent year-round.

**Relationship to `timezone`:** The two fields serve distinct purposes and can be set independently:

- `timezone` — which timezone to use when reading `availableStartTime`/`availableEndTime` values
- `alignmentTimezone` — which timezone's midnight to use as the alignment grid anchor

The rare case in which they differ: a provider whose availability hours and appointment grid are managed in different timezones.

## Actor Level Availability [​](/content/docs/scheduling/defining-availability\#actor-level-availability "Direct link to Actor Level Availability"/index.html)

Actor-level availability is defined per [`Schedule`](/content/docs/api/fhir/resources/schedule/index.html) and is resolved **field-by-field against** the [service-level parameters](/content/docs/scheduling/defining-availability#service-level-availability/index.html) on the [HealthcareService](/content/docs/api/fhir/resources/healthcareservice/index.html):

- For each parameter (`duration`, buffers, alignment, **and `availability`**), the value set on the Schedule **overrides** the HealthcareService value for that field; any field the Schedule does not set is **inherited** from the service. This is an override, not a merge — a Schedule that sets its own `availability` fully replaces the service's `availableTime`, rather than narrowing it. See [Override Behavior](/content/docs/scheduling/defining-availability#override-behavior/index.html) for the full resolution order.
- A separate **intersection** applies only when [booking across multiple Schedules at once](/content/docs/scheduling/defining-availability#example-3-location-specific-complex-surgical-scheduling/index.html) (e.g. surgeon + room + anesthesiologist): a time is offered only when _every_ required Schedule is available. That intersection is between Schedules — not between a Schedule and its HealthcareService.

### The Concept of Implicit Availability [​](/content/docs/scheduling/defining-availability\#the-concept-of-implicit-availability "Direct link to The Concept of Implicit Availability"/index.html)

Medplum's scheduling model uses **implicit availability**: time is assumed to be free by default. You define availability rules using extensions that specify when resources are available based on recurring patterns. [`Slot`](/content/docs/api/fhir/resources/slot/index.html) resources are only used for explicit overrides—either to mark time as busy (when an appointment is booked) or to block out unavailable time.

This approach avoids the need to pre-generate thousands of Slot resources for every possible time slot. Instead, the system calculates available windows dynamically based on the availability rules you define.

### The Schedule Resource [​](/content/docs/scheduling/defining-availability\#the-schedule-resource "Direct link to The Schedule Resource"/index.html)

The [`Schedule`](/content/docs/api/fhir/resources/schedule/index.html) resource is the foundation for defining actor-level availability for a provider, location, or device.

The Schedule resource should define the service types that it is capable of acting on in its `serviceType` attribute. To use Medplum Scheduling APIs, this should include the extension `https://medplum.com/fhir/service-type-reference` holding a reference to the matching HealthcareService.

Here is an example of a [Schedule](/content/docs/api/fhir/resources/schedule/index.html) resource that defines availability for a [Practitioner](/content/docs/api/fhir/resources/practitioner/index.html).

```ts
{

resourceType: 'Schedule',

id: 'dr-smith-schedule',

// Schedule has no 'name' field in R4 - use comment for a label

comment: "Dr. Smith's Office Visit availability",

// Practitioner or PractitionerRole; the actor must carry a timezone extension

actor: [{ reference: 'Practitioner/dr-smith' }],

// `serviceType` links this schedule to the HealthcareService resources that it is permitted to use for scheduling

serviceType: [\
\
    {\
\
      text: 'Office Visit',\
\
      coding: [{ code: 'office-visit' }],\
\
      extension: [\
\
        {\
\
          url: 'https://medplum.com/fhir/service-type-reference',\
\
          valueReference: {\
\
            reference: 'HealthcareService/23c3f1cc-4f55-4990-9775-511b02487e7e',\
\
            display: 'Office Visit',\
\
          },\
\
        },\
\
      ],\
\
    },\
\
  ],

extension: [\
\
    // This extension applies custom recurring availability when this schedule is\
\
    // used with the "Office Visit" service type.\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        {\
\
          url: 'service',\
\
          valueReference: {\
\
            reference: 'HealthcareService/23c3f1cc-4f55-4990-9775-511b02487e7e',\
\
            display: 'Office Visit',\
\
          },\
\
        },\
\
        {\
\
          url: 'availability',\
\
          extension: [\
\
            {\
\
              url: 'availableTime',\
\
              extension: [\
\
                { url: 'availableStartTime', valueTime: '09:00:00' },\
\
                { url: 'availableEndTime', valueTime: '17:00:00' },\
\
                { url: 'daysOfWeek', valueCode: 'mon' },\
\
                { url: 'daysOfWeek', valueCode: 'tue' },\
\
                { url: 'daysOfWeek', valueCode: 'wed' },\
\
                { url: 'daysOfWeek', valueCode: 'thu' },\
\
                { url: 'daysOfWeek', valueCode: 'fri' },\
\
              ],\
\
            },\
\
          ],\
\
        },\
\
      ],\
\
    },\
\
  ],

};
```

`Schedule` has no `name` element

In FHIR R4, [`Schedule`](/content/docs/api/fhir/resources/schedule/index.html) has no `name` element. This is easy to trip over because `Organization`, `HealthcareService`, and `Location` all _do_ have `name`. Sending `name` on a `Schedule` fails validation with `Invalid additional property "name"`.

Instead, use `comment` — a free-text field that's a good place for a human-readable description or label (for example, `"Dr. Smith's Office Visit availability"`).

#### Choosing the actor: `Practitioner` vs `PractitionerRole` [​](/content/docs/scheduling/defining-availability\#choosing-the-actor-practitioner-vs-practitionerrole "Direct link to choosing-the-actor-practitioner-vs-practitionerrole"/index.html)

`Schedule.actor` may reference a [`Practitioner`](/content/docs/api/fhir/resources/practitioner/index.html), [`PractitionerRole`](/content/docs/api/fhir/resources/practitionerrole/index.html), [`Location`](/content/docs/api/fhir/resources/location/index.html), or [`Device`](/content/docs/api/fhir/resources/device/index.html). When the actor is a person:

- Use **`Practitioner`** when availability is for the individual regardless of role or location.
- Use **`PractitionerRole`** when availability is specific to a role, organization, or location binding (for example, when licensure varies by state — see [state-by-state licensure](/content/docs/scheduling/state-by-state-licensure/index.html)).

### `availability` Extension [​](/content/docs/scheduling/defining-availability\#availability-extension "Direct link to availability-extension"/index.html)

The `availability` sub-extension mirrors the FHIR R5+ [`Availability`](https://hl7.org/fhir/R5/metadatatypes.html#Availability) datatype shape. It is encoded using nested R4 extensions (because R4 does not have a native `Availability` data type). This is close to the R4 definition of `HealthcareService.availabileTime`, which is another possible source of scheduling availability data. If this sub-extension is not present, availability is constrained only by the presence of existing `Slot` resources for the schedule.

| Sub-extension | Type | Description | Repeatable |
| --- | --- | --- | --- |
| `availableTime` | (nested) | One entry per availability window | Yes |
| ↳ `daysOfWeek` | `valueCode` | One entry per day (`mon`–`sun`) | Yes |
| ↳ `allDay` | `valueBoolean` | If `true`, window spans the full day | No |
| ↳ `availableStartTime` | `valueTime` | Opening time (not allowed when `allDay` is present) | No |
| ↳ `availableEndTime` | `valueTime` | Closing time (not allowed when `allDay` is present) | No |
| `notAvailableTime` | (nested) | Typed for future use; not yet processed | Yes |

```ts
{

resourceType: 'Schedule',

id: 'dr-smith-schedule',

comment: "Dr. Smith's Office Visit availability",

actor: [{ reference: 'Practitioner/dr-smith' }],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        {\
\
          url: 'service',\
\
          valueReference: {\
\
            reference: 'HealthcareService/23c3f1cc-4f55-4990-9775-511b02487e7e',\
\
            display: 'Office Visit',\
\
          },\
\
        },\
\
        {\
\
          url: 'duration',\
\
          valueDuration: { value: 1, unit: 'h' },\
\
        },\
\
        {\
\
          url: 'availability',\
\
          extension: [\
\
            {\
\
              url: 'availableTime',\
\
              extension: [\
\
                { url: 'daysOfWeek', valueCode: 'mon' },\
\
                { url: 'daysOfWeek', valueCode: 'tue' },\
\
                { url: 'daysOfWeek', valueCode: 'wed' },\
\
                { url: 'daysOfWeek', valueCode: 'thu' },\
\
                { url: 'daysOfWeek', valueCode: 'fri' },\
\
                { url: 'availableStartTime', valueTime: '09:00:00' },\
\
                { url: 'availableEndTime', valueTime: '17:00:00' },\
\
              ],\
\
            },\
\
          ],\
\
        },\
\
      ],\
\
    },\
\
  ],

};
```

#### Windows that cross midnight [​](/content/docs/scheduling/defining-availability\#windows-that-cross-midnight "Direct link to Windows that cross midnight"/index.html)

When `availableEndTime` is less than or equal to `availableStartTime`, the window is read as continuing into the following day. An entry of `{ daysOfWeek: ['tue'], availableStartTime: '22:00:00', availableEndTime: '06:00:00' }` means 10pm Tuesday until 6am Wednesday. This is also how 24-hour availability is expressed without `allDay`: `00:00:00` to `00:00:00` on all seven days, since the FHIR [`time`](https://hl7.org/fhir/R4/datatypes.html#time) type does not permit `24:00`.

Because `00:00:00` is read as the following midnight rather than as the start of the same day, it is the correct way to say "until end of day". Do not use `23:59:59` for this: it leaves a one-second gap that splits the window in two, which prevents booking anything that spans midnight.

A window is keyed to the day it **starts** on, and only that day needs to appear in `daysOfWeek`. An entry on `fri` ending at `06:00:00` makes Saturday morning bookable without `sat` being listed anywhere.

Windows that cross midnight and other FHIR systems

This reading is Medplum's convention. The FHIR specification does not define what an end time before a start time means, and imposes no invariant either way, so the data is valid FHIR but other systems may not interpret it the same way.

## Service Level Availability [​](/content/docs/scheduling/defining-availability\#service-level-availability "Direct link to Service Level Availability"/index.html)

### Service Types and HealthcareService [​](/content/docs/scheduling/defining-availability\#service-types-and-healthcareservice "Direct link to Service Types and HealthcareService"/index.html)

A [HealthcareService](/content/docs/api/fhir/resources/healthcareservice/index.html) gives a mechanism to define common scheduling parameters for an appointment type, which can then be used by multiple [Practitioner](/content/docs/api/fhir/resources/practitioner/index.html)'s [Schedules](/content/docs/api/fhir/resources/schedule/index.html). This allows you to define standard appointment durations, buffer times, and grid alignment settings once and apply them across multiple providers.

In Medplum scheduling, **one HealthcareService represents one bookable appointment type** (e.g. "Office Visit", "New Patient Visit", "Bariatric Surgery"). It is the place where the shared `SchedulingParameters` extension lives, so the same `duration`, buffers, and alignment apply everywhere the type is booked.

#### Coding a HealthcareService [​](/content/docs/scheduling/defining-availability\#coding-a-healthcareservice "Direct link to Coding a HealthcareService"/index.html)

A `HealthcareService` carries several `CodeableConcept` fields. The most relevant for scheduling is `type` (the specific appointment/service type that the `Schedule.serviceType` matches against). The full set:

| Field | Meaning | Conventional code system |
| --- | --- | --- |
| `category` | Broad category of service | HL7 [`service-category`](https://hl7.org/fhir/R4/valueset-service-category.html) |
| `type` | The specific service / appointment type | HL7 [`service-type`](https://hl7.org/fhir/R4/valueset-service-type.html), or [SNOMED CT](https://www.snomed.org/) procedure codes |
| `specialty` | Clinical specialties handled by the site | [SNOMED CT](https://www.snomed.org/) practice-setting codes ( [c80-practice-codes](https://hl7.org/fhir/R4/valueset-c80-practice-codes.html)) |

These bindings are **example/preferred, not required** — FHIR does not force a particular terminology here. You can use a code from the HL7 `service-type` value set, a SNOMED CT code (as the surgical examples below do), or your own local system (as the `http://example.org/appointment-types` examples below do). What matters is that the same code/system you put on `HealthcareService.type` is what the `Schedule.serviceType` declares and what `$find` is queried with.

LOINC is not used here

LOINC codes describe observations, lab tests, and documents — not services. Use the HL7 `service-type`/`service-category` systems or SNOMED CT for a `HealthcareService`, not LOINC.

For a `Schedule` to use the `HealthcareService`'s scheduling parameters, the `Schedule.serviceType` must include a reference to the HealthcareService in its extensions.

```ts
{

resourceType: 'HealthcareService',

id: '23c3f1cc-4f55-4990-9775-511b02487e7e',

type: [\
\
    {\
\
      text: 'Office Visit',\
\
      coding: [{ system: 'http://example.org/appointment-types', code: 'office-visit' }],\
\
    },\
\
  ],

availableTime: [\
\
    {\
\
      daysOfWeek: ['mon', 'tue', 'wed', 'thu', 'fri'],\
\
      availableStartTime: '09:00:00',\
\
      availableEndTime: '17:00:00',\
\
    },\
\
  ],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      // set duration / alignmentInterval / alignmentTimezone HERE; all schedules booked together must match on these\
\
      extension: [\
\
        {\
\
          url: 'duration',\
\
          valueDuration: { value: 1, unit: 'h' },\
\
        },\
\
      ],\
\
    },\
\
  ],

};
```

```ts
{

resourceType: 'Schedule',

id: 'dr-smith-schedule',

comment: "Dr. Smith's Office Visit availability",

actor: [{ reference: 'Practitioner/dr-smith' }],

};
```

### Override Behavior [​](/content/docs/scheduling/defining-availability\#override-behavior "Direct link to Override Behavior"/index.html)

A [Practitioner](/content/docs/api/fhir/resources/practitioner/index.html)'s [Schedule](/content/docs/api/fhir/resources/schedule/index.html) can override individual scheduling parameters for a specific service type by adding a `SchedulingParameters` extension that references that service. **Only the fields explicitly set on the Schedule override the HealthcareService defaults** — all other fields are inherited.

This means you only need to specify what differs. For example, to restrict availability to Tuesday and Thursday mornings while keeping all other parameters (duration, buffers, alignment) from the HealthcareService:

```ts
{

resourceType: 'Schedule',

id: 'dr-chen-schedule',

active: true,

comment: 'Dr. Chen - New Patient Visit (Tue/Thu mornings only)',

actor: [{ reference: 'Practitioner/dr-chen' }],

serviceType: [\
\
    {\
\
      coding: [{ code: 'new-patient-visit' }],\
\
      extension: [\
\
        {\
\
          url: 'https://medplum.com/fhir/service-type-reference',\
\
          valueReference: {\
\
            reference: 'HealthcareService/f44bbf25-bf57-4263-8f10-be060cc91672',\
\
            display: 'New Patient Visit',\
\
          },\
\
        },\
\
      ],\
\
    },\
\
  ],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        {\
\
          // required: identifies which HealthcareService these parameters override\
\
          url: 'service',\
\
          valueReference: {\
\
            reference: 'HealthcareService/f44bbf25-bf57-4263-8f10-be060cc91672',\
\
            display: 'New Patient Visit',\
\
          },\
\
        },\
\
        {\
\
          // overridden here; duration, buffers, and alignment are inherited from HealthcareService\
\
          url: 'availability',\
\
          extension: [\
\
            {\
\
              url: 'availableTime',\
\
              extension: [\
\
                { url: 'daysOfWeek', valueCode: 'tue' },\
\
                { url: 'daysOfWeek', valueCode: 'thu' },\
\
                { url: 'availableStartTime', valueTime: '09:00:00' },\
\
                { url: 'availableEndTime', valueTime: '13:00:00' },\
\
              ],\
\
            },\
\
          ],\
\
        },\
\
      ],\
\
    },\
\
  ],

};
```

**Field-level inheritance**: When a Schedule has a `SchedulingParameters` extension for a service, each field is resolved independently using this priority order (highest to lowest):

1. The field value from the Schedule's `SchedulingParameters` extension for that service
2. The field value from the HealthcareService's `SchedulingParameters` extension
3. The system default (0 for buffers and offset; 60 minutes for alignment interval; always-available for availability)
4. Per-actor timezone information (via `Schedule.actor`; only for `timezone` attribute)

If a Schedule has **no**`SchedulingParameters` extension at all, all parameters are inherited from this chain.

### Blocking Time by Service Type [​](/content/docs/scheduling/defining-availability\#blocking-time-by-service-type "Direct link to Blocking Time by Service Type"/index.html)

Here is an example of a [Slot](/content/docs/api/fhir/resources/slot/index.html) resource that blocks time for a specific service type.

```ts
{

resourceType: 'Slot',

schedule: { reference: 'Schedule/dr-johnson-schedule' },

status: 'busy-unavailable',

start: '2025-12-24T08:00:00Z',

end: '2025-12-27T07:59:59Z',

comment: 'Holiday vacation',

serviceType: [{ coding: [{ code: 'office-visit' }] }],

};
```

- **With serviceType**: Blocks only that specific service
- **Without serviceType**: Blocks all services

## Timezone Resolution [​](/content/docs/scheduling/defining-availability\#timezone-resolution "Direct link to Timezone Resolution"/index.html)

### Timezone per Scheduling Parameters Entry [​](/content/docs/scheduling/defining-availability\#timezone-per-scheduling-parameters-entry "Direct link to Timezone per Scheduling Parameters Entry"/index.html)

The `timezone` parameter allows you to specify different timezones for different service types within the same Schedule. This is useful when a provider needs to define availability in different timezones for different services (e.g., a doctor who provides cardiac surgery where they might travel to in one time zone and call center availability in another time zone).

**Fallback Logic:** If no time zone is specified in the `scheduling-parameters` extension, then the availability will be interpreted in the time zone defined on the Schedule's actor reference (Practitioner, Location, or Device). It looks for the FHIR sanctioned time zone extension:

Adding a Timezone to an Actor

There is no native timezone field on [`Practitioner`](/content/docs/api/fhir/resources/practitioner/index.html), [`Location`](/content/docs/api/fhir/resources/location/index.html), or [`Device`](/content/docs/api/fhir/resources/device/index.html), so you must add it via the FHIR timezone extension:

```ts
{

resourceType: 'Practitioner',

// ...

extension: [\
\
    {\
\
      url: 'http://hl7.org/fhir/StructureDefinition/timezone',\
\
      valueCode: 'America/Los_Angeles',\
\
    },\
\
  ],

};
```

**Timezone Resolution Order:**

1. If `timezone` is specified in the `scheduling-parameters` extension of a `Schedule` resource, use that time zone
2. If `timezone` is specified in the `scheduling-parameters` extension of a `HealthcareService` resource, use that time zone
3. Otherwise, fall back to the time zone defined on the Schedule's actor reference (Practitioner, PractitionerRole, Location, or Device)

**Important Notes:**

- The time zone value should be an IANA time zone identifier (e.g., `America/New_York`, `America/Los_Angeles`, `America/Miami`)
- When `timezone` is specified, all Time values in the `availability` extension are interpreted in that time zone

Here is an example of a Schedule with multiple service types, each with its own time zone:

```ts
{

resourceType: 'Schedule',

id: 'dr-smith-schedule',

comment: 'Dr. Smith - Cardiac Surgery (PT) and Call Center (ET)',

actor: [{ reference: 'Practitioner/dr-smith' }],

serviceType: [\
\
    {\
\
      text: 'Cardiac Surgery',\
\
      coding: [{ code: 'cardiac-surgery' }],\
\
    },\
\
    {\
\
      text: 'Call Center Availability',\
\
      coding: [{ code: 'call-center-availability' }],\
\
    },\
\
  ],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        {\
\
          url: 'service',\
\
          valueReference: {\
\
            reference: 'HealthcareService/a8f88a98-2578-4644-b408-7ba73f104298',\
\
            display: 'Cardiac Surgery',\
\
          },\
\
        },\
\
        {\
\
          url: 'timezone',\
\
          valueCode: 'America/Los_Angeles',\
\
        },\
\
        {\
\
          url: 'duration',\
\
          valueDuration: { value: 1, unit: 'h' },\
\
        },\
\
        {\
\
          url: 'availability',\
\
          extension: [\
\
            {\
\
              url: 'availableTime',\
\
              extension: [\
\
                { url: 'daysOfWeek', valueCode: 'mon' },\
\
                { url: 'daysOfWeek', valueCode: 'tue' },\
\
                { url: 'daysOfWeek', valueCode: 'wed' },\
\
                { url: 'availableStartTime', valueTime: '11:00:00' }, // Interpreted in America/Los_Angeles\
\
                { url: 'availableEndTime', valueTime: '15:00:00' }, // Interpreted in America/Los_Angeles\
\
              ],\
\
            },\
\
          ],\
\
        },\
\
      ],\
\
    },\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        {\
\
          url: 'timezone',\
\
          valueCode: 'America/New_York',\
\
        },\
\
        {\
\
          url: 'service',\
\
          valueReference: {\
\
            reference: 'HealthcareService/0dbe6bf1-40b8-4204-a406-f78b5a0e59d0',\
\
            display: 'Call Center Availability',\
\
          },\
\
        },\
\
        {\
\
          url: 'duration',\
\
          valueDuration: { value: 1, unit: 'h' },\
\
        },\
\
        {\
\
          url: 'availability',\
\
          extension: [\
\
            {\
\
              url: 'availableTime',\
\
              extension: [\
\
                { url: 'daysOfWeek', valueCode: 'mon' },\
\
                { url: 'daysOfWeek', valueCode: 'tue' },\
\
                { url: 'daysOfWeek', valueCode: 'wed' },\
\
                { url: 'availableStartTime', valueTime: '09:00:00' }, // Interpreted in America/New_York\
\
                { url: 'availableEndTime', valueTime: '17:00:00' }, // Interpreted in America/New_York\
\
              ],\
\
            },\
\
          ],\
\
        },\
\
      ],\
\
    },\
\
  ],

};
```

In this example:

- Cardiac surgery availability is defined in `America/Los_Angeles` time zone (Mon-Wed 11am-3pm America/Los Angeles)
- Call Center availability is defined in `America/New_York` time zone (Mon-Wed 9am-5pm Eastern)
- Each service type's availability times are interpreted independently based on their respective timezones

## Editing Availability in a React App [​](/content/docs/scheduling/defining-availability\#editing-availability-in-a-react-app "Direct link to Editing Availability in a React App"/index.html)

Rather than hand-authoring the [`availability` extension](/content/docs/scheduling/defining-availability#availability-extension/index.html), the [`@medplum/react-scheduling`](https://www.npmjs.com/package/@medplum/react-scheduling) library provides a `ScheduleAvailabilityEditor` component. It edits a Schedule's weekly `availability` override for a given visit service type, or, with the `schedule` prop omitted, the [service-level default](/content/docs/scheduling/defining-availability#service-level-availability/index.html) hours themselves. It implements the [override behavior](/content/docs/scheduling/defining-availability#override-behavior/index.html) described above through a single switch, and is used in the [Medplum Provider](https://github.com/medplum/medplum/tree/main/examples/medplum-provider) example app.

The helpers the component reads and writes the override through, `getEffectiveAvailability` and `setScheduleAvailability`, are exported from the same library. They are plain functions over FHIR resources, so a custom editor can use them without the component. `@medplum/core` holds the generic scheduling parameter helpers underneath them — `getScheduleParameters`, `setScheduleParameter`, and `clearScheduleParameter` — which read and write any parameter, `availability` included, as extensions.

See the [`ScheduleAvailabilityEditor` docs in Storybook](https://storybook.medplum.com/?path=/docs/medplum-scheduleavailabilityeditor--docs) for interactive examples, the behavior in detail, and the full component and utility API.

## Examples [​](/content/docs/scheduling/defining-availability\#examples "Direct link to Examples"/index.html)

### Example 1: Simple Primary Care Office with Appointment Type Defaults [​](/content/docs/scheduling/defining-availability\#example-1-simple-primary-care-office-with-appointment-type-defaults "Direct link to Example 1: Simple Primary Care Office with Appointment Type Defaults"/index.html)

This example shows how to define availability for a simple primary care office where Practitioner's Schedules inherit default scheduling parameters from an HealthcareService.

HealthcareService: Office Visit Defaults

This HealthcareService defines default scheduling parameters for a 30-minute office visit with 5-minute buffers and 15-minute alignment intervals.

```ts
{

resourceType: 'HealthcareService',

id: 'office-visit',

type: [\
\
    {\
\
      text: 'Office Visit',\
\
      coding: [{ system: 'http://example.org/appointment-types', code: 'office-visit', display: 'Office Visit' }],\
\
    },\
\
  ],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        {\
\
          url: 'duration',\
\
          valueDuration: { value: 30, unit: 'min' },\
\
        },\
\
        { url: 'bufferBefore', valueDuration: { value: 5, unit: 'min' } },\
\
        { url: 'bufferAfter', valueDuration: { value: 5, unit: 'min' } },\
\
        { url: 'alignmentInterval', valueDuration: { value: 15, unit: 'min' } },\
\
        { url: 'alignmentOffset', valueDuration: { value: 0, unit: 'min' } },\
\
      ],\
\
    },\
\
  ],

};
```

Schedule: Practitioner's Schedule without Overrides

This Schedule shows Dr. Johnson's availability (Mon-Fri 9am-5pm) that inherits all default parameters from the HealthcareService without any service-specific overrides.

```ts
{

resourceType: 'Schedule',

id: 'dr-johnson-schedule',

active: true,

comment: "Dr. Sarah Johnson's Office Visit availability",

actor: [\
\
    {\
\
      reference: 'Practitioner/dr-johnson',\
\
      display: 'Dr. Sarah Johnson',\
\
    },\
\
  ],

planningHorizon: {

start: '2025-01-01T00:00:00Z',

end: '2025-12-31T23:59:59Z',

},

serviceType: [\
\
    // This entry will allow using the office-visit shared HealthcareService definitions\
\
    {\
\
      text: 'Office Visit',\
\
      coding: [{ code: 'office-visit' }],\
\
      extension: [\
\
        {\
\
          url: 'https://medplum.com/fhir/service-type-reference',\
\
          valueReference: {\
\
            reference: 'HealthcareService/office-visit',\
\
            display: 'Office Visit',\
\
          },\
\
        },\
\
      ],\
\
    },\
\
  ],

// No SchedulingParameters extension: everything is inherited from the HealthcareService

};
```

**Result**: Dr. Johnson's schedule inherits all the default parameters from the HealthcareService for an office visit:

- $find called with `service-type=office-visit`: For office visits, available to start every 15 minutes (:00, :15, :30, :45) with 5-minute buffers **\[from HealthcareService\]**

Booking

HealthcareService

\*Office Visit Defaults\*

Schedule

\*Dr. Johnson's Schedule\*

Practitioner

\*Dr. Sarah Johnson\*

Slot

\*status: busy\*

Appointment

\*status: booked\*

### Example 2: Multi-Service Provider with Multiple Appointment Types and Overrides [​](/content/docs/scheduling/defining-availability\#example-2-multi-service-provider-with-multiple-appointment-types-and-overrides "Direct link to Example 2: Multi-Service Provider with Multiple Appointment Types and Overrides"/index.html)

A provider who offers different appointment types with varying availability and constraints. Overrides the default scheduling parameters for new patient visits.

HealthcareService: New Patient Visit

This HealthcareService defines a 60-minute new patient visit with 15-minute buffers and 30-minute alignment intervals.

```ts
{

resourceType: 'HealthcareService',

id: 'new-patient-visit',

type: [\
\
    {\
\
      text: 'New Patient Visit',\
\
      coding: [\
\
        { system: 'http://example.org/appointment-types', code: 'new-patient-visit', display: 'New Patient Visit' },\
\
      ],\
\
    },\
\
  ],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        { url: 'duration', valueDuration: { value: 1, unit: 'h' } },\
\
        { url: 'bufferBefore', valueDuration: { value: 15, unit: 'min' } },\
\
        { url: 'bufferAfter', valueDuration: { value: 15, unit: 'min' } },\
\
        { url: 'alignmentInterval', valueDuration: { value: 30, unit: 'min' } },\
\
      ],\
\
    },\
\
  ],

};
```

HealthcareService: Follow-up Visit

This HealthcareService defines a 20-minute follow-up visit with 5-minute buffers and 10-minute alignment intervals for more frequent scheduling.

It defines default availability of Monday-Friday, 9am-5pm.

```ts
{

resourceType: 'HealthcareService',

id: 'follow-up-visit',

type: [\
\
    {\
\
      text: 'Follow-up Visit',\
\
      coding: [{ system: 'http://example.org/appointment-types', code: 'follow-up', display: 'Follow-up Visit' }],\
\
    },\
\
  ],

availableTime: [\
\
    {\
\
      daysOfWeek: ['mon', 'tue', 'wed', 'thu', 'fri'],\
\
      availableStartTime: '09:00:00',\
\
      availableEndTime: '17:00:00',\
\
    },\
\
  ],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        { url: 'duration', valueDuration: { value: 20, unit: 'min' } },\
\
        { url: 'bufferBefore', valueDuration: { value: 5, unit: 'min' } },\
\
        { url: 'bufferAfter', valueDuration: { value: 5, unit: 'min' } },\
\
        { url: 'alignmentInterval', valueDuration: { value: 10, unit: 'min' } },\
\
      ],\
\
    },\
\
  ],

};
```

Schedule: Multi-Service with Overrides

This schedule declares in its `serviceType` array that it can be booked for New Patient visits and Follow-Up visits.

This Schedule uses the shared availability from the "Follow-Up" service (Mon-Fri 9am-5pm). It overrides "New Patient Visit" appointment type to only be available on Tuesday and Thursday mornings (9am-1pm).

```ts
{

resourceType: 'Schedule',

id: 'dr-chen-schedule',

active: true,

comment: 'Dr. Chen - New Patient and Follow-up Visits',

actor: [{ reference: 'PractitionerRole/dr-chen' }],

planningHorizon: {

start: '2025-01-01T00:00:00Z',

end: '2025-12-31T23:59:59Z',

},

serviceType: [\
\
    {\
\
      text: 'New Patient Visit',\
\
      coding: [{ system: 'http://example.org/appointment-types', code: 'new-patient-visit' }],\
\
      extension: [\
\
        {\
\
          url: 'https://medplum.com/fhir/service-type-reference',\
\
          valueReference: {\
\
            reference: 'HealthcareService/new-patient-visit',\
\
            display: 'New Patient Visit',\
\
          },\
\
        },\
\
      ],\
\
    },\
\
    {\
\
      text: 'Follow-up Visit',\
\
      coding: [{ system: 'http://example.org/appointment-types', code: 'follow-up' }],\
\
      extension: [\
\
        {\
\
          url: 'https://medplum.com/fhir/service-type-reference',\
\
          valueReference: {\
\
            reference: 'HealthcareService/follow-up',\
\
            display: 'Follow-up Visit',\
\
          },\
\
        },\
\
      ],\
\
    },\
\
  ],

extension: [\
\
    // New patient visits only on Tuesday and Thursday mornings\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        {\
\
          url: 'service',\
\
          valueReference: {\
\
            reference: 'HealthcareService/new-patient-visit',\
\
            display: 'New Patient Visit',\
\
          },\
\
        },\
\
        {\
\
          url: 'availability',\
\
          extension: [\
\
            {\
\
              url: 'availableTime',\
\
              extension: [\
\
                { url: 'daysOfWeek', valueCode: 'tue' },\
\
                { url: 'daysOfWeek', valueCode: 'thu' },\
\
                { url: 'availableStartTime', valueTime: '09:00:00' },\
\
                { url: 'availableEndTime', valueTime: '13:00:00' },\
\
              ],\
\
            },\
\
          ],\
\
        },\
\
      ],\
\
    },\
\
  ],

};
```

**Result**:

- **New patient visits (ie. `$find` with the "New patient visit" HealthcareService)**: Tue/Thu 9am-1pm only, 60 minutes, can start every 30 minutes, 15-min buffers
- **Follow-ups (ie. `$find` called with the "Follow-up visit" HealthcareService)**: Mon-Fri 9am-5pm, 20 minutes, can start every 10 minutes, 5-min buffers

Booking

overridden on

Schedule

HealthcareService

\*New Patient Visit\*

Schedule

\*Dr. Chen's Schedule\*

HealthcareService

\*Follow-up Visit\*

PractitionerRole

\*Dr. Chen\*

Slot

\*status: busy\*

new patient

Appointment

\*status: booked\*

### Example 3: Location-Specific Complex Surgical Scheduling [​](/content/docs/scheduling/defining-availability\#example-3-location-specific-complex-surgical-scheduling "Direct link to Example 3: Location-Specific Complex Surgical Scheduling"/index.html)

A bariatric surgery requiring surgeon, OR room, and anesthesiologist coordination.

HealthcareService: Bariatric Surgery

This HealthcareService defines scheduling for a 120-minute surgical procedure with 45/30-minute buffers.

```ts
{

resourceType: 'HealthcareService',

id: 'bariatric-surgery',

type: [\
\
    {\
\
      coding: [{ system: 'http://snomed.info/sct', code: '287809009', display: 'Bariatric Surgery' }],\
\
    },\
\
  ],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        { url: 'duration', valueDuration: { value: 120, unit: 'min' } },\
\
        { url: 'bufferBefore', valueDuration: { value: 45, unit: 'min' } },\
\
        { url: 'bufferAfter', valueDuration: { value: 30, unit: 'min' } },\
\
        { url: 'alignmentInterval', valueDuration: { value: 30, unit: 'min' } },\
\
      ],\
\
    },\
\
  ],

};
```

Schedule: Surgeon Availability

This Schedule shows Dr. Martinez's availability for bariatric surgeries, limited to Tuesday and Thursday mornings (8am-4pm).

```ts
{

resourceType: 'Schedule',

id: 'surgeon-martinez-schedule',

active: true,

comment: 'Dr. Maria Martinez (Surgeon) - Bariatric Surgery',

serviceType: [\
\
    {\
\
      coding: [{ system: 'http://snomed.info/sct', code: '287809009' }],\
\
      extension: [\
\
        {\
\
          url: 'https://medplum.com/fhir/service-type-reference',\
\
          valueReference: { reference: 'HealthcareService/bariatric-surgery' },\
\
        },\
\
      ],\
\
    },\
\
  ],

actor: [\
\
    {\
\
      reference: 'Practitioner/surgeon-martinez',\
\
      display: 'Dr. Maria Martinez - Bariatric Surgeon',\
\
    },\
\
  ],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        {\
\
          url: 'service',\
\
          valueReference: { reference: 'HealthcareService/bariatric-surgery' },\
\
        },\
\
        {\
\
          url: 'availability',\
\
          extension: [\
\
            {\
\
              url: 'availableTime',\
\
              extension: [\
\
                { url: 'daysOfWeek', valueCode: 'tue' },\
\
                { url: 'daysOfWeek', valueCode: 'thu' },\
\
                { url: 'availableStartTime', valueTime: '08:00:00' },\
\
                { url: 'availableEndTime', valueTime: '16:00:00' },\
\
              ],\
\
            },\
\
          ],\
\
        },\
\
      ],\
\
    },\
\
  ],

};
```

Schedule: Operating Room Availability

This Schedule shows Operating Room 3's availability for surgical procedures, available weekdays 7am-7pm with extended 12-hour blocks.

```ts
{

resourceType: 'Schedule',

id: 'or-3-schedule',

active: true,

comment: 'Operating Room 3 - Bariatric Surgery',

actor: [\
\
    {\
\
      reference: 'Location/or-3',\
\
      display: 'Operating Room 3',\
\
    },\
\
  ],

extension: [\
\
    {\
\
      url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
      extension: [\
\
        {\
\
          url: 'service',\
\
          valueReference: { reference: 'HealthcareService/bariatric-surgery' },\
\
        },\
\
        {\
\
          url: 'availability',\
\
          extension: [\
\
            {\
\
              url: 'availableTime',\
\
              extension: [\
\
                { url: 'daysOfWeek', valueCode: 'mon' },\
\
                { url: 'daysOfWeek', valueCode: 'tue' },\
\
                { url: 'daysOfWeek', valueCode: 'wed' },\
\
                { url: 'daysOfWeek', valueCode: 'thu' },\
\
                { url: 'daysOfWeek', valueCode: 'fri' },\
\
                { url: 'availableStartTime', valueTime: '07:00:00' },\
\
                { url: 'availableEndTime', valueTime: '19:00:00' },\
\
              ],\
\
            },\
\
          ],\
\
        },\
\
      ],\
\
    },\
\
  ],

};
```

Schedule: Anesthesiologist Availability

This Schedule shows Dr. Kim's availability for surgical procedures, covering weekdays 7am-5pm (10-hour blocks).

```ts
{

resourceType: 'Schedule',

id: 'anesthesiologist-kim-schedule',

active: true,

comment: 'Dr. James Kim (Anesthesiologist) - Bariatric Surgery',

actor: [\
\
    {\
\
      reference: 'Practitioner/anesthesiologist-kim',\
\
      display: 'Dr. James Kim - Anesthesiologist',\
\
    },\
\
  ],

};
```

**Result**: When booking a bariatric surgery, the system queries all three schedules, calculates the intersection of availability, and creates atomic transaction bundles to book all required resources simultaneously.

Transaction Bundle

HealthcareService

\*Defaults for

Bariatric Surgery\*

Schedule

\*Operating Room 3's

Schedule\*

Schedule

\*Surgeon Martinez's

Schedule\*

Schedule

\*Anesthesiologist Kim's

Schedule\*

Location

\*Operating Room 3\*

Practitioner

\*Surgeon Martinez\*

Practitioner

\*Anesthesiologist Kim\*

Slot

\*status: busy\*

Slot

\*status: busy\*

Slot

\*status: busy\*

Appointment 1

\*status: booked\*

### Example 4: Canonical Seed Bundle — One Service, Multiple Practitioners and Rooms [​](/content/docs/scheduling/defining-availability\#example-4-canonical-seed-bundle--one-service-multiple-practitioners-and-rooms "Direct link to Example 4: Canonical Seed Bundle — One Service, Multiple Practitioners and Rooms"/index.html)

This is a complete, uploadable seed bundle for the common case of a single service (here, a "Surgical Procedure") that is bookable across multiple practitioners and rooms. It is the recommended starting point for configuring a multi-schedule setup.

It demonstrates the patterns from [Common Pitfalls](/content/docs/scheduling/defining-availability#common-pitfalls/index.html):

- **`type: transaction`** so a single bad entry rolls back the whole bundle (see [FHIR Batch Requests](/content/docs/fhir-datastore/fhir-batch-requests/index.html)).
- **`urn:uuid``fullUrl`s** so resources can cross-reference each other before they have server-assigned IDs.
- **`comment`, not `name`,** on each `Schedule`.
- **`Practitioner`/`Location` actors each carrying a timezone extension.**
- **`ifNoneExist`** on the `Organization` and `HealthcareService` so re-running the seed is idempotent for those resources.

Each `Schedule` sets only its own `availability` and inherits `duration`, buffers, and alignment from the `HealthcareService`. Add more practitioners or rooms by duplicating the Practitioner/Location + Schedule pair.

note

The service code below uses a placeholder `http://example.org/appointment-types` system. Replace it with a real SNOMED CT or CPT code before using this in production.

```ts
{

resourceType: 'Bundle',

// type: transaction so a bad entry rolls back instead of silently partial-committing

type: 'transaction',

entry: [\
\
    {\
\
      fullUrl: 'urn:uuid:caae036f-8872-4d5c-ac2b-bbb37df5f08e',\
\
      resource: {\
\
        resourceType: 'Organization',\
\
        name: 'Bayview Surgery Center',\
\
        identifier: [{ system: 'http://example.org/organizations', value: 'bayview-surgery-center' }],\
\
      },\
\
      request: {\
\
        method: 'POST',\
\
        url: 'Organization',\
\
        // ifNoneExist makes re-running the seed idempotent (conditional create)\
\
        ifNoneExist: 'identifier=http://example.org/organizations|bayview-surgery-center',\
\
      },\
\
    },\
\
    {\
\
      fullUrl: 'urn:uuid:fe101ba5-44f9-45e9-803c-201605a5dde3',\
\
      resource: {\
\
        resourceType: 'HealthcareService',\
\
        providedBy: { reference: 'urn:uuid:caae036f-8872-4d5c-ac2b-bbb37df5f08e', display: 'Bayview Surgery Center' },\
\
        name: 'Surgical Procedure',\
\
        type: [\
\
          {\
\
            text: 'Surgical Procedure',\
\
            coding: [\
\
              {\
\
                system: 'http://example.org/appointment-types',\
\
                code: 'surgical-procedure',\
\
                display: 'Surgical Procedure',\
\
              },\
\
            ],\
\
          },\
\
        ],\
\
        extension: [\
\
          {\
\
            url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
            // set duration / alignmentInterval / alignmentTimezone HERE; all schedules booked together must match on these\
\
            extension: [\
\
              {\
\
                url: 'duration',\
\
                valueDuration: { value: 120, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
              },\
\
              {\
\
                url: 'bufferBefore',\
\
                valueDuration: { value: 30, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
              },\
\
              {\
\
                url: 'bufferAfter',\
\
                valueDuration: { value: 30, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
              },\
\
              {\
\
                url: 'alignmentInterval',\
\
                valueDuration: { value: 30, unit: 'min', system: 'http://unitsofmeasure.org', code: 'min' },\
\
              },\
\
              { url: 'alignmentTimezone', valueCode: 'America/Los_Angeles' },\
\
            ],\
\
          },\
\
        ],\
\
        identifier: [{ system: 'http://example.org/serviceTypes', value: 'surgical-procedure' }],\
\
      },\
\
      request: {\
\
        method: 'POST',\
\
        url: 'HealthcareService',\
\
        ifNoneExist: 'identifier=http://example.org/serviceTypes|surgical-procedure',\
\
      },\
\
    },\
\
    {\
\
      fullUrl: 'urn:uuid:39ff8f6a-5557-496a-b5cd-05f61899d223',\
\
      resource: {\
\
        resourceType: 'Location',\
\
        name: 'OR-1',\
\
        mode: 'instance',\
\
        extension: [{ url: 'http://hl7.org/fhir/StructureDefinition/timezone', valueCode: 'America/Los_Angeles' }],\
\
      },\
\
      request: { method: 'POST', url: 'Location' },\
\
    },\
\
    {\
\
      fullUrl: 'urn:uuid:a152fbf8-aef5-450f-9868-0819bb06a1cd',\
\
      resource: {\
\
        resourceType: 'Practitioner',\
\
        name: [{ given: ['Maria'], family: 'Martinez', prefix: ['Dr.'] }],\
\
        extension: [{ url: 'http://hl7.org/fhir/StructureDefinition/timezone', valueCode: 'America/Los_Angeles' }],\
\
      },\
\
      request: { method: 'POST', url: 'Practitioner' },\
\
    },\
\
    {\
\
      resource: {\
\
        resourceType: 'Schedule',\
\
        active: true,\
\
        // Schedule has no 'name' field in R4 - use comment for a label\
\
        comment: 'Dr. Martinez (Surgeon) - Surgical Procedure',\
\
        actor: [{ reference: 'urn:uuid:a152fbf8-aef5-450f-9868-0819bb06a1cd', display: 'Dr. Maria Martinez' }],\
\
        serviceType: [\
\
          {\
\
            text: 'Surgical Procedure',\
\
            coding: [{ system: 'http://example.org/appointment-types', code: 'surgical-procedure' }],\
\
            extension: [\
\
              {\
\
                url: 'https://medplum.com/fhir/service-type-reference',\
\
                valueReference: {\
\
                  reference: 'urn:uuid:fe101ba5-44f9-45e9-803c-201605a5dde3',\
\
                  display: 'Surgical Procedure',\
\
                },\
\
              },\
\
            ],\
\
          },\
\
        ],\
\
        extension: [\
\
          {\
\
            url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
            extension: [\
\
              {\
\
                url: 'service',\
\
                valueReference: {\
\
                  reference: 'urn:uuid:fe101ba5-44f9-45e9-803c-201605a5dde3',\
\
                  display: 'Surgical Procedure',\
\
                },\
\
              },\
\
              {\
\
                url: 'availability',\
\
                extension: [\
\
                  {\
\
                    url: 'availableTime',\
\
                    extension: [\
\
                      { url: 'daysOfWeek', valueCode: 'tue' },\
\
                      { url: 'daysOfWeek', valueCode: 'thu' },\
\
                      { url: 'availableStartTime', valueTime: '08:00:00' },\
\
                      { url: 'availableEndTime', valueTime: '16:00:00' },\
\
                    ],\
\
                  },\
\
                ],\
\
              },\
\
            ],\
\
          },\
\
        ],\
\
      },\
\
      request: { method: 'POST', url: 'Schedule' },\
\
    },\
\
    {\
\
      resource: {\
\
        resourceType: 'Schedule',\
\
        active: true,\
\
        comment: 'OR-1 - Surgical Procedure',\
\
        actor: [{ reference: 'urn:uuid:39ff8f6a-5557-496a-b5cd-05f61899d223', display: 'OR-1' }],\
\
        serviceType: [\
\
          {\
\
            text: 'Surgical Procedure',\
\
            coding: [{ system: 'http://example.org/appointment-types', code: 'surgical-procedure' }],\
\
            extension: [\
\
              {\
\
                url: 'https://medplum.com/fhir/service-type-reference',\
\
                valueReference: {\
\
                  reference: 'urn:uuid:fe101ba5-44f9-45e9-803c-201605a5dde3',\
\
                  display: 'Surgical Procedure',\
\
                },\
\
              },\
\
            ],\
\
          },\
\
        ],\
\
        extension: [\
\
          {\
\
            url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',\
\
            extension: [\
\
              {\
\
                url: 'service',\
\
                valueReference: {\
\
                  reference: 'urn:uuid:fe101ba5-44f9-45e9-803c-201605a5dde3',\
\
                  display: 'Surgical Procedure',\
\
                },\
\
              },\
\
              {\
\
                url: 'availability',\
\
                extension: [\
\
                  {\
\
                    url: 'availableTime',\
\
                    extension: [\
\
                      { url: 'daysOfWeek', valueCode: 'mon' },\
\
                      { url: 'daysOfWeek', valueCode: 'tue' },\
\
                      { url: 'daysOfWeek', valueCode: 'wed' },\
\
                      { url: 'daysOfWeek', valueCode: 'thu' },\
\
                      { url: 'daysOfWeek', valueCode: 'fri' },\
\
                      { url: 'availableStartTime', valueTime: '07:00:00' },\
\
                      { url: 'availableEndTime', valueTime: '19:00:00' },\
\
                    ],\
\
                  },\
\
                ],\
\
              },\
\
            ],\
\
          },\
\
        ],\
\
      },\
\
      request: { method: 'POST', url: 'Schedule' },\
\
    },\
\
  ],

};
```

## Location Modeling [​](/content/docs/scheduling/defining-availability\#location-modeling "Direct link to Location Modeling"/index.html)

### Location Hierarchy Pattern [​](/content/docs/scheduling/defining-availability\#location-hierarchy-pattern "Direct link to Location Hierarchy Pattern"/index.html)

```text
Organization (Surgery Center)

└─ Location (Building) [mode=kind]

├─ Location (Operating Rooms) [mode=kind, type=OR]

│    ├─ Location (OR-1) [mode=instance]

│    ├─ Location (OR-2) [mode=instance]

│    └─ Location (OR-3) [mode=instance]

└─ Location (Recovery Rooms) [mode=kind, type=RR]

├─ Location (Recovery-A) [mode=instance]

└─ Location (Recovery-B) [mode=instance]
```

### Specific vs. "Any Available" Room [​](/content/docs/scheduling/defining-availability\#specific-vs-any-available-room "Direct link to Specific vs. \"Any Available\" Room"/index.html)

**Specific room required:**

- Query `Schedule?actor=Location/or-3`

**Any OR room acceptable:**

- Query: `Schedule?actor:Location.partof:Location.type=OR`

Location

\*Operating Rooms\*

mode=kind, type=OR

Location

\*OR-1\*

mode=instance

Location

\*OR-2\*

mode=instance

Location

\*OR-3\*

mode=instance

Schedule

\*OR-1 Schedule\*

Schedule

\*OR-2 Schedule\*

Schedule

\*OR-3 Schedule\*

## Best Practices [​](/content/docs/scheduling/defining-availability\#best-practices "Direct link to Best Practices"/index.html)

#### 1\. Set Defaults on HealthcareService, Override Only What Differs on Schedule [​](/content/docs/scheduling/defining-availability\#1-set-defaults-on-healthcareservice-override-only-what-differs-on-schedule "Direct link to 1. Set Defaults on HealthcareService, Override Only What Differs on Schedule"/index.html)

Define `duration`, buffers, and alignment once on the HealthcareService. Only add a `SchedulingParameters` extension to a Schedule when that actor's availability or parameters differ from the service defaults. Omit any field that should be inherited.

#### 2\. Minimize Pre-Generated Slots [​](/content/docs/scheduling/defining-availability\#2-minimize-pre-generated-slots "Direct link to 2. Minimize Pre-Generated Slots"/index.html)

Only create Slot resources for:

- Booked appointments (status: busy)
- Blocked time (status: busy-unavailable)

Let `$find` calculate available windows dynamically.

## Common Pitfalls [​](/content/docs/scheduling/defining-availability\#common-pitfalls "Direct link to Common Pitfalls"/index.html)

A few constraints trip people up most often when configuring availability:

- **`Schedule` has no `name` element** in FHIR R4. Use `comment` for a human-readable label — sending `name` fails validation with `Invalid additional property "name"`. See [The Schedule Resource](/content/docs/scheduling/defining-availability#the-schedule-resource/index.html).
- **A Schedule's parameters override, they don't merge.** Setting `availability` on a Schedule fully replaces the service's `availableTime` rather than narrowing it. See [Override Behavior](/content/docs/scheduling/defining-availability#override-behavior/index.html).
- **To book across multiple schedules at once**, `duration`, `alignmentInterval`, `alignmentOffset`, and `alignmentTimezone` must match across them. Prefer setting these only on the `HealthcareService`.

## Beta Status [​](/content/docs/scheduling/defining-availability\#beta-status "Direct link to Beta Status"/index.html)

The Scheduling API is under active development. This [beta](/content/docs/compliance/alpha-beta/index.html) release of the scheduling API is expected to gain additional capabilities.

- `bookingLimit` \- An upcoming scheduling parameter that will allow you to express how often a given service type may be added to a schedule. This is not yet implemented.
- Editing availability on a calendar view, by dragging hours across days rather than picking times, is a possible future enhancement to the example app. The `ScheduleAvailabilityEditor` component covers this today with time pickers.

Major changes to these APIs during the beta period are tracked in the [beta changelog](/content/docs/scheduling/beta-changelog/index.html).

- [The Scheduling Parameters Extension](/content/docs/scheduling/defining-availability#the-scheduling-parameters-extension/index.html)
  - [Alignment grid](/content/docs/scheduling/defining-availability#alignment-grid/index.html)
- [Actor Level Availability](/content/docs/scheduling/defining-availability#actor-level-availability/index.html)
  - [The Concept of Implicit Availability](/content/docs/scheduling/defining-availability#the-concept-of-implicit-availability/index.html)
  - [The Schedule Resource](/content/docs/scheduling/defining-availability#the-schedule-resource/index.html)
  - [`availability` Extension](/content/docs/scheduling/defining-availability#availability-extension/index.html)
- [Service Level Availability](/content/docs/scheduling/defining-availability#service-level-availability/index.html)
  - [Service Types and HealthcareService](/content/docs/scheduling/defining-availability#service-types-and-healthcareservice/index.html)
  - [Override Behavior](/content/docs/scheduling/defining-availability#override-behavior/index.html)
  - [Blocking Time by Service Type](/content/docs/scheduling/defining-availability#blocking-time-by-service-type/index.html)
- [Timezone Resolution](/content/docs/scheduling/defining-availability#timezone-resolution/index.html)
  - [Timezone per Scheduling Parameters Entry](/content/docs/scheduling/defining-availability#timezone-per-scheduling-parameters-entry/index.html)
- [Editing Availability in a React App](/content/docs/scheduling/defining-availability#editing-availability-in-a-react-app/index.html)
- [Examples](/content/docs/scheduling/defining-availability#examples/index.html)
  - [Example 1: Simple Primary Care Office with Appointment Type Defaults](/content/docs/scheduling/defining-availability#example-1-simple-primary-care-office-with-appointment-type-defaults/index.html)
  - [Example 2: Multi-Service Provider with Multiple Appointment Types and Overrides](/content/docs/scheduling/defining-availability#example-2-multi-service-provider-with-multiple-appointment-types-and-overrides/index.html)
  - [Example 3: Location-Specific Complex Surgical Scheduling](/content/docs/scheduling/defining-availability#example-3-location-specific-complex-surgical-scheduling/index.html)
  - [Example 4: Canonical Seed Bundle — One Service, Multiple Practitioners and Rooms](/content/docs/scheduling/defining-availability#example-4-canonical-seed-bundle--one-service-multiple-practitioners-and-rooms/index.html)
- [Location Modeling](/content/docs/scheduling/defining-availability#location-modeling/index.html)
  - [Location Hierarchy Pattern](/content/docs/scheduling/defining-availability#location-hierarchy-pattern/index.html)
  - [Specific vs. "Any Available" Room](/content/docs/scheduling/defining-availability#specific-vs-any-available-room/index.html)
- [Best Practices](/content/docs/scheduling/defining-availability#best-practices/index.html)
- [Common Pitfalls](/content/docs/scheduling/defining-availability#common-pitfalls/index.html)
- [Beta Status](/content/docs/scheduling/defining-availability#beta-status/index.html)
