Reference: Appointments (V1) Endpoint Documentation
This endpoint retrieves appointments belonging to locations within your organization.
Always narrow your query with at least one filter. The following filter keys are supported:
| Filter | Supported Operators |
|---|---|
status | ==, != |
patient.id | ==, != |
operatory.id | ==, !=, -> |
location.id | ==, !=, -> |
provider.id | ==, !=, -> |
asap | == |
start | >, <, >=, <= |
lastModified | >, <, >=, <= |
See the endpoint reference for the full up-to-date list of supported parameters.
⚠️ Upcoming Breaking Change:
location.id(as a list of IDs or["ALL"]) will become a required filter parameter. Omittinglocation.idcurrently defaults to all locations.
⚠️ Not Recommended: Do not paginate through large data sets using the
pageparameter, even atpageSize=500. This approach is slow and unreliable at scale.
✅ Recommended: Use
lastIdpagination instead. See the section below.
The lastId parameter is the most efficient way to page through large result sets. It uses the appointment's id as a cursor rather than an offset.
Send your first request without lastId:
GET /api/v1/appointments?pageSize=500&filter=lastModified>2025-02-16T00:00:00.000Z,location.id->[64000000000004]Take the id of the last record in the returned data array and pass it as lastId in the next request:
GET /api/v1/appointments?lastId=8000052360370&pageSize=500&filter=lastModified>2025-02-16T00:00:00.000Z,location.id->[64000000000004]Continue until the response returns an empty data array or a no-more-data indicator.
You can combine lastId with additional filters to narrow results further — for example, filtering by a specific date range, operatory, location, or provider.
For real-time changes — such as newly created or updated appointments on a live schedule — use the Streaming API instead of polling the Public API on a schedule. The Streaming API pushes changes as they happen and is far more efficient than repeated polling.
A successful 200 response returns a BulkAppointmentResponseWrapperV1 object:
| Field | Type | Description |
|---|---|---|
data | array | Array of AppointmentV1 objects |
warnings | array | Non-fatal warnings (operation still succeeded) |
errors | array | Field-level or structural errors |
meta | object | Pagination metadata — see meta.pagination below |
| Field | Type | Description |
|---|---|---|
lastId | integer | The id of the last record returned. Pass as lastId in the next request to continue pagination. |
Each item in data represents a single appointment:
| Field | Type | Access | Description |
|---|---|---|---|
id | integer | read-only | Unique appointment identifier |
start | string | read/write | Appointment start time (ISO 8601, e.g. 2024-05-09T12:00:00.000Z) |
end | string | read-only | Computed end time based on start + duration |
duration | integer | read/write | Length of the appointment in minutes. Valid range: 5–1440 |
status | string | read/write | Appointment status enum. See status field errors for valid values |
statusId | number | read-only | Numeric representation of status |
created | string | read-only | Timestamp when the appointment was created |
lastModified | string | read-only | Timestamp of the most recent update |
bookingType | string | read/write | Booking type enum: TREATMENT, RECARE, NEW_PATIENT, EXISTING_PATIENT |
needsFollowUp | boolean | read/write | Whether the patient requires a follow-up |
followedUpOn | string | read/write | Date follow-up was completed. Requires needsFollowUp: true |
note | string | read/write | Internal appointment note |
other | string | read/write | Free-text procedure description. Maximum 90 characters |
asap | boolean | read/write | Whether the appointment is flagged as ASAP |
bookedOnline | boolean | read/write | Whether the appointment was booked online |
needsPremedicate | boolean | read/write | Whether the patient requires premedication |
labCaseDentalLab | object | read/write | Linked model reference to the dental lab. Setting to null clears all lab case fields |
labCaseStatus | string | read/write | Lab case status: OUTSTANDING, RECEIVED |
labCaseDueDate | string | read/write | Lab case due date (must be on or before appointment end time) |
labCaseNote | string | read/write | Note associated with the lab case |
provider | object | read/write | Linked model reference to the primary provider |
otherProvider | object | read/write | Linked model reference to a secondary provider |
patient | object | read/write | Linked model reference to the patient |
operatory | object | read/write | Linked model reference to the operatory |
patientProcedures | array | read/write | Array of linked model references to patient procedures |
practiceProcedures | array | read/write | Array of linked model references to practice procedures |
visits | array | read/write | Array of linked model references to treatment plan visits |
timePattern | object | read-only | Breakdown of provider/assistant/chair time within the appointment |
confirmed | string | read-only | Timestamp when the appointment was confirmed |
leftMessage | string | read-only | Date a message was left for the patient |
HTTP Status Code Errors — click to expand
These apply to all Appointments V1 endpoints.
The request body or query parameters are invalid or cannot be processed. See the Field-Level Validation Errors section below for specific causes.
| 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. |
| 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; write operations (POST, PUT, DELETE) require model:write. |
| Fix | Request a token that includes the appropriate scope for the operation you are attempting. |
| Message | The requested resource is either missing or does not exist. |
| Why | No appointment exists for the given appointmentId within your organization, or the ID is not a valid integer (e.g. 0 or non-numeric). |
| Fix | Confirm the appointment ID is correct and belongs to your organization. |
| Message | The server timed out while processing the request. |
| Why | The server took too long to respond, often due to a complex query or high server load. |
| Fix | Retry the request. If the issue persists, simplify your query or contact support. |
| 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. |
| 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. |
Field-Level Validation Errors — click to collapse
These errors are returned in the errors array of the response body alongside a 400 status code.
Error: start is required and must not be empty.
| Why | The start field is required on POST. It was omitted or sent as an empty string. |
| Fix | Provide a valid ISO 8601 datetime string, e.g. 2025-06-15T09:00:00.000Z. |
Error: start has an invalid date format.
| Why | The datetime string was provided in an unsupported format. Formats such as YYYY-MM-DDTHH, YYYY-MM-DDTHH:mm, and YYYY-MM-DDTHH:mm:ss are all rejected — they are missing the milliseconds/offset component. |
| Fix | Use a full ISO 8601 datetime format: YYYY-MM-DDTHH:mm:ss.sssZ. |
Error: start and end should be at the same day
| Why | The computed end time (start + duration) crosses midnight into the next calendar day in the location's timezone. |
| Fix | Adjust start or duration so the appointment does not span midnight. |
Error: duration must be an integer between 5 and 1440.
| Why | The duration value (in minutes) is outside the allowed range. Values below 5 or above 1440 (24 hours) are rejected. |
| Fix | Provide a duration value between 5 and 1440 inclusive. |
Error: Invalid enum value for status.
| Why | The value provided for status is not recognized. |
| Fix | Use one of the following valid values: UNCONFIRMED, CONFIRMED, LATE, HERE, READY, CHAIR, CHECKOUT, COMPLETED, WILL_CALL, NO_SHOW, BROKEN, LEFT_MESSAGE, UNREACHABLE, CANCELLEDBYOFFICE. |
Error: Appointment cannot be completed due to missing email for Patient
| Field | status |
| Why | status is being set to COMPLETED, the location has workflowComplianceReminderEnabled = true, and the patient has no emailAddress on file. Additionally, either no COLLECT_EMAIL AppointmentTask exists for this appointment, or the task has not been skipped with a skippedReason. |
| Fix | Add an email address to the patient record, OR ensure a COLLECT_EMAIL AppointmentTask exists with a skippedReason set before marking the appointment COMPLETED. |
Error: Appointment cannot be completed due to missing phone for Patient
| Field | status |
| Why | status is being set to COMPLETED, the location has workflowComplianceReminderEnabled = true, and the patient has no phone numbers on file. Additionally, either no COLLECT_PHONE AppointmentTask exists for this appointment, or the task has not been skipped. |
| Fix | Add a phone number to the patient record, OR ensure a COLLECT_PHONE AppointmentTask exists with a skippedReason set before marking the appointment COMPLETED. |
Error: other exceeds maximum length of 90 characters.
| Why | The other free-text field has a maximum length of 90 characters. |
| Fix | Shorten the other value to 90 characters or fewer. |
Error: needsFollowUp should have value 'true' for setting followedUpOn date
| Field | needsFollowUp |
| Why | A followedUpOn date was provided but needsFollowUp is false or not set. The followedUpOn date is only valid when needsFollowUp is true. |
| Fix | Set needsFollowUp: true alongside followedUpOn, or omit followedUpOn entirely if follow-up is not needed. |
Error: labCaseDueDate cannot be after the end date of the Appointment
| Field | labCaseDueDate |
| Why | The labCaseDueDate you supplied is after the appointment's computed end datetime. Lab case work must be completed before the appointment ends. |
| Fix | Set labCaseDueDate to a date on or before the appointment's end time. |
Error: Either other, practiceProcedures, patientProcedures, or visits must be provided
| Why | An appointment must have at least one of: other, practiceProcedures, patientProcedures, or visits. All four are absent or empty. |
| Fix | Include at least one of other, practiceProcedures, patientProcedures, or visits in your request. |
Error: PatientProcedures with ids [X, Y] are completed and cannot be assigned to the Appointment
| Field | patientProcedures |
| Why | One or more of the provided patientProcedure IDs already have a COMPLETED status. Completed procedures cannot be re-scheduled. |
| Fix | Only link patient procedures that are not yet completed. |
Error: PatientProcedures with ids [X, Y] are created for another Patient and cannot be assigned to the Appointment with different Patient
| Field | patientProcedures |
| Why | The patient procedures you provided belong to a different patient than the one linked to this appointment. |
| Fix | Ensure all patientProcedures belong to the same patient specified in the appointment's patient field. |
Error: Visits with ids [X, Y] are completed and cannot be assigned to the Appointment
| Field | visits |
| Why | One or more of the visits you are linking have all of their procedures in COMPLETED status, making the visit effectively complete. Completed visits cannot be re-scheduled. |
| Fix | Only link visits that still have at least one non-completed procedure. |
Error: Visits with ids [X, Y] are created for another Patient and cannot be assigned to the Appointment with different Patient
| Field | visits |
| Why | The tx plan visits you provided belong to a treatment case for a different patient than the one on this appointment. |
| Fix | Ensure all visits belong to the same patient as the appointment. |
Error: Visit with id 'X' already contains PatientProcedure with id 'Y' so they cannot be assigned separately to the same Appointment
| Why | You included both a visit and one of that visit's own patientProcedures as separate line items on the same appointment. This creates a duplicate association. |
| Fix | Include either the visit or its individual procedures — not both on the same appointment. |
Error: OperatoryV1 not found
| Why | No operatory linked model was provided, or the operatory ID does not exist within your organization. |
| Fix | Provide a valid operatory linked model with an ID that exists in your organization. |
Error: The specified operatory is inactive. Appointment cannot be scheduled.
| Field | operatory.active |
| Why | The operatory you are assigning has active: false. |
| Fix | Use an active operatory, or reactivate the operatory before scheduling. |
Error: location of Appointment and location of Operatory should be the same
| Field | operatory |
| Why | On PUT, the new operatory belongs to a different location than the appointment's existing location. An appointment's location is derived from its operatory and cannot be changed via an operatory swap. |
| Fix | To move an appointment to a different location, delete and recreate it at the correct location. To change operatories, use one from the same location. |
Error: Provider should have 'isNonPersonEntity: false' to be assigned to the appointment
| Field | provider.isNonPersonEntity or otherProvider.isNonPersonEntity |
| Why | The provider is flagged as a non-person entity (e.g. a room or equipment resource). Only person-type providers can be assigned to appointments. |
| Fix | Use a provider with isNonPersonEntity: false. |
Error: Provider should have 'active: true' to be assigned to the appointment
| Field | provider.active or otherProvider.active |
| Why | The provider linked to provider or otherProvider is currently inactive. |
| Fix | Use an active provider, or reactivate the provider before assigning them. |
These fire when a referenced linked model ID cannot be found within your organization.
Error: Linked model with id 'X' of type '<DomainType>' not found
| Why | A single linked model reference (e.g. patient, provider, operatory, labCaseDentalLab) was provided with an ID that does not exist in your organization. |
| Fix | Verify the ID is correct and belongs to your organization. |
Error: Linked models with ids [X, Y] of type '<DomainType>' not found
| Why | One or more IDs in a linked model array (e.g. patientProcedures, practiceProcedures, visits) do not exist in your organization. |
| Fix | Verify all IDs in the array exist and belong to your organization. |
Common
<DomainType>values:LocationV1,PatientV1,ProviderV1,OperatoryV1,DentalLabV1,PracticeProcedureV1,PatientProcedureV1,VisitV1
Error: Related data is absent for the appointment
| HTTP Status | 500 |
| Why | During audit record creation, an expected relationship (e.g. patient or operatory) was unexpectedly null on the appointment record. This is an internal consistency error and should not occur under normal usage. |
| Fix | This typically indicates a data integrity issue. Contact support. |
Warnings — click to collapse
Warnings are returned in the warnings array of the response body when an operation succeeds but there are recommended follow-up actions. They do not cause the request to fail.
| Title | Required operation |
| Description | AppointmentTask with type 'COLLECT_EMAIL' is missing for this appointment and should be created |
| Why | The appointment was created or updated with status = COMPLETED, the location has workflowComplianceReminderEnabled = true, but no COLLECT_EMAIL task has been created for this appointment. |
| Fix | Create an AppointmentTask of type COLLECT_EMAIL for this appointment. |
| Title | Required operation |
| Description | AppointmentTask with type 'COLLECT_PHONE' is missing for this appointment and should be created |
| Why | The appointment was created or updated with status = COMPLETED, the location has workflowComplianceReminderEnabled = true, but no COLLECT_PHONE task has been created for this appointment. |
| Fix | Create an AppointmentTask of type COLLECT_PHONE for this appointment. |
⚠️ Important: These warnings are informational — the operation still succeeds. However, if the appointment is subsequently moved to
COMPLETEDstatus without the tasks being resolved (completed or skipped with askippedReason), the update will be blocked with a400error. See thestatusfield errors above.