curl --request POST \
--url https://api.usecobalt.com/v1/appointments \
--header 'Content-Type: application/json' \
--header 'access_token: <api-key>' \
--header 'client_id: <api-key>' \
--header 'client_secret: <api-key>' \
--data '
{
"mrn": "12345",
"patient_name": "Jane Doe",
"location": "location-1",
"datetime": "2026-06-15T14:30:00-07:00",
"date": "2026-06-15",
"time": "2:30 pm",
"timezone": "America/Chicago",
"provider": "provider-123",
"secondary_provider": "provider-456",
"type": "NP",
"note": "Purpose of visit: Annual checkup",
"duration": "30",
"reason": "Annual checkup",
"billing_note": "Copay collected at check-in",
"check_eligibility": "false",
"complaint_type": "complaint-123",
"prevent_double_booking": "true",
"max_overlapping_appointments": "2",
"skip_visit_type_validation": "false",
"skip_confidential_charts": "false",
"include_inactive_patients": "false",
"new_patient": "true",
"practice_id": "practice-1",
"department_id": "department-1",
"non_billable": "false",
"recall_id": "recall-abc-123",
"callback_urls": [
"https://example.com/webhooks/cobalt"
],
"expires_at": "2027-06-15T14:30:00Z"
}
'import requests
url = "https://api.usecobalt.com/v1/appointments"
payload = {
"mrn": "12345",
"patient_name": "Jane Doe",
"location": "location-1",
"datetime": "2026-06-15T14:30:00-07:00",
"date": "2026-06-15",
"time": "2:30 pm",
"timezone": "America/Chicago",
"provider": "provider-123",
"secondary_provider": "provider-456",
"type": "NP",
"note": "Purpose of visit: Annual checkup",
"duration": "30",
"reason": "Annual checkup",
"billing_note": "Copay collected at check-in",
"check_eligibility": "false",
"complaint_type": "complaint-123",
"prevent_double_booking": "true",
"max_overlapping_appointments": "2",
"skip_visit_type_validation": "false",
"skip_confidential_charts": "false",
"include_inactive_patients": "false",
"new_patient": "true",
"practice_id": "practice-1",
"department_id": "department-1",
"non_billable": "false",
"recall_id": "recall-abc-123",
"callback_urls": ["https://example.com/webhooks/cobalt"],
"expires_at": "2027-06-15T14:30:00Z"
}
headers = {
"client_id": "<api-key>",
"client_secret": "<api-key>",
"access_token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
client_id: '<api-key>',
client_secret: '<api-key>',
access_token: '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
mrn: '12345',
patient_name: 'Jane Doe',
location: 'location-1',
datetime: '2026-06-15T14:30:00-07:00',
date: '2026-06-15',
time: '2:30 pm',
timezone: 'America/Chicago',
provider: 'provider-123',
secondary_provider: 'provider-456',
type: 'NP',
note: 'Purpose of visit: Annual checkup',
duration: '30',
reason: 'Annual checkup',
billing_note: 'Copay collected at check-in',
check_eligibility: 'false',
complaint_type: 'complaint-123',
prevent_double_booking: 'true',
max_overlapping_appointments: '2',
skip_visit_type_validation: 'false',
skip_confidential_charts: 'false',
include_inactive_patients: 'false',
new_patient: 'true',
practice_id: 'practice-1',
department_id: 'department-1',
non_billable: 'false',
recall_id: 'recall-abc-123',
callback_urls: ['https://example.com/webhooks/cobalt'],
expires_at: '2027-06-15T14:30:00Z'
})
};
fetch('https://api.usecobalt.com/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.usecobalt.com/v1/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([
'mrn' => '12345',
'patient_name' => 'Jane Doe',
'location' => 'location-1',
'datetime' => '2026-06-15T14:30:00-07:00',
'date' => '2026-06-15',
'time' => '2:30 pm',
'timezone' => 'America/Chicago',
'provider' => 'provider-123',
'secondary_provider' => 'provider-456',
'type' => 'NP',
'note' => 'Purpose of visit: Annual checkup',
'duration' => '30',
'reason' => 'Annual checkup',
'billing_note' => 'Copay collected at check-in',
'check_eligibility' => 'false',
'complaint_type' => 'complaint-123',
'prevent_double_booking' => 'true',
'max_overlapping_appointments' => '2',
'skip_visit_type_validation' => 'false',
'skip_confidential_charts' => 'false',
'include_inactive_patients' => 'false',
'new_patient' => 'true',
'practice_id' => 'practice-1',
'department_id' => 'department-1',
'non_billable' => 'false',
'recall_id' => 'recall-abc-123',
'callback_urls' => [
'https://example.com/webhooks/cobalt'
],
'expires_at' => '2027-06-15T14:30:00Z'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"access_token: <api-key>",
"client_id: <api-key>",
"client_secret: <api-key>"
],
]);
$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.usecobalt.com/v1/appointments"
payload := strings.NewReader("{\n \"mrn\": \"12345\",\n \"patient_name\": \"Jane Doe\",\n \"location\": \"location-1\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\n \"date\": \"2026-06-15\",\n \"time\": \"2:30 pm\",\n \"timezone\": \"America/Chicago\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"type\": \"NP\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"duration\": \"30\",\n \"reason\": \"Annual checkup\",\n \"billing_note\": \"Copay collected at check-in\",\n \"check_eligibility\": \"false\",\n \"complaint_type\": \"complaint-123\",\n \"prevent_double_booking\": \"true\",\n \"max_overlapping_appointments\": \"2\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"include_inactive_patients\": \"false\",\n \"new_patient\": \"true\",\n \"practice_id\": \"practice-1\",\n \"department_id\": \"department-1\",\n \"non_billable\": \"false\",\n \"recall_id\": \"recall-abc-123\",\n \"callback_urls\": [\n \"https://example.com/webhooks/cobalt\"\n ],\n \"expires_at\": \"2027-06-15T14:30:00Z\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("client_id", "<api-key>")
req.Header.Add("client_secret", "<api-key>")
req.Header.Add("access_token", "<api-key>")
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.usecobalt.com/v1/appointments")
.header("client_id", "<api-key>")
.header("client_secret", "<api-key>")
.header("access_token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"mrn\": \"12345\",\n \"patient_name\": \"Jane Doe\",\n \"location\": \"location-1\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\n \"date\": \"2026-06-15\",\n \"time\": \"2:30 pm\",\n \"timezone\": \"America/Chicago\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"type\": \"NP\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"duration\": \"30\",\n \"reason\": \"Annual checkup\",\n \"billing_note\": \"Copay collected at check-in\",\n \"check_eligibility\": \"false\",\n \"complaint_type\": \"complaint-123\",\n \"prevent_double_booking\": \"true\",\n \"max_overlapping_appointments\": \"2\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"include_inactive_patients\": \"false\",\n \"new_patient\": \"true\",\n \"practice_id\": \"practice-1\",\n \"department_id\": \"department-1\",\n \"non_billable\": \"false\",\n \"recall_id\": \"recall-abc-123\",\n \"callback_urls\": [\n \"https://example.com/webhooks/cobalt\"\n ],\n \"expires_at\": \"2027-06-15T14:30:00Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.usecobalt.com/v1/appointments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["client_id"] = '<api-key>'
request["client_secret"] = '<api-key>'
request["access_token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"mrn\": \"12345\",\n \"patient_name\": \"Jane Doe\",\n \"location\": \"location-1\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\n \"date\": \"2026-06-15\",\n \"time\": \"2:30 pm\",\n \"timezone\": \"America/Chicago\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"type\": \"NP\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"duration\": \"30\",\n \"reason\": \"Annual checkup\",\n \"billing_note\": \"Copay collected at check-in\",\n \"check_eligibility\": \"false\",\n \"complaint_type\": \"complaint-123\",\n \"prevent_double_booking\": \"true\",\n \"max_overlapping_appointments\": \"2\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"include_inactive_patients\": \"false\",\n \"new_patient\": \"true\",\n \"practice_id\": \"practice-1\",\n \"department_id\": \"department-1\",\n \"non_billable\": \"false\",\n \"recall_id\": \"recall-abc-123\",\n \"callback_urls\": [\n \"https://example.com/webhooks/cobalt\"\n ],\n \"expires_at\": \"2027-06-15T14:30:00Z\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "Appointment queued for creation",
"appointment_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"job_id": 12345
}{
"success": false,
"message": "Bad request — missing or invalid parameters"
}{
"success": false,
"message": "Unauthorized — missing or invalid credentials"
}{
"success": false,
"message": "Resource not found"
}{
"success": false,
"message": "An appointment already exists for this patient/date/time",
"appointment_id": null
}{
"success": false,
"message": "Internal server error"
}Create Appointments
Creates a new appointment for a patient.
curl --request POST \
--url https://api.usecobalt.com/v1/appointments \
--header 'Content-Type: application/json' \
--header 'access_token: <api-key>' \
--header 'client_id: <api-key>' \
--header 'client_secret: <api-key>' \
--data '
{
"mrn": "12345",
"patient_name": "Jane Doe",
"location": "location-1",
"datetime": "2026-06-15T14:30:00-07:00",
"date": "2026-06-15",
"time": "2:30 pm",
"timezone": "America/Chicago",
"provider": "provider-123",
"secondary_provider": "provider-456",
"type": "NP",
"note": "Purpose of visit: Annual checkup",
"duration": "30",
"reason": "Annual checkup",
"billing_note": "Copay collected at check-in",
"check_eligibility": "false",
"complaint_type": "complaint-123",
"prevent_double_booking": "true",
"max_overlapping_appointments": "2",
"skip_visit_type_validation": "false",
"skip_confidential_charts": "false",
"include_inactive_patients": "false",
"new_patient": "true",
"practice_id": "practice-1",
"department_id": "department-1",
"non_billable": "false",
"recall_id": "recall-abc-123",
"callback_urls": [
"https://example.com/webhooks/cobalt"
],
"expires_at": "2027-06-15T14:30:00Z"
}
'import requests
url = "https://api.usecobalt.com/v1/appointments"
payload = {
"mrn": "12345",
"patient_name": "Jane Doe",
"location": "location-1",
"datetime": "2026-06-15T14:30:00-07:00",
"date": "2026-06-15",
"time": "2:30 pm",
"timezone": "America/Chicago",
"provider": "provider-123",
"secondary_provider": "provider-456",
"type": "NP",
"note": "Purpose of visit: Annual checkup",
"duration": "30",
"reason": "Annual checkup",
"billing_note": "Copay collected at check-in",
"check_eligibility": "false",
"complaint_type": "complaint-123",
"prevent_double_booking": "true",
"max_overlapping_appointments": "2",
"skip_visit_type_validation": "false",
"skip_confidential_charts": "false",
"include_inactive_patients": "false",
"new_patient": "true",
"practice_id": "practice-1",
"department_id": "department-1",
"non_billable": "false",
"recall_id": "recall-abc-123",
"callback_urls": ["https://example.com/webhooks/cobalt"],
"expires_at": "2027-06-15T14:30:00Z"
}
headers = {
"client_id": "<api-key>",
"client_secret": "<api-key>",
"access_token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
client_id: '<api-key>',
client_secret: '<api-key>',
access_token: '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
mrn: '12345',
patient_name: 'Jane Doe',
location: 'location-1',
datetime: '2026-06-15T14:30:00-07:00',
date: '2026-06-15',
time: '2:30 pm',
timezone: 'America/Chicago',
provider: 'provider-123',
secondary_provider: 'provider-456',
type: 'NP',
note: 'Purpose of visit: Annual checkup',
duration: '30',
reason: 'Annual checkup',
billing_note: 'Copay collected at check-in',
check_eligibility: 'false',
complaint_type: 'complaint-123',
prevent_double_booking: 'true',
max_overlapping_appointments: '2',
skip_visit_type_validation: 'false',
skip_confidential_charts: 'false',
include_inactive_patients: 'false',
new_patient: 'true',
practice_id: 'practice-1',
department_id: 'department-1',
non_billable: 'false',
recall_id: 'recall-abc-123',
callback_urls: ['https://example.com/webhooks/cobalt'],
expires_at: '2027-06-15T14:30:00Z'
})
};
fetch('https://api.usecobalt.com/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.usecobalt.com/v1/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([
'mrn' => '12345',
'patient_name' => 'Jane Doe',
'location' => 'location-1',
'datetime' => '2026-06-15T14:30:00-07:00',
'date' => '2026-06-15',
'time' => '2:30 pm',
'timezone' => 'America/Chicago',
'provider' => 'provider-123',
'secondary_provider' => 'provider-456',
'type' => 'NP',
'note' => 'Purpose of visit: Annual checkup',
'duration' => '30',
'reason' => 'Annual checkup',
'billing_note' => 'Copay collected at check-in',
'check_eligibility' => 'false',
'complaint_type' => 'complaint-123',
'prevent_double_booking' => 'true',
'max_overlapping_appointments' => '2',
'skip_visit_type_validation' => 'false',
'skip_confidential_charts' => 'false',
'include_inactive_patients' => 'false',
'new_patient' => 'true',
'practice_id' => 'practice-1',
'department_id' => 'department-1',
'non_billable' => 'false',
'recall_id' => 'recall-abc-123',
'callback_urls' => [
'https://example.com/webhooks/cobalt'
],
'expires_at' => '2027-06-15T14:30:00Z'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"access_token: <api-key>",
"client_id: <api-key>",
"client_secret: <api-key>"
],
]);
$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.usecobalt.com/v1/appointments"
payload := strings.NewReader("{\n \"mrn\": \"12345\",\n \"patient_name\": \"Jane Doe\",\n \"location\": \"location-1\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\n \"date\": \"2026-06-15\",\n \"time\": \"2:30 pm\",\n \"timezone\": \"America/Chicago\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"type\": \"NP\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"duration\": \"30\",\n \"reason\": \"Annual checkup\",\n \"billing_note\": \"Copay collected at check-in\",\n \"check_eligibility\": \"false\",\n \"complaint_type\": \"complaint-123\",\n \"prevent_double_booking\": \"true\",\n \"max_overlapping_appointments\": \"2\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"include_inactive_patients\": \"false\",\n \"new_patient\": \"true\",\n \"practice_id\": \"practice-1\",\n \"department_id\": \"department-1\",\n \"non_billable\": \"false\",\n \"recall_id\": \"recall-abc-123\",\n \"callback_urls\": [\n \"https://example.com/webhooks/cobalt\"\n ],\n \"expires_at\": \"2027-06-15T14:30:00Z\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("client_id", "<api-key>")
req.Header.Add("client_secret", "<api-key>")
req.Header.Add("access_token", "<api-key>")
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.usecobalt.com/v1/appointments")
.header("client_id", "<api-key>")
.header("client_secret", "<api-key>")
.header("access_token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"mrn\": \"12345\",\n \"patient_name\": \"Jane Doe\",\n \"location\": \"location-1\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\n \"date\": \"2026-06-15\",\n \"time\": \"2:30 pm\",\n \"timezone\": \"America/Chicago\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"type\": \"NP\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"duration\": \"30\",\n \"reason\": \"Annual checkup\",\n \"billing_note\": \"Copay collected at check-in\",\n \"check_eligibility\": \"false\",\n \"complaint_type\": \"complaint-123\",\n \"prevent_double_booking\": \"true\",\n \"max_overlapping_appointments\": \"2\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"include_inactive_patients\": \"false\",\n \"new_patient\": \"true\",\n \"practice_id\": \"practice-1\",\n \"department_id\": \"department-1\",\n \"non_billable\": \"false\",\n \"recall_id\": \"recall-abc-123\",\n \"callback_urls\": [\n \"https://example.com/webhooks/cobalt\"\n ],\n \"expires_at\": \"2027-06-15T14:30:00Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.usecobalt.com/v1/appointments")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["client_id"] = '<api-key>'
request["client_secret"] = '<api-key>'
request["access_token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"mrn\": \"12345\",\n \"patient_name\": \"Jane Doe\",\n \"location\": \"location-1\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\n \"date\": \"2026-06-15\",\n \"time\": \"2:30 pm\",\n \"timezone\": \"America/Chicago\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"type\": \"NP\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"duration\": \"30\",\n \"reason\": \"Annual checkup\",\n \"billing_note\": \"Copay collected at check-in\",\n \"check_eligibility\": \"false\",\n \"complaint_type\": \"complaint-123\",\n \"prevent_double_booking\": \"true\",\n \"max_overlapping_appointments\": \"2\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"include_inactive_patients\": \"false\",\n \"new_patient\": \"true\",\n \"practice_id\": \"practice-1\",\n \"department_id\": \"department-1\",\n \"non_billable\": \"false\",\n \"recall_id\": \"recall-abc-123\",\n \"callback_urls\": [\n \"https://example.com/webhooks/cobalt\"\n ],\n \"expires_at\": \"2027-06-15T14:30:00Z\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "Appointment queued for creation",
"appointment_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"job_id": 12345
}{
"success": false,
"message": "Bad request — missing or invalid parameters"
}{
"success": false,
"message": "Unauthorized — missing or invalid credentials"
}{
"success": false,
"message": "Resource not found"
}{
"success": false,
"message": "An appointment already exists for this patient/date/time",
"appointment_id": null
}{
"success": false,
"message": "Internal server error"
}Provider and Location IDs
When creating appointments, use theehr_id values from your providers and locations:
provider: Use theehr_idfromGET /v1/providers(not theid)location: Use theehr_idfromGET /v1/locations(not theid)secondary_provider: Use theehr_idfromGET /v1/providers(not theid)
id here. The id field from GET responses is Cobalt’s internal UUID, used only for updating provider/location settings via PATCH endpoints.Date and Time Formats
You have two options for specifying the appointment date and time:Option 1: ISO 8601 Format (Recommended)
Use thedatetime parameter with full ISO 8601 timestamp including timezone:
curl -X POST https://api.usecobalt.com/v1/appointments \
-H 'Content-Type: application/json' \
-H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \
-H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \
-H 'access_token: 493JKLHIU98789hLKH9HHJH' \
-d '{
"mrn": "123456789",
"location": "123456789",
"datetime": "2025-01-01T10:00:00-05:00",
"provider": "123456789",
"type": "new_patient"
}'
YYYY-MM-DDTHH:mm:ss±HH:MM (24-hour time with timezone offset)
Option 2: Separate Date and Time
Usedate and time parameters separately. Important: When using this option, the time must be in 12-hour format with am or pm.
curl -X POST https://api.usecobalt.com/v1/appointments \
-H 'Content-Type: application/json' \
-H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \
-H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \
-H 'access_token: 493JKLHIU98789hLKH9HHJH' \
-d '{
"mrn": "123456789",
"location": "123456789",
"date": "2025-01-01",
"time": "10:00 am",
"provider": "123456789",
"type": "new_patient"
}'
YYYY-MM-DD
Time Format: h:mm am/pm or hh:mm am/pm (12-hour format, must include am/pm)
"time": "10:00" without am or pm will result in an error. Always include am or pm when using separate date and time parameters.Booking Timezone
By default the appointment time is interpreted in the account’s timezone. For EHR instances whose facilities span multiple timezones, pass an optionaltimezone (IANA name) to book the appointment at that facility’s local wall-clock time. The time you send (via datetime or date and time) is then treated as local to that zone.
curl -X POST https://api.usecobalt.com/v1/appointments \
-H 'Content-Type: application/json' \
-H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \
-H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \
-H 'access_token: 493JKLHIU98789hLKH9HHJH' \
-d '{
"mrn": "123456789",
"location": "123456789",
"date": "2025-01-01",
"time": "9:20 am",
"timezone": "America/Chicago",
"provider": "123456789",
"type": "new_patient"
}'
Preventing Double Bookings
Setprevent_double_booking to "true" to have Cobalt check the provider’s schedule for conflicts before submitting the appointment to your EHR. If the requested time slot overlaps with an existing patient visit or a schedule block, the appointment fails immediately with a descriptive error rather than being submitted.
curl -X POST https://api.usecobalt.com/v1/appointments \
-H 'Content-Type: application/json' \
-H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \
-H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \
-H 'access_token: 493JKLHIU98789hLKH9HHJH' \
-d '{
"mrn": "123456789",
"location": "123456789",
"datetime": "2025-01-01T10:00:00-05:00",
"provider": "123456789",
"type": "follow_up",
"prevent_double_booking": "true"
}'
appointment.failed webhook with the conflict_type and conflict_details fields populated (see Failure Examples below).
The check reads the provider’s schedule from your EHR when the appointment is created, not from Cobalt’s synced copy. A visit overlaps if any part of it falls between the requested start and end. Cancelled and rescheduled visits do not count.
To allow more than one visit in a slot, set max_overlapping_appointments to the number of overlapping visits that should reject the appointment. For example, "2" books into a slot that holds one visit and rejects a slot that holds two. It defaults to 1 and requires prevent_double_booking to be "true". Schedule blocks always reject, whatever the limit.
Request Expiration
Cobalt retries failed appointment creates in the background, so an appointment can be created after your own timeout. If you handle the request another way once you time out, setexpires_at to an ISO 8601 timestamp with a timezone offset:
curl -X POST https://api.usecobalt.com/v1/appointments \
-H 'Content-Type: application/json' \
-H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \
-H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \
-H 'access_token: 493JKLHIU98789hLKH9HHJH' \
-d '{
"mrn": "123456789",
"location": "123456789",
"datetime": "2025-01-01T10:00:00-05:00",
"provider": "123456789",
"type": "follow_up",
"expires_at": "2027-06-15T09:00:10-05:00"
}'
appointment.failed webhook with reason code EXPIRED (see Failure Examples below).
This is an attempt deadline, not cancellation of an operation already in progress. A save or its reconciliation can complete after it, and webhook delivery also takes time. If you receive appointment.created after your timeout, the appointment exists in your EHR. The same optional field is available on other asynchronous operations; see Request Expiration.
Complete Example Request
curl -X POST https://api.usecobalt.com/v1/appointments \
-H 'Content-Type: application/json' \
-H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \
-H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \
-H 'access_token: 493JKLHIU98789hLKH9HHJH' \
-d '{
"mrn": "123456789",
"location": "123456789",
"date": "2025-01-01",
"time": "10:00 am",
"provider": "123456789",
"secondary_provider": "123456789",
"type": "new_patient",
"note": "This is a test appointment",
"reason": "Patient requested appointment for routine checkup",
"department_id": "1",
"non_billable": "false"
}'
Example Response
{
"success": true,
"message": "Appointment processing. A webhook event will be sent upon completion.",
"appointment_id": "123456789",
"job_id": 15550942
}
Webhook Notifications
When the appointment processing is complete, we will send a webhook to your registered endpoint. Here are examples of what those webhook payloads will look like:Success
{
"id": "evt_1J9X2q2eZvKYlo2Cmnopqr",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:00:00Z",
"type": "appointment.created",
"data": {
"appointment_id": "123456789",
"date_time": "2025-01-01T10:00",
"timezone": "America/New_York",
"provider_id": "emr_prov_123",
"secondary_provider_id": "emr_sec_prov_456",
"provider_name": "Dr. Smith",
"mrn": "123456789"
}
}
Failure Examples
Theappointment.failed webhook event includes a failure_reason and can contain additional fields in the data object depending on the cause of the failure.
Patient Not Found:
{
"id": "evt_1J9X2q2eZvKYlo2Cwxyz",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:05:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456790",
"mrn": "000000",
"failure_reason": "Patient MRN '000000' not found in eCW. Please update the MRN using the PATCH /v1/appointments/:id endpoint."
}
}
{
"id": "evt_1J9X2q2eZvKYlo2Cabcde",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:10:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456791",
"mrn": "123456789",
"failure_reason": "Visit type \"Annual Wellness Visit\" not available for the selected resource/provider/location combination in eCW. Allowed visit types for this resource: New Patient, Follow Up, Telehealth. Please update the visit type using the PATCH /v1/appointments/:id endpoint, or re-create with skip_visit_type_validation: \"true\" if this visit type is bookable in eCW (e.g. via availability slots).",
"visit_type": "Annual Wellness Visit",
"allowed_visit_types": ["New Patient", "Follow Up", "Telehealth"]
}
}
skip_visit_type_validation set to "true" and an explicit duration.
Confidential Chart (when skip_confidential_charts is "true"):
{
"id": "evt_1J9X2q2eZvKYlo2Cklmno",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:12:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456793",
"mrn": "123456789",
"failure_reason": "Appointment not created: the patient chart is confidential in eCW and skip_confidential_charts is \"true\".",
"reason_code": "confidential_chart"
}
}
expires_at time passed before the appointment could be saved):
{
"id": "evt_1J9X2q2eZvKYlo2Cpqrst",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:13:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456794",
"patient_mrn": "123456789",
"failure_reason": "Appointment not created: the request expired (expires_at) before the appointment could be saved.",
"reasons": [
{
"code": "EXPIRED",
"description": "Appointment not created: the request expired (expires_at) before the appointment could be saved."
}
]
}
}
{
"id": "evt_1J9X2q2eZvKYlo2Cfghij",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:15:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456792",
"mrn": "123456789",
"failure_reason": "Provider 'Dr. Unknown' not found in eCW. Please check the provider configuration or use PATCH /v1/appointments/:id endpoint to update.",
"provider_name": "Dr. Unknown"
}
}
{
"id": "evt_1J9X2q2eZvKYlo2Cklmno",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:20:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456793",
"mrn": "123456789",
"failure_reason": "Appointment creation failed: The appointment conflicts with a schedule block (Admin Block 9am-12pm). Permission denied. Please choose a different time or resolve the schedule conflict.",
"conflict_type": "schedule_block",
"block_details": "Admin Block 9am-12pm"
}
}
{
"id": "evt_1J9X2q2eZvKYlo2Cpqrst",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:25:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456794",
"mrn": "123456789",
"failure_reason": "Appointment creation failed: This appointment conflicts with another existing appointment. Permission denied. Please choose a different time.",
"conflict_type": "appointment_conflict"
}
}
{
"id": "evt_1J9X2q2eZvKYlo2Cuvwxy",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:28:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456796",
"patient_mrn": "123456789",
"failure_reason": "Provider already has an appointment with Doe, Jane from 10:00 AM to 10:15 AM",
"conflict_type": "existing_patient_visit",
"conflict_details": {
"patient_name": "Doe, Jane",
"start_time": "10:00 AM",
"end_time": "10:15 AM",
"visit_type": "F/U",
"max_overlapping_appointments": 1,
"overlapping_appointments": [
{
"patient_name": "Doe, Jane",
"start_time": "10:00 AM",
"end_time": "10:15 AM",
"visit_type": "F/U"
}
]
}
}
}
{
"id": "evt_1J9X2q2eZvKYlo2Cuvwxz",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:29:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456797",
"patient_mrn": "123456789",
"failure_reason": "Provider has a scheduled block (Admin Block) from 9:00 AM to 12:00 PM",
"conflict_type": "scheduled_block",
"conflict_details": {
"block_description": "Admin Block",
"start_time": "9:00 AM",
"end_time": "12:00 PM"
}
}
}
{
"id": "evt_1J9X2q2eZvKYlo2Cuvwxyz",
"access_token_reference_id": "user_1J9X2q2eZvKYlo2Cstuv",
"object": "event",
"created": "2023-10-28T11:30:00Z",
"type": "appointment.failed",
"data": {
"appointment_id": "123456795",
"mrn": "123456789",
"failure_reason": "Appointment creation failed after 3 attempts. Last error: Some internal error message. Please check logs or contact support."
}
}
Authorizations
Body
Patient MRN. One of mrn or patient_name is required (bespoke check).
"12345"
Patient name, used when the MRN is not known.
"Jane Doe"
EMR location ID. Validated against the account's locations.
"location-1"
Appointment start as an ISO 8601 datetime. Provide either datetime, or date and time.
"2026-06-15T14:30:00-07:00"
Appointment date (YYYY-MM-DD). Used together with time.
"2026-06-15"
Appointment time (h:mm am/pm). Used together with date.
"2:30 pm"
IANA time zone name (e.g. "America/Chicago") to book the appointment in, overriding the account timezone. Use for eClinicalWorks instances whose facilities span multiple time zones: the datetime (or date and time) is interpreted in this zone and booked at that exact facility-local wall-clock time. Omit to use the account timezone.
"America/Chicago"
Rendering provider EMR ID. Validated against the account's providers.
"provider-123"
Secondary provider / resource EMR ID. Sent to the EMR by eClinicalWorks and PTPracticePro only; for every EMR it is stored and counts toward that provider's availability.
"provider-456"
Visit type code. Validated against the account's visit types.
"NP"
Appointment note as a plain string.
"Purpose of visit: Annual checkup"
Appointment duration in minutes, as a positive integer.
"30"
Reason for the visit.
"Annual checkup"
Billing note to set on the appointment. Read it back with the appointment_notes include on the fetch endpoints.
"Copay collected at check-in"
Whether to run an eligibility check on creation. Accepted, but no EMR uses it, so it has no effect.
true, false "false"
Complaint type ID. Validated against the account's complaint types.
"complaint-123"
Whether to reject the appointment if the slot is already booked.
true, false "true"
With prevent_double_booking, reject only when the requested time already overlaps this many patient visits for the provider. A visit counts if any part of it falls inside the requested start to end; cancelled and rescheduled visits do not count. Schedule blocks always reject. Defaults to 1.
"2"
Skip visit-type validation. Requires duration, since the visit type is not read.
true, false "false"
Fail the appointment instead of creating it when the patient chart is confidential (restricted) for the connected user.
true, false "false"
Also match a patient chart that is inactive in the EMR when looking up the patient. By default only active charts are matched.
true, false "false"
Whether the patient is new.
true, false "true"
eClinicalWorks practice ID (for multi-practice provider/location pairs).
"practice-1"
eClinicalWorks department ID, validated against the synced departments.
"department-1"
Whether the appointment is non-billable.
true, false "false"
Cobalt recall ID to link the new appointment to (from GET /v1/recalls).
"recall-abc-123"
URL(s) to receive the completion webhook for this appointment, in addition to the account webhook.
["https://example.com/webhooks/cobalt"]
Optional ISO 8601 timestamp with an explicit timezone offset, in the future when submitted. Cobalt starts no new operation attempt after this deadline and reports reasons[].code EXPIRED. An attempt already in progress, including write reconciliation, may finish after it.
"2027-06-15T14:30:00Z"