On this page

Beta

The `$find` operation is currently in [beta](/content/docs/compliance/alpha-beta/index.html).

The `$find` operation takes a list of [`Schedule`](/content/docs/api/fhir/resources/schedule/index.html) references and a [`HealthcareService`](/content/docs/api/fhir/resources/healthcareservice/index.html) reference and returns a bundle of proposed [`Appointment`](/content/docs/api/fhir/resources/appointment/index.html) resources within a specified time range. Slots are computed dynamically from each [`SchedulingParameters`](/content/docs/scheduling/defining-availability/index.html) extensions on each Schedule and HealthcareService — no Slots need to be pre-generated.

## Use Cases

- **Patient-facing booking flows**: Show a patient the available time windows for a given provider or location
- **Availability checks**: Determine whether a provider has open time before attempting to book
- **Multi-provider scheduling**: Query multiple Schedules and intersect results to find shared availability

## Invoke the `$find` operation

```text
[base]/R4/Appointment/$find
```

- TypeScript
- cURL

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

import type { Bundle, Appointment } from '@medplum/fhirtypes';

const medplum = new MedplumClient();

const url = medplum.fhirUrl('Appointment', '$find');

url.searchParams.append('start', '2026-03-10T09:00:00-05:00')

url.searchParams.append('end', '2026-03-10T17:00:00-05:00');

url.searchParams.append('service-type-reference', 'HealthcareService/my-healthcareservice-id')

url.searchParams.append('schedule', 'Schedule/my-schedule-id')

const bundle = await medplum.get<Bundle<Appointment>>(url);

const appointments = bundle.entry?.map((e) => e.resource as Appointment) ?? [];
```

```bash
curl -G 'https://api.medplum.com/fhir/R4/Appointment/$find' \
  -H "Authorization: Bearer MY_ACCESS_TOKEN" \
  --data-urlencode "start=2026-03-10T09:00:00-05:00" \
  --data-urlencode "end=2026-03-10T17:00:00-05:00" \
  --data-urlencode "service-type-reference=HealthcareService/my-healthcareservice-id" \
  --data-urlencode "schedule=Schedule/my-schedule-id"
```

## Parameters

| Name | Type | Description | Required |
| --- | --- | --- | --- |
| `start` | `dateTime` | Start of the search window (inclusive) | Yes |
| `end` | `dateTime` | End of the search window (inclusive) | Yes |
| `service-type-reference` | `reference(HealthcareService)` | The HealthcareService describing the type of appointment to be scheduled. | Yes |
| `schedule` | `reference(Schedule)` | A schedule to check for availability. May be passed multiple times with different schedules. | Yes |
| `_count` | `integer` | Maximum number of Appointment resources to return. Defaults to 20. Maximum is 1000. | No |

### Constraints

- `start` must be before `end`
- The search window cannot exceed **31 days**
- At least one schedule must be provided
- Each schedule must have exactly **one actor** reference
- Each schedule's `serviceType` field must match the requested HealthcareService.type
- Each schedule's actor (Practitioner, Location, or Device) must have a timezone defined via the `http://hl7.org/fhir/StructureDefinition/timezone` extension

## Output

Returns a [`Parameters`](/content/docs/api/fhir/resources/parameters/index.html) resource wrapping a `Bundle` of `Appointment` resources with `status: proposed`.

The Appointments are virtual — they are not persisted in the FHIR store. Each Appointment has a `contained` attribute holding virtual (not persisted) Slot resources. These contained resources represent Slot resources that will be created if this Appointment is booked.

### Example Response

```json
{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "return",
      "resource": {
        "resourceType": "Bundle",
        "type": "searchset",
        "entry": [
          {
            "resource": {
              "resourceType": "Appointment",
              "status": "proposed",
              "start": "2026-03-10T09:00:00.000Z",
              "end": "2026-03-10T10:00:00.000Z",
              "participant": [
                {
                  "actor": { "reference": "Practitioner/my-practitioner-id" },
                  "required": "required",
                  "status": "needs-action"
                }
              ],
              "serviceType": [
                {
                  "text": "Office Visit",
                  "coding": [{ "system": "http://example.org/appointment-types", "code": "office-visit" }]
                }
              ],
              "contained": [
                {
                  "resourceType": "Slot",
                  "status": "busy",
                  "start": "2026-03-10T09:00:00.000Z",
                  "end": "2026-03-10T10:00:00.000Z",
                  "serviceType": [
                    {
                      "text": "Office Visit",
                      "coding": [{ "system": "http://example.org/appointment-types", "code": "office-visit" }]
                    }
                  ],
                  "schedule": { "reference": "Schedule/my-schedule-id" }
                }
              ]
            }
          },
          {
            "resource": {
              "resourceType": "Appointment",
              "status": "proposed",
              "start": "2026-03-10T10:00:00.000Z",
              "end": "2026-03-10T11:00:00.000Z",
              "participant": [
                {
                  "actor": { "reference": "Practitioner/my-practitioner-id" },
                  "required": "required",
                  "status": "needs-action"
                }
              ],
              "serviceType": [
                {
                  "text": "Office Visit",
                  "coding": [{ "system": "http://example.org/appointment-types", "code": "office-visit" }]
                }
              ],
              "contained": [
                {
                  "resourceType": "Slot",
                  "status": "busy",
                  "start": "2026-03-10T10:00:00.000Z",
                  "end": "2026-03-10T11:00:00.000Z",
                  "serviceType": [
                    {
                      "text": "Office Visit",
                      "coding": [{ "system": "http://example.org/appointment-types", "code": "office-visit" }]
                    }
                  ],
                  "schedule": { "reference": "Schedule/my-schedule-id" }
                }
              ]
            }
          }
        ]
      }
    }
  ]
}
```

## Availability Logic

`$find` calculates available windows by:

1. Reading each Schedule's `SchedulingParameters` extension to determine recurring availability windows, slot duration, buffer times, and alignment constraints
2. Fetching existing Slot resources for each Schedule in the requested range (busy, busy-tentative, busy-unavailable, and free slots)
3. Adding time for existing Slot resources with status `free`
4. Subtracting occupied time for existing Slot resources with status `busy`, `busy-tentative`, or `busy-unavailable`.
5. Applying alignment intervals and offsets to produce valid start times
6. Returning Appointments up to `_count`

See [Defining Availability](/content/docs/scheduling/defining-availability/index.html) for full details on how `SchedulingParameters` are configured.

## Error Responses

### Invalid Time Range

```json
{
  "resourceType": "OperationOutcome",
  "issue": [{ "severity": "error", "code": "invalid", "details": { "text": "Invalid search time range" }}]
}
```

### Range Exceeds 31 Days

```json
{
  "resourceType": "OperationOutcome",
  "issue": [{ "severity": "error", "code": "invalid", "details": { "text": "Search range cannot exceed 31 days" }}]
}
```

### Actor Missing Timezone

```json
{
  "resourceType": "OperationOutcome",
  "issue": [{ "severity": "error", "code": "invalid", "details": { "text": "No timezone specified" }}]
}
```

### Schedule Has Multiple Actors

```json
{
  "resourceType": "OperationOutcome",
  "issue": [{ "severity": "error", "code": "invalid", "details": { "text": "$find only supported on schedules with exactly one actor" }}]
}
```

### `Schedule.serviceType` does not match `HealthcareService.type`

```json
{
  "resourceType": "OperationOutcome",
  "issue": [{ "severity": "error", "code": "invalid", "details": { "text": "Schedule is not schedulable for requested service type" }}]
}
```

## Beta Status

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 enforced in `$find`.
