## Beta

Medplum Scheduling APIs are currently in [beta](/content/docs/compliance/alpha-beta/index.html). While in beta, we may make backwards-incompatible changes to fix bugs or align behavior with the FHIR spec. This page is a running log of those changes, newest first, along with the migration steps (if any) needed to adopt them.

## `Slot.serviceType` is now respected on busy Slots [​](/content/docs/scheduling/beta-changelog#slotservicetype-is-now-respected-on-busy-slots/index.html)

**Target release:** September 1, 2026  ·  **Issues:** [#9995](https://github.com/medplum/medplum/issues/9995), [#9998](https://github.com/medplum/medplum/pull/9998)

### What changed [​](/content/docs/scheduling/beta-changelog#what-changed/index.html)

Previously, [`$find`](/content/docs/scheduling/appointment-find/index.html) and [`$book`](/content/docs/scheduling/appointment-book/index.html) only applied the `serviceType` filter to **free** Slots. Every `busy` / `busy-unavailable` / `busy-tentative` Slot was subtracted from availability regardless of its `serviceType`, so a busy Slot scoped to one service actually blocked the **entire** Schedule.

This contradicted the [documented behavior](/content/docs/scheduling/defining-availability#blocking-time-by-service-type/index.html):

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

Starting after September 1, the `serviceType` filter is applied to busy Slots as well:

- A busy Slot **without** a `serviceType` still blocks all services (unchanged).
- A busy Slot **with** a `serviceType` now blocks **only** the matching service(s).

Thank you to [Nick Catalano](https://github.com/nickcatal) for reporting the issue.

### Who is affected [​](/content/docs/scheduling/beta-changelog#who-is-affected/index.html)

Users of `$find` / `$book` / `$hold` before Medplum Server v5.1.28 will have had `Slot` resources created with a `serviceType` attribute.

These slots were intended to block their related Schedule availability for all services ("wildcard" service type matching), but before [#9998](https://github.com/medplum/medplum/pull/9998) the slots were being created with `serviceType` set to the type of the linked Appointment resource.

While the bug above existed, that stray `serviceType` was ignored, so those Slots still blocked everything and no harm was done.

Once this fix lands, those Slots will block only their single service — leaving the provider bookable for other services at the same time and allowing double-booking. If you used `$find` / `$book` before #9998 shipped, you likely have such Slots and should run the migration below.

You are also affected if you authored `busy` / `busy-unavailable` / `busy-tentative` Slots with a `serviceType` and relied on them blocking every service. If you never set `serviceType` on busy Slots, or you always intended a busy Slot to block only its referenced service, no action is needed.

### Migration [​](/content/docs/scheduling/beta-changelog#migration/index.html)

To preserve the old "block all services" behavior, clear `serviceType` on the affected busy Slots before upgrading. Since blocked time in the past no longer matters, you only need to migrate Slots ending after your upgrade date.

The script below finds every `busy` / `busy-unavailable` / `busy-tentative` Slot with a `serviceType` ending after `TARGET_DATE` and removes the `serviceType` field, so those Slots continue to block all services.

```ts
// Migration for medplum/medplum#9995 (and #9998): clear `serviceType` on busy /

// busy-unavailable Slots ending after a target date, so they keep blocking

// ALL services (the pre-fix behavior) instead of only the matching service.
// Chiefly cleans up Slots that $find/$book wrote a serviceType onto before
// #9998, which would otherwise allow double-booking once #9995's fix lands.
//
//   export MEDPLUM_BASE_URL='https://api.medplum.com/'
//   export MEDPLUM_CLIENT_ID='...'
//   export MEDPLUM_CLIENT_SECRET='...'
//   export TARGET_DATE='2026-09-01T00:00:00Z'   # your upgrade date
//
//   npx tsx clear-busy-slot-service-type.ts
//
// Dependencies: npm i @medplum/core tsx

import { MedplumClient } from '@medplum/core';

async function main(): Promise<void> {
  const targetDate = new Date(process.env.TARGET_DATE as string);
  const medplum = new MedplumClient({ baseUrl: process.env.MEDPLUM_BASE_URL });
  await medplum.startClientLogin(process.env.MEDPLUM_CLIENT_ID as string, process.env.MEDPLUM_CLIENT_SECRET as string);

// Phase 1: gather affected slot IDs (fully drained before we mutate anything).
  // `service-type:missing=false` limits the scan to slots that carry a serviceType;
  // `end=gt...` uses Medplum's custom Slot end search parameter.
  const ids: string[] = [];

for await (const page of medplum.searchResourcePages('Slot', {
    status: 'busy,busy-unavailable,busy-tentative',
    'service-type:missing': 'false',
    end: `gt${targetDate.toISOString()}`,
    _count: 1000,
  })) {
    for (const slot of page) {
      if (slot.id) {
        ids.push(slot.id);
      }
    }
  }

console.log(`Found ${ids.length} slot(s) to update.`);

// Phase 2: clear serviceType on each with a targeted JSON Patch (no re-read).
  for (const id of ids) {
    await medplum.patchResource('Slot', id, [{ op: 'remove', path: '/serviceType' }]);
    console.log(`Updated Slot/${id}`);
  }
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
```

- [`Slot.serviceType` is now respected on busy Slots](/content/docs/scheduling/beta-changelog#slotservicetype-is-now-respected-on-busy-slots/index.html)
  - [What changed](/content/docs/scheduling/beta-changelog#what-changed/index.html)
  - [Who is affected](/content/docs/scheduling/beta-changelog#who-is-affected/index.html)
  - [Migration](/content/docs/scheduling/beta-changelog#migration/index.html)
