Skip to content

Appointments V1 — Developer Guide

Reference: Appointments (V1) Endpoint Documentation


GET — Bulk Appointments

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:

FilterSupported 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.


Important Notes

⚠️ Upcoming Breaking Change: location.id (as a list of IDs or ["ALL"]) will become a required filter parameter. Omitting location.id currently defaults to all locations.

⚠️ Not Recommended: Do not paginate through large data sets using the page parameter, even at pageSize=500. This approach is slow and unreliable at scale.

✅ Recommended: Use lastId pagination instead. See the section below.


Paginating with lastId

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.

Step 1 — Initial Request

Send your first request without lastId:

GET /api/v1/appointments?pageSize=500&filter=lastModified>2025-02-16T00:00:00.000Z,location.id->[64000000000004]

Step 2 — Subsequent Requests

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]

Step 3 — Repeat

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.


Streaming API

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.


Response Structure

A successful 200 response returns a BulkAppointmentResponseWrapperV1 object:

FieldTypeDescription
dataarrayArray of AppointmentV1 objects
warningsarrayNon-fatal warnings (operation still succeeded)
errorsarrayField-level or structural errors
metaobjectPagination metadata — see meta.pagination below

meta.pagination Object

FieldTypeDescription
lastIdintegerThe id of the last record returned. Pass as lastId in the next request to continue pagination.

AppointmentV1 Object

Each item in data represents a single appointment:

FieldTypeAccessDescription
idintegerread-onlyUnique appointment identifier
startstringread/writeAppointment start time (ISO 8601, e.g. 2024-05-09T12:00:00.000Z)
endstringread-onlyComputed end time based on start + duration
durationintegerread/writeLength of the appointment in minutes. Valid range: 51440
statusstringread/writeAppointment status enum. See status field errors for valid values
statusIdnumberread-onlyNumeric representation of status
createdstringread-onlyTimestamp when the appointment was created
lastModifiedstringread-onlyTimestamp of the most recent update
bookingTypestringread/writeBooking type enum: TREATMENT, RECARE, NEW_PATIENT, EXISTING_PATIENT
needsFollowUpbooleanread/writeWhether the patient requires a follow-up
followedUpOnstringread/writeDate follow-up was completed. Requires needsFollowUp: true
notestringread/writeInternal appointment note
otherstringread/writeFree-text procedure description. Maximum 90 characters
asapbooleanread/writeWhether the appointment is flagged as ASAP
bookedOnlinebooleanread/writeWhether the appointment was booked online
needsPremedicatebooleanread/writeWhether the patient requires premedication
labCaseDentalLabobjectread/writeLinked model reference to the dental lab. Setting to null clears all lab case fields
labCaseStatusstringread/writeLab case status: OUTSTANDING, RECEIVED
labCaseDueDatestringread/writeLab case due date (must be on or before appointment end time)
labCaseNotestringread/writeNote associated with the lab case
providerobjectread/writeLinked model reference to the primary provider
otherProviderobjectread/writeLinked model reference to a secondary provider
patientobjectread/writeLinked model reference to the patient
operatoryobjectread/writeLinked model reference to the operatory
patientProceduresarrayread/writeArray of linked model references to patient procedures
practiceProceduresarrayread/writeArray of linked model references to practice procedures
visitsarrayread/writeArray of linked model references to treatment plan visits
timePatternobjectread-onlyBreakdown of provider/assistant/chair time within the appointment
confirmedstringread-onlyTimestamp when the appointment was confirmed
leftMessagestringread-onlyDate a message was left for the patient

Errors & Warnings Reference


HTTP Status Code Errors — click to expand

These apply to all Appointments V1 endpoints.


400 — Bad Request

The request body or query parameters are invalid or cannot be processed. See the Field-Level Validation Errors section below for specific causes.


401 — Unauthorized

MessageAuthentication is required and has failed or has not been provided.
WhyNo valid auth token was sent, the token is expired, or the token is malformed.
FixProvide a valid Bearer token in the Authorization header.

403 — Forbidden

MessageThe request is understood, but it has been refused or access is not allowed.
WhyThe authenticated token does not have the required scope. Read operations require model:read; write operations (POST, PUT, DELETE) require model:write.
FixRequest a token that includes the appropriate scope for the operation you are attempting.

404 — Not Found

MessageThe requested resource is either missing or does not exist.
WhyNo appointment exists for the given appointmentId within your organization, or the ID is not a valid integer (e.g. 0 or non-numeric).
FixConfirm the appointment ID is correct and belongs to your organization.

408 — Request Timeout

MessageThe server timed out while processing the request.
WhyThe server took too long to respond, often due to a complex query or high server load.
FixRetry the request. If the issue persists, simplify your query or contact support.

429 — Too Many Requests

MessageRate limit exceeded.
WhyYour client has made too many requests in a short period of time.
FixImplement exponential backoff and retry logic. Reduce request frequency.

500 — Internal Server Error

MessageAn unexpected error occurred.
WhyAn unhandled server-side error occurred.
FixRetry 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.


start

Error: start is required and must not be empty.

WhyThe start field is required on POST. It was omitted or sent as an empty string.
FixProvide a valid ISO 8601 datetime string, e.g. 2025-06-15T09:00:00.000Z.

Error: start has an invalid date format.

WhyThe 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.
FixUse a full ISO 8601 datetime format: YYYY-MM-DDTHH:mm:ss.sssZ.

Error: start and end should be at the same day

