curl --request GET \
--url https://api.maxcare.ai/v1/appointments \
--header 'Authorization: Bearer <token>' \
--header 'X-Organization-Id: <x-organization-id>'import requests
url = "https://api.maxcare.ai/v1/appointments"
headers = {
"X-Organization-Id": "<x-organization-id>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'X-Organization-Id': '<x-organization-id>', Authorization: 'Bearer <token>'}
};
fetch('https://api.maxcare.ai/v1/appointments', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.maxcare.ai/v1/appointments",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"X-Organization-Id: <x-organization-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.maxcare.ai/v1/appointments"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Organization-Id", "<x-organization-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.maxcare.ai/v1/appointments")
.header("X-Organization-Id", "<x-organization-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.maxcare.ai/v1/appointments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Organization-Id"] = '<x-organization-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"code": "success",
"data": {
"appointments": [
{
"id": "e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8a9b",
"scheduledStartDate": "2025-01-20T09:00:00.000Z",
"scheduledEndDate": "2025-01-20T09:30:00.000Z",
"status": "confirmed",
"integrationStatus": "CHECKED_IN",
"isNewPatient": false,
"instanceId": "3a7b9c1d-2e4f-45a6-8b0c-1d2e3f4a5b6c",
"appointmentTypeAbbreviation": "EST",
"appointmentTypeDurationMinutes": 15,
"cancelReasonIntegrationId": "CR00000001",
"cancelReasonName": "Patient cancelled",
"appointmentTypeName": "Injection Visit",
"integrationAppointmentTypeId": "12345",
"reasonForVisit": "Biologic injection follow-up",
"isCosmetic": false,
"patientId": "c56103bc-d39c-46d3-9f31-38dd2b5e05f6",
"patientFirstName": "Sarah",
"patientLastName": "Johnson",
"patientMrn": "MRN-10042",
"providerId": "d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8a",
"providerFirstName": "James",
"providerLastName": "Wilson",
"facilityId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"facilityName": "West LA Dermatology",
"createdAt": "2025-01-15T09:30:00.000Z",
"lastSyncedAt": "2025-01-16T04:12:00.000Z",
"externalId": "<string>",
"syncStatus": "pending",
"lastError": "auth_error",
"retryCount": 0,
"nextRetryAt": "<string>",
"bookingSource": {
"system": "dcc-online-booking",
"bookingReference": "BK-2026-000123",
"campaignId": "google-gbp-flint",
"url": "https://book.dccderm.com/l/flint?utm_source=gbp"
}
}
],
"pagination": {
"page": 1,
"pageSize": 100,
"totalCount": 250,
"totalPages": 3
}
}
}{
"code": "bad_request",
"message": "'id' must be a valid UUID",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "unauthorized",
"message": "Invalid or missing API key",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "forbidden",
"message": "Insufficient scope",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Maximum 1000 requests per 60 seconds.",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}List appointments
Returns a paginated list of appointments. Requires read:appointments scope.
curl --request GET \
--url https://api.maxcare.ai/v1/appointments \
--header 'Authorization: Bearer <token>' \
--header 'X-Organization-Id: <x-organization-id>'import requests
url = "https://api.maxcare.ai/v1/appointments"
headers = {
"X-Organization-Id": "<x-organization-id>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'X-Organization-Id': '<x-organization-id>', Authorization: 'Bearer <token>'}
};
fetch('https://api.maxcare.ai/v1/appointments', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.maxcare.ai/v1/appointments",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"X-Organization-Id: <x-organization-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.maxcare.ai/v1/appointments"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Organization-Id", "<x-organization-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.maxcare.ai/v1/appointments")
.header("X-Organization-Id", "<x-organization-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.maxcare.ai/v1/appointments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Organization-Id"] = '<x-organization-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"code": "success",
"data": {
"appointments": [
{
"id": "e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8a9b",
"scheduledStartDate": "2025-01-20T09:00:00.000Z",
"scheduledEndDate": "2025-01-20T09:30:00.000Z",
"status": "confirmed",
"integrationStatus": "CHECKED_IN",
"isNewPatient": false,
"instanceId": "3a7b9c1d-2e4f-45a6-8b0c-1d2e3f4a5b6c",
"appointmentTypeAbbreviation": "EST",
"appointmentTypeDurationMinutes": 15,
"cancelReasonIntegrationId": "CR00000001",
"cancelReasonName": "Patient cancelled",
"appointmentTypeName": "Injection Visit",
"integrationAppointmentTypeId": "12345",
"reasonForVisit": "Biologic injection follow-up",
"isCosmetic": false,
"patientId": "c56103bc-d39c-46d3-9f31-38dd2b5e05f6",
"patientFirstName": "Sarah",
"patientLastName": "Johnson",
"patientMrn": "MRN-10042",
"providerId": "d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8a",
"providerFirstName": "James",
"providerLastName": "Wilson",
"facilityId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"facilityName": "West LA Dermatology",
"createdAt": "2025-01-15T09:30:00.000Z",
"lastSyncedAt": "2025-01-16T04:12:00.000Z",
"externalId": "<string>",
"syncStatus": "pending",
"lastError": "auth_error",
"retryCount": 0,
"nextRetryAt": "<string>",
"bookingSource": {
"system": "dcc-online-booking",
"bookingReference": "BK-2026-000123",
"campaignId": "google-gbp-flint",
"url": "https://book.dccderm.com/l/flint?utm_source=gbp"
}
}
],
"pagination": {
"page": 1,
"pageSize": 100,
"totalCount": 250,
"totalPages": 3
}
}
}{
"code": "bad_request",
"message": "'id' must be a valid UUID",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "unauthorized",
"message": "Invalid or missing API key",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "forbidden",
"message": "Insufficient scope",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Maximum 1000 requests per 60 seconds.",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}Authorizations
Marketplace API key
Headers
Target clinic organization ID
Query Parameters
Filter appointments from this date (ISO 8601)
"2025-01-01"
Filter appointments up to this date (ISO 8601)
"2025-12-31"
Comma-separated list of normalized statuses to filter by. See GET /appointments/statuses for the vocabulary; cancelled,no_show is the reactivation filter. A value outside the vocabulary is rejected with 400 — it is NOT silently ignored, and this applies on every version including v1/v2. Send the normalized status, never the EHR's own integrationStatus.
scheduled, confirmed, arrived, in_progress, completed, cancelled, no_show, rescheduled, other, unknown "cancelled,no_show"
Comma-separated list of appointment type ids from GET /appointment-types — the id field, NOT externalId. The EHR-native id is only unique within one EHR instance, so filtering on it would silently match another instance's appointments on a multi-instance organization; the catalogue id is unique. An id that does not resolve is dropped, and a filter where NONE resolve returns no appointments rather than falling back to unfiltered. At most 100 ids per request. (Stated here rather than as maxItems: Nest emits that only for body schemas, not query parameters — verified against the generated documents.)
Comma-separated list of cancellation reason ids from GET /cancel-reasons — the id field, NOT externalId. Only matches appointments whose reason has been captured, which is currently none — see the cancelReason field's note. At most 100 ids per request.
Filter by facility ID
Filter by patient ID
Only return appointments whose record was created or re-synced at/after this timestamp (ISO 8601). Compared against lastSyncedAt: this resource has no separate modification stamp, and the sync advances that field only when one of the appointment's OWN columns actually changes. So this feed tracks the appointment row and nothing else — it does NOT track the joined patient*, provider* and facilityName fields it returns. Renaming a patient changes patientLastName in the response without moving lastSyncedAt; mirror those from /patients, /providers and /facilities instead. When set, results are ordered by lastSyncedAt ascending with id as tiebreaker, overriding sortBy. Must carry an explicit UTC offset (...Z or ...+02:00) — a zone-less instant would be resolved against the database session timezone on some resources and the API process timezone on others, so the same string would mean two different moments. Pair with cursorId to walk safely — see that parameter; page/offset paging of this feed can drop rows. The bound is inclusive without cursorId, so boundary rows re-deliver — dedupe on id. A change feed cannot report a deletion; a consumer that prunes still needs a full walk.
"2026-08-01T00:00:00.000Z"
Only return appointments whose record was last re-synced at/before this timestamp (ISO 8601). Pin this to the instant the walk started to freeze the window. Must carry an explicit UTC offset.
"2026-08-31T23:59:59.999Z"
Id of the last row you already consumed, for a keyset walk. Pass it together with updatedSince set to that same row's lastSyncedAt, always requesting page 1; the next page is everything ordered after (lastSyncedAt, id). Use this rather than page/offset for any walk that must not drop rows. Paging a delta feed with page/offset is unsafe because the sort column is exactly what the sync workers rewrite: a row on an earlier page that changes mid-walk moves to the tail, every later row shifts one position toward the front, and the offset for the next page steps over whichever row moved into that slot. That row is never returned and its stamp is below the cursor the walk finishes on, so it is missed permanently while the walk still looks successful. On a cursor page pagination.totalCount counts the window starting at your cursor, so it shrinks as you walk — stop on a short page rather than on the count.
"apt_e5f6a7b8c9d04e1f2a3b4c5d6e7f8a9b"
Sort field. Ignored when updatedSince/updatedUntil is set — see updatedSince.
scheduledStartDate, lastSyncedAt, status Sort direction
asc, desc 