curl --request POST \
--url https://api.maxcare.ai/v2/appointments \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-Organization-Id: <x-organization-id>' \
--data '
{
"patientId": "<string>",
"providerId": "<string>",
"facilityId": "<string>",
"appointmentTypeId": "<string>",
"startsAt": "2026-08-03T08:00:00-04:00",
"durationMinutes": 15,
"newPatient": true,
"visitReasonId": "<string>",
"holdId": "<string>",
"templateId": "<string>",
"notes": "Patient comment text"
}
'import requests
url = "https://api.maxcare.ai/v2/appointments"
payload = {
"patientId": "<string>",
"providerId": "<string>",
"facilityId": "<string>",
"appointmentTypeId": "<string>",
"startsAt": "2026-08-03T08:00:00-04:00",
"durationMinutes": 15,
"newPatient": True,
"visitReasonId": "<string>",
"holdId": "<string>",
"templateId": "<string>",
"notes": "Patient comment text"
}
headers = {
"X-Organization-Id": "<x-organization-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Organization-Id': '<x-organization-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
patientId: '<string>',
providerId: '<string>',
facilityId: '<string>',
appointmentTypeId: '<string>',
startsAt: '2026-08-03T08:00:00-04:00',
durationMinutes: 15,
newPatient: true,
visitReasonId: '<string>',
holdId: '<string>',
templateId: '<string>',
notes: 'Patient comment text'
})
};
fetch('https://api.maxcare.ai/v2/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/v2/appointments",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'patientId' => '<string>',
'providerId' => '<string>',
'facilityId' => '<string>',
'appointmentTypeId' => '<string>',
'startsAt' => '2026-08-03T08:00:00-04:00',
'durationMinutes' => 15,
'newPatient' => true,
'visitReasonId' => '<string>',
'holdId' => '<string>',
'templateId' => '<string>',
'notes' => 'Patient comment text'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"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"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.maxcare.ai/v2/appointments"
payload := strings.NewReader("{\n \"patientId\": \"<string>\",\n \"providerId\": \"<string>\",\n \"facilityId\": \"<string>\",\n \"appointmentTypeId\": \"<string>\",\n \"startsAt\": \"2026-08-03T08:00:00-04:00\",\n \"durationMinutes\": 15,\n \"newPatient\": true,\n \"visitReasonId\": \"<string>\",\n \"holdId\": \"<string>\",\n \"templateId\": \"<string>\",\n \"notes\": \"Patient comment text\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Organization-Id", "<x-organization-id>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.maxcare.ai/v2/appointments")
.header("X-Organization-Id", "<x-organization-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"patientId\": \"<string>\",\n \"providerId\": \"<string>\",\n \"facilityId\": \"<string>\",\n \"appointmentTypeId\": \"<string>\",\n \"startsAt\": \"2026-08-03T08:00:00-04:00\",\n \"durationMinutes\": 15,\n \"newPatient\": true,\n \"visitReasonId\": \"<string>\",\n \"holdId\": \"<string>\",\n \"templateId\": \"<string>\",\n \"notes\": \"Patient comment text\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.maxcare.ai/v2/appointments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Organization-Id"] = '<x-organization-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"patientId\": \"<string>\",\n \"providerId\": \"<string>\",\n \"facilityId\": \"<string>\",\n \"appointmentTypeId\": \"<string>\",\n \"startsAt\": \"2026-08-03T08:00:00-04:00\",\n \"durationMinutes\": 15,\n \"newPatient\": true,\n \"visitReasonId\": \"<string>\",\n \"holdId\": \"<string>\",\n \"templateId\": \"<string>\",\n \"notes\": \"Patient comment text\"\n}"
response = http.request(request)
puts response.read_body{
"code": "success",
"data": {
"appointment": {
"id": "apt_e5f6a7b8c9d04e1f2a3b4c5d6e7f8a9b",
"externalId": null,
"patientId": "pat_c56103bcd39c46d39f3138dd2b5e05f6",
"providerId": "prv_35103413aaaa4bbbccccddddeeeeffff",
"facilityId": "fac_2706aaaa4bbbccccddddeeeeffff1111",
"appointmentTypeId": "atp_1c0f8a2b4d5e4f6a8b9c0d1e2f3a4b5c",
"visitReasonId": "<string>",
"startsAt": "2026-08-03T08:00:00-04:00",
"durationMinutes": 15,
"status": "scheduled",
"syncStatus": "pending",
"newPatient": true,
"notes": "<string>",
"source": {
"system": "dcc-online-booking",
"bookingReference": "BK-2026-000123",
"campaignId": "google-gbp-flint",
"url": "https://book.dccderm.com/l/flint?utm_source=gbp"
},
"createdAt": "2026-07-26T13:00:00.000Z"
},
"idempotentReplay": true
}
}{
"code": "success",
"data": {
"appointment": {
"id": "apt_e5f6a7b8c9d04e1f2a3b4c5d6e7f8a9b",
"externalId": null,
"patientId": "pat_c56103bcd39c46d39f3138dd2b5e05f6",
"providerId": "prv_35103413aaaa4bbbccccddddeeeeffff",
"facilityId": "fac_2706aaaa4bbbccccddddeeeeffff1111",
"appointmentTypeId": "atp_1c0f8a2b4d5e4f6a8b9c0d1e2f3a4b5c",
"visitReasonId": "<string>",
"startsAt": "2026-08-03T08:00:00-04:00",
"durationMinutes": 15,
"status": "scheduled",
"syncStatus": "pending",
"newPatient": true,
"notes": "<string>",
"source": {
"system": "dcc-online-booking",
"bookingReference": "BK-2026-000123",
"campaignId": "google-gbp-flint",
"url": "https://book.dccderm.com/l/flint?utm_source=gbp"
},
"createdAt": "2026-07-26T13:00:00.000Z"
},
"idempotentReplay": true
}
}{
"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": "not_found",
"message": "Resource not found",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "conflict",
"message": "Cannot edit a signed note",
"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"
}{
"code": "unexpected_integration_error",
"message": "EHR sync failed",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "server_unresponsive",
"message": "EHR integration is not available for this note",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}Book an appointment
Accepts a booking. The EHR write is asynchronous (changed deliberately — see below): a 202 with syncStatus: "pending" means the slot is RESERVED against everyone else booking through Max AI and the appointment is durably recorded, but it is NOT on the practice’s calendar yet and externalId is null. Completion is reported by webhook: appointment.confirmed (written; externalId now known), appointment.rejected (the slot went away — the event carries suggestedSlots, so offer those rather than restarting the flow), or appointment.write_failed (could not be written after retries). Polling GET /appointments/{id} works too. Do not tell a patient they are booked on this response alone. This previously returned 201 after a synchronous write, which made a slow tenant a slow booking form and a vendor outage a booking outage. Booking now succeeds while the EHR is unreachable. Send an Idempotency-Key header — it is honoured for 24 hours per app+organization, and a completed request replays its result with idempotentReplay: true. If acceptance could not be confirmed, the same key remains protected for that period and returns 409 ehr_write_uncertain with requiresReconciliation: true; verify the booking before creating another. The guarantee FAILS CLOSED: if the idempotency store is unreachable the request is refused with 503 idempotency_unavailable and nothing is sent to the EHR, rather than proceeding with the guarantee silently withdrawn. That response is safe to retry without checking for a duplicate first. To accept at-least-once delivery instead, omit the header. Returns 409 slot_unavailable when capacity went away, 409 hold_expired when the supplied hold lapsed, and 503 no_booking_identity when the organization has no system connector for unattended writes.
curl --request POST \
--url https://api.maxcare.ai/v2/appointments \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-Organization-Id: <x-organization-id>' \
--data '
{
"patientId": "<string>",
"providerId": "<string>",
"facilityId": "<string>",
"appointmentTypeId": "<string>",
"startsAt": "2026-08-03T08:00:00-04:00",
"durationMinutes": 15,
"newPatient": true,
"visitReasonId": "<string>",
"holdId": "<string>",
"templateId": "<string>",
"notes": "Patient comment text"
}
'import requests
url = "https://api.maxcare.ai/v2/appointments"
payload = {
"patientId": "<string>",
"providerId": "<string>",
"facilityId": "<string>",
"appointmentTypeId": "<string>",
"startsAt": "2026-08-03T08:00:00-04:00",
"durationMinutes": 15,
"newPatient": True,
"visitReasonId": "<string>",
"holdId": "<string>",
"templateId": "<string>",
"notes": "Patient comment text"
}
headers = {
"X-Organization-Id": "<x-organization-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Organization-Id': '<x-organization-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
patientId: '<string>',
providerId: '<string>',
facilityId: '<string>',
appointmentTypeId: '<string>',
startsAt: '2026-08-03T08:00:00-04:00',
durationMinutes: 15,
newPatient: true,
visitReasonId: '<string>',
holdId: '<string>',
templateId: '<string>',
notes: 'Patient comment text'
})
};
fetch('https://api.maxcare.ai/v2/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/v2/appointments",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'patientId' => '<string>',
'providerId' => '<string>',
'facilityId' => '<string>',
'appointmentTypeId' => '<string>',
'startsAt' => '2026-08-03T08:00:00-04:00',
'durationMinutes' => 15,
'newPatient' => true,
'visitReasonId' => '<string>',
'holdId' => '<string>',
'templateId' => '<string>',
'notes' => 'Patient comment text'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"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"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.maxcare.ai/v2/appointments"
payload := strings.NewReader("{\n \"patientId\": \"<string>\",\n \"providerId\": \"<string>\",\n \"facilityId\": \"<string>\",\n \"appointmentTypeId\": \"<string>\",\n \"startsAt\": \"2026-08-03T08:00:00-04:00\",\n \"durationMinutes\": 15,\n \"newPatient\": true,\n \"visitReasonId\": \"<string>\",\n \"holdId\": \"<string>\",\n \"templateId\": \"<string>\",\n \"notes\": \"Patient comment text\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Organization-Id", "<x-organization-id>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.maxcare.ai/v2/appointments")
.header("X-Organization-Id", "<x-organization-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"patientId\": \"<string>\",\n \"providerId\": \"<string>\",\n \"facilityId\": \"<string>\",\n \"appointmentTypeId\": \"<string>\",\n \"startsAt\": \"2026-08-03T08:00:00-04:00\",\n \"durationMinutes\": 15,\n \"newPatient\": true,\n \"visitReasonId\": \"<string>\",\n \"holdId\": \"<string>\",\n \"templateId\": \"<string>\",\n \"notes\": \"Patient comment text\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.maxcare.ai/v2/appointments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Organization-Id"] = '<x-organization-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"patientId\": \"<string>\",\n \"providerId\": \"<string>\",\n \"facilityId\": \"<string>\",\n \"appointmentTypeId\": \"<string>\",\n \"startsAt\": \"2026-08-03T08:00:00-04:00\",\n \"durationMinutes\": 15,\n \"newPatient\": true,\n \"visitReasonId\": \"<string>\",\n \"holdId\": \"<string>\",\n \"templateId\": \"<string>\",\n \"notes\": \"Patient comment text\"\n}"
response = http.request(request)
puts response.read_body{
"code": "success",
"data": {
"appointment": {
"id": "apt_e5f6a7b8c9d04e1f2a3b4c5d6e7f8a9b",
"externalId": null,
"patientId": "pat_c56103bcd39c46d39f3138dd2b5e05f6",
"providerId": "prv_35103413aaaa4bbbccccddddeeeeffff",
"facilityId": "fac_2706aaaa4bbbccccddddeeeeffff1111",
"appointmentTypeId": "atp_1c0f8a2b4d5e4f6a8b9c0d1e2f3a4b5c",
"visitReasonId": "<string>",
"startsAt": "2026-08-03T08:00:00-04:00",
"durationMinutes": 15,
"status": "scheduled",
"syncStatus": "pending",
"newPatient": true,
"notes": "<string>",
"source": {
"system": "dcc-online-booking",
"bookingReference": "BK-2026-000123",
"campaignId": "google-gbp-flint",
"url": "https://book.dccderm.com/l/flint?utm_source=gbp"
},
"createdAt": "2026-07-26T13:00:00.000Z"
},
"idempotentReplay": true
}
}{
"code": "success",
"data": {
"appointment": {
"id": "apt_e5f6a7b8c9d04e1f2a3b4c5d6e7f8a9b",
"externalId": null,
"patientId": "pat_c56103bcd39c46d39f3138dd2b5e05f6",
"providerId": "prv_35103413aaaa4bbbccccddddeeeeffff",
"facilityId": "fac_2706aaaa4bbbccccddddeeeeffff1111",
"appointmentTypeId": "atp_1c0f8a2b4d5e4f6a8b9c0d1e2f3a4b5c",
"visitReasonId": "<string>",
"startsAt": "2026-08-03T08:00:00-04:00",
"durationMinutes": 15,
"status": "scheduled",
"syncStatus": "pending",
"newPatient": true,
"notes": "<string>",
"source": {
"system": "dcc-online-booking",
"bookingReference": "BK-2026-000123",
"campaignId": "google-gbp-flint",
"url": "https://book.dccderm.com/l/flint?utm_source=gbp"
},
"createdAt": "2026-07-26T13:00:00.000Z"
},
"idempotentReplay": true
}
}{
"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": "not_found",
"message": "Resource not found",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "conflict",
"message": "Cannot edit a signed note",
"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"
}{
"code": "unexpected_integration_error",
"message": "EHR sync failed",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "server_unresponsive",
"message": "EHR integration is not available for this note",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}Authorizations
Marketplace API key
Headers
Client-generated UUID. Strongly recommended: on a live calendar a duplicate appointment is worse than a slow response. Sending it is also a request for exactly-once — if that cannot be honoured the write is refused (503 idempotency_unavailable) rather than downgraded.
Target clinic organization ID
Body
Patient ID (pat_…)
Provider ID (prv_…)
Facility ID (fac_…)
Appointment type ID (atp_…)
Start instant, ISO-8601 with an explicit offset
"2026-08-03T08:00:00-04:00"
Appointment length in minutes
15
Whether this is a new-patient visit. Both EHRs distinguish it, and it changes the EHR-side workflow.
true
Visit reason ID (vrs_…). Only meaningful on EHRs where /visit-reasons reports supported: true.
Hold ID (hld_…) taken from POST /schedule/holds
Availability template ID (avt_…) from the chosen slot's templateIds. On ModMed this pins the appointment to that calendar preference so it consumes the right window's capacity.
Patient-supplied comment
"Patient comment text"
Booking attribution, echoed back on read. Use this instead of prefixing appointment notes — notes are a clinical field and get read aloud at check-in.
Show child attributes
Show child attributes