WhyThe computed end time (start + duration) crosses midnight into the next calendar day in the location's timezone.
FixAdjust start or duration so the appointment does not span midnight.

duration

Error: duration must be an integer between 5 and 1440.

WhyThe duration value (in minutes) is outside the allowed range. Values below 5 or above 1440 (24 hours) are rejected.
FixProvide a duration value between 5 and 1440 inclusive.

status

Error: Invalid enum value for status.

WhyThe value provided for status is not recognized.
FixUse 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

Fieldstatus
Whystatus 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.
FixAdd 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

Fieldstatus
Whystatus 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.
FixAdd a phone number to the patient record, OR ensure a COLLECT_PHONE AppointmentTask exists with a skippedReason set before marking the appointment COMPLETED.

other

Error: other exceeds maximum length of 90 characters.

WhyThe other free-text field has a maximum length of 90 characters.
FixShorten the other value to 90 characters or fewer.

needsFollowUp / followedUpOn

Error: needsFollowUp should have value 'true' for setting followedUpOn date

FieldneedsFollowUp
WhyA followedUpOn date was provided but needsFollowUp is false or not set. The followedUpOn date is only valid when needsFollowUp is true.
FixSet needsFollowUp: true alongside followedUpOn, or omit followedUpOn entirely if follow-up is not needed.

labCaseDueDate

Error: labCaseDueDate cannot be after the end date of the Appointment

FieldlabCaseDueDate
WhyThe labCaseDueDate you supplied is after the appointment's computed end datetime. Lab case work must be completed before the appointment ends.
FixSet labCaseDueDate to a date on or before the appointment's end time.

practiceProcedures / patientProcedures / visits / other

Error: Either other, practiceProcedures, patientProcedures, or visits must be provided

WhyAn appointment must have at least one of: other, practiceProcedures, patientProcedures, or visits. All four are absent or empty.
FixInclude at least one of other, practiceProcedures, patientProcedures, or visits in your request.

patientProcedures

Error: PatientProcedures with ids [X, Y] are completed and cannot be assigned to the Appointment

FieldpatientProcedures
WhyOne or more of the provided patientProcedure IDs already have a COMPLETED status. Completed procedures cannot be re-scheduled.
FixOnly 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

FieldpatientProcedures
WhyThe patient procedures you provided belong to a different patient than the one linked to this appointment.
FixEnsure all patientProcedures belong to the same patient specified in the appointment's patient field.

visits

Error: Visits with ids [X, Y] are completed and cannot be assigned to the Appointment

Fieldvisits
WhyOne 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.
FixOnly 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

Fieldvisits
WhyThe tx plan visits you provided belong to a treatment case for a different patient than the one on this appointment.
FixEnsure 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

WhyYou 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.
FixInclude either the visit or its individual procedures — not both on the same appointment.

operatory

Error: OperatoryV1 not found

WhyNo operatory linked model was provided, or the operatory ID does not exist within your organization.
FixProvide a valid operatory linked model with an ID that exists in your organization.

Error: The specified operatory is inactive. Appointment cannot be scheduled.

Fieldoperatory.active
WhyThe operatory you are assigning has active: false.
FixUse an active operatory, or reactivate the operatory before scheduling.

Error: location of Appointment and location of Operatory should be the same

Fieldoperatory
WhyOn 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.
FixTo move an appointment to a different location, delete and recreate it at the correct location. To change operatories, use one from the same location.

provider / otherProvider

Error: Provider should have 'isNonPersonEntity: false' to be assigned to the appointment

Fieldprovider.isNonPersonEntity or otherProvider.isNonPersonEntity
WhyThe provider is flagged as a non-person entity (e.g. a room or equipment resource). Only person-type providers can be assigned to appointments.
FixUse a provider with isNonPersonEntity: false.

Error: Provider should have 'active: true' to be assigned to the appointment

Fieldprovider.active or otherProvider.active
WhyThe provider linked to provider or otherProvider is currently inactive.
FixUse an active provider, or reactivate the provider before assigning them.

Linked Model "Not Found" Errors

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

WhyA single linked model reference (e.g. patient, provider, operatory, labCaseDentalLab) was provided with an ID that does not exist in your organization.
FixVerify the ID is correct and belongs to your organization.

Error: Linked models with ids [X, Y] of type '<DomainType>' not found

WhyOne or more IDs in a linked model array (e.g. patientProcedures, practiceProcedures, visits) do not exist in your organization.
FixVerify 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 Status500
WhyDuring 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.
FixThis 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.


Warning — Missing COLLECT_EMAIL AppointmentTask

TitleRequired operation
DescriptionAppointmentTask with type 'COLLECT_EMAIL' is missing for this appointment and should be created
WhyThe appointment was created or updated with status = COMPLETED, the location has workflowComplianceReminderEnabled = true, but no COLLECT_EMAIL task has been created for this appointment.
FixCreate an AppointmentTask of type COLLECT_EMAIL for this appointment.

Warning — Missing COLLECT_PHONE AppointmentTask

TitleRequired operation
DescriptionAppointmentTask with type 'COLLECT_PHONE' is missing for this appointment and should be created
WhyThe appointment was created or updated with status = COMPLETED, the location has workflowComplianceReminderEnabled = true, but no COLLECT_PHONE task has been created for this appointment.
FixCreate 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 COMPLETED status without the tasks being resolved (completed or skipped with a skippedReason), the update will be blocked with a 400 error. See the status field errors above.