# Schedule Openings V1 — Developer Guide

> **Reference:** [ScheduleOpenings (V1) Endpoint Documentation](https://papidocs.hs1api.com/publicapi/api-appointment-management/scheduleopenings-(v1))


## GET — Bulk Schedule Openings

This endpoint returns a list of available schedule openings for a location based on filter criteria. It calculates open time slots for providers and operatories within the specified date range.

> **⚠️ Prerequisite:** Provider Hours **must** be configured in the Dentrix Ascend before calling this endpoint. Without Provider Hours set up, the endpoint will return no results.


### Required Filters

The following filters **must** be included on every request:

| Filter | Supported Operators | Description |
|  --- | --- | --- |
| `location.id` | `==` | The location to retrieve schedule openings for |
| `startDate` | `==` | Start of the date range (format: `YYYY-MM-DD`) |
| `endDate` | `==` | End of the date range (format: `YYYY-MM-DD`) |


### Optional Filters

Narrow your results further with any of the following:

| Filter | Supported Operators | Description |
|  --- | --- | --- |
| `operatory.id` | `==`, `->` | Filter by a single operatory or a list of operatory IDs |
| `provider.id` | `==`, `->` | Filter by a single provider or a list of provider IDs |
| `minOpeningLength` | `==` | Minimum slot length in minutes. Valid range: `5`–`1440` |
| `booking.type` | `==`, `->` | Filter by booking type. Valid values: `TREATMENT`, `RECARE`, `NEW_PATIENT`, `EXISTING_PATIENT` |
| `reason.id` | `==`, `->` | IDs of **schedule template reasons** for the location. See [Quick reference: `reason.id`](#quick-reference-reasonid-and-scheduletemplatereasons). |


> See the endpoint reference for the full up-to-date list of supported parameters.


### Quick reference: `reason.id` and scheduletemplatereasons

The **`reason.id`** filter uses the same identifiers as resources from **GET /v1/scheduletemplatereasons** ([ScheduleTemplateReasons (V1)](https://papidocs.hs1api.com/publicapi/api-appointment-management/scheduletemplatereasons-(v1))). Call **scheduletemplatereasons** with `filter=location.id==…` to list those reasons and their **`id`** values, then pass those values into **scheduleopenings** as **`reason.id==…`** or **`reason.id->[…]`**.

### Important Notes

> **⚠️ Rate Limited:** This endpoint is rate limited. See the [rate limiting guide](https://papidocs.hs1api.com/publicapi/api-consumer-guide#rate-limiting) for details.


> **✅ Tip:** Use `operatory.id` and `provider.id` filters together to narrow results to a specific chair/provider combination and reduce response size.


### Example Request

```
GET /api/v1/scheduleopenings?filter=location.id==123,startDate==2025-06-01,endDate==2025-06-07
```

With optional filters:

```
GET /api/v1/scheduleopenings?filter=location.id==123,startDate==2025-06-01,endDate==2025-06-07,provider.id->[789,790],minOpeningLength==30,booking.type==TREATMENT
```

With **`reason.id`** (IDs from **GET /v1/scheduletemplatereasons** for the same location):

```
GET /api/v1/scheduleopenings?filter=location.id==123,startDate==2025-06-01,endDate==2025-06-07,reason.id->[101,102]
```

## Response Structure

A successful `200` response returns a `ScheduleOpeningsV1` object:

| Field | Type | Description |
|  --- | --- | --- |
| `data` | array | Array of `ScheduleOpeningsV1` objects |
| `warnings` | array | Non-fatal warnings (operation still succeeded) |
| `errors` | array | Field-level or structural errors |


### `ScheduleOpeningsV1` Object

Each item in `data` represents the available slots for a provider/operatory pair on a given day:

| Field | Type | Description |
|  --- | --- | --- |
| `provider` | object | Linked model reference to the provider |
| `operatory` | object | Linked model reference to the operatory |
| `day` | string | The calendar date for these openings (format: `YYYY-MM-DD`, e.g. `2025-06-01`) |
| `slots` | array | Array of available time slots for this provider/operatory/day combination |


### `slots` Array Items

Each slot within `slots` has the following structure:

| Field | Type | Description |
|  --- | --- | --- |
| `start` | string | Slot start time (ISO 8601 datetime, e.g. `2025-06-01T09:00:00.000Z`) |
| `end` | string | Slot end time (ISO 8601 datetime, e.g. `2025-06-01T09:30:00.000Z`) |
| `scheduleTemplateBookingType` | array | Linked model references to the booking types assigned to this slot |
| `scheduleTemplateReason` | array | Linked model references to the schedule template reasons for this slot |


## Errors & Warnings Reference

details
summary
strong
HTTP Status Code Errors
— click to expand
These apply to all Schedule Openings V1 endpoints.

#### `400` — Bad Request

The request query parameters are invalid or cannot be processed. See the [Field-Level Validation Errors](#field-level-validation-errors) section below for specific causes.

#### `401` — Unauthorized

|  |  |
|  --- | --- |
| **Message** | Authentication is required and has failed or has not been provided. |
| **Why** | No valid auth token was sent, the token is expired, or the token is malformed. |
| **Fix** | Provide a valid Bearer token in the `Authorization` header. |


#### `403` — Forbidden

|  |  |
|  --- | --- |
| **Message** | The request is understood, but it has been refused or access is not allowed. |
| **Why** | The authenticated token does not have the required scope. Read operations require `model:read`. |
| **Fix** | Request a token that includes the `model:read` scope. |


#### `404` — Not Found

|  |  |
|  --- | --- |
| **Message** | The requested resource is either missing or does not exist. |
| **Why** | No schedule openings exist for the given filter criteria within your organization, or a referenced ID (e.g. `location.id`, `operatory.id`, `provider.id`) does not exist or belong to your organization. |
| **Fix** | Confirm all filter IDs are correct and belong to your organization. |


#### `408` — Request Timeout

|  |  |
|  --- | --- |
| **Message** | The server timed out while processing the request. |
| **Why** | The server took too long to respond, often due to a large date range, complex query, or high server load. |
| **Fix** | Retry the request. If the issue persists, reduce the date range or simplify your filters, or contact support. |


#### `429` — Too Many Requests

|  |  |
|  --- | --- |
| **Message** | Rate limit exceeded. |
| **Why** | Your client has made too many requests in a short period of time. |
| **Fix** | Implement exponential backoff and retry logic. Reduce request frequency. See the [rate limiting guide](https://papidocs.hs1api.com/publicapi/api-consumer-guide#rate-limiting). |


#### `500` — Internal Server Error

|  |  |
|  --- | --- |
| **Message** | An unexpected error occurred. |
| **Why** | An unhandled server-side error occurred. |
| **Fix** | Retry the request. If the issue persists, contact support with the request details and timestamp. |


details
summary
strong
Field-Level Validation Errors
— click to collapse
These errors are returned in the `errors` array of the response body alongside a `400` status code.

### `location.id`

**Error:** `location.id` is required and must not be empty.

|  |  |
|  --- | --- |
| **Why** | The `location.id` filter is required on every request. It was omitted or left empty. |
| **Fix** | Include `location.id==<id>` in your `filter` query parameter, e.g. `filter=location.id==123,startDate==2025-06-01,endDate==2025-06-07`. |


### `startDate` / `endDate`

**Error:** `startDate` is required and must not be empty.

|  |  |
|  --- | --- |
| **Why** | The `startDate` filter is required. It was omitted or left empty. |
| **Fix** | Provide a valid date string in `YYYY-MM-DD` format, e.g. `startDate==2025-06-01`. |


**Error:** `endDate` is required and must not be empty.

|  |  |
|  --- | --- |
| **Why** | The `endDate` filter is required. It was omitted or left empty. |
| **Fix** | Provide a valid date string in `YYYY-MM-DD` format, e.g. `endDate==2025-06-07`. |


**Error:** `startDate` or `endDate` has an invalid date format.

|  |  |
|  --- | --- |
| **Why** | The date string was not provided in the expected `YYYY-MM-DD` format, or contains an invalid calendar date. |
| **Fix** | Use the `YYYY-MM-DD` format, e.g. `2025-06-01`. |


**Error:** `endDate` must not be before `startDate`.

|  |  |
|  --- | --- |
| **Why** | The `endDate` value is chronologically earlier than `startDate`. |
| **Fix** | Ensure `endDate` is on or after `startDate`. |


### `minOpeningLength`

**Error:** `minOpeningLength` must be an integer between `5` and `1440`.

|  |  |
|  --- | --- |
| **Why** | The `minOpeningLength` value (in minutes) is outside the allowed range. Values below `5` or above `1440` (24 hours) are rejected. |
| **Fix** | Provide a `minOpeningLength` value between `5` and `1440` inclusive. |


### `booking.type`

**Error:** Invalid enum value for `booking.type`.

|  |  |
|  --- | --- |
| **Why** | The value provided for `booking.type` is not a recognized enum. |
| **Fix** | Use one of the following valid values: `TREATMENT`, `RECARE`, `NEW_PATIENT`, `EXISTING_PATIENT`. |