curl --request PATCH \
--url https://api.usecobalt.com/v1/appointments/{id} \
--header 'Content-Type: application/json' \
--header 'access_token: <api-key>' \
--header 'client_id: <api-key>' \
--header 'client_secret: <api-key>' \
--data '
{
"status": "cancelled",
"note": "Purpose of visit: Annual checkup",
"cancellation_reason": "Patient Illness",
"confirmation_method": "LPV",
"type": "NP",
"duration": "30",
"skip_visit_type_validation": "false",
"skip_confidential_charts": "false",
"provider": "provider-123",
"secondary_provider": "provider-456",
"practice_id": "practice-1",
"mrn": "12345",
"datetime": "2026-06-15T14:30:00-07:00",
"callback_urls": [
"https://example.com/webhooks/cobalt"
],
"expires_at": "2027-06-15T14:30:00Z"
}
'import requests
url = "https://api.usecobalt.com/v1/appointments/{id}"
payload = {
"status": "cancelled",
"note": "Purpose of visit: Annual checkup",
"cancellation_reason": "Patient Illness",
"confirmation_method": "LPV",
"type": "NP",
"duration": "30",
"skip_visit_type_validation": "false",
"skip_confidential_charts": "false",
"provider": "provider-123",
"secondary_provider": "provider-456",
"practice_id": "practice-1",
"mrn": "12345",
"datetime": "2026-06-15T14:30:00-07:00",
"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.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {
client_id: '<api-key>',
client_secret: '<api-key>',
access_token: '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
status: 'cancelled',
note: 'Purpose of visit: Annual checkup',
cancellation_reason: 'Patient Illness',
confirmation_method: 'LPV',
type: 'NP',
duration: '30',
skip_visit_type_validation: 'false',
skip_confidential_charts: 'false',
provider: 'provider-123',
secondary_provider: 'provider-456',
practice_id: 'practice-1',
mrn: '12345',
datetime: '2026-06-15T14:30:00-07:00',
callback_urls: ['https://example.com/webhooks/cobalt'],
expires_at: '2027-06-15T14:30:00Z'
})
};
fetch('https://api.usecobalt.com/v1/appointments/{id}', 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/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'status' => 'cancelled',
'note' => 'Purpose of visit: Annual checkup',
'cancellation_reason' => 'Patient Illness',
'confirmation_method' => 'LPV',
'type' => 'NP',
'duration' => '30',
'skip_visit_type_validation' => 'false',
'skip_confidential_charts' => 'false',
'provider' => 'provider-123',
'secondary_provider' => 'provider-456',
'practice_id' => 'practice-1',
'mrn' => '12345',
'datetime' => '2026-06-15T14:30:00-07:00',
'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/{id}"
payload := strings.NewReader("{\n \"status\": \"cancelled\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"cancellation_reason\": \"Patient Illness\",\n \"confirmation_method\": \"LPV\",\n \"type\": \"NP\",\n \"duration\": \"30\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"practice_id\": \"practice-1\",\n \"mrn\": \"12345\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\n \"callback_urls\": [\n \"https://example.com/webhooks/cobalt\"\n ],\n \"expires_at\": \"2027-06-15T14:30:00Z\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.usecobalt.com/v1/appointments/{id}")
.header("client_id", "<api-key>")
.header("client_secret", "<api-key>")
.header("access_token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"status\": \"cancelled\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"cancellation_reason\": \"Patient Illness\",\n \"confirmation_method\": \"LPV\",\n \"type\": \"NP\",\n \"duration\": \"30\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"practice_id\": \"practice-1\",\n \"mrn\": \"12345\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\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/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.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 \"status\": \"cancelled\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"cancellation_reason\": \"Patient Illness\",\n \"confirmation_method\": \"LPV\",\n \"type\": \"NP\",\n \"duration\": \"30\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"practice_id\": \"practice-1\",\n \"mrn\": \"12345\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\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 update queued",
"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": "Internal server error"
}Update Appointment
Updates an existing appointment. The update process differs based on the current status of the appointment.
curl --request PATCH \
--url https://api.usecobalt.com/v1/appointments/{id} \
--header 'Content-Type: application/json' \
--header 'access_token: <api-key>' \
--header 'client_id: <api-key>' \
--header 'client_secret: <api-key>' \
--data '
{
"status": "cancelled",
"note": "Purpose of visit: Annual checkup",
"cancellation_reason": "Patient Illness",
"confirmation_method": "LPV",
"type": "NP",
"duration": "30",
"skip_visit_type_validation": "false",
"skip_confidential_charts": "false",
"provider": "provider-123",
"secondary_provider": "provider-456",
"practice_id": "practice-1",
"mrn": "12345",
"datetime": "2026-06-15T14:30:00-07:00",
"callback_urls": [
"https://example.com/webhooks/cobalt"
],
"expires_at": "2027-06-15T14:30:00Z"
}
'import requests
url = "https://api.usecobalt.com/v1/appointments/{id}"
payload = {
"status": "cancelled",
"note": "Purpose of visit: Annual checkup",
"cancellation_reason": "Patient Illness",
"confirmation_method": "LPV",
"type": "NP",
"duration": "30",
"skip_visit_type_validation": "false",
"skip_confidential_charts": "false",
"provider": "provider-123",
"secondary_provider": "provider-456",
"practice_id": "practice-1",
"mrn": "12345",
"datetime": "2026-06-15T14:30:00-07:00",
"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.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {
client_id: '<api-key>',
client_secret: '<api-key>',
access_token: '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
status: 'cancelled',
note: 'Purpose of visit: Annual checkup',
cancellation_reason: 'Patient Illness',
confirmation_method: 'LPV',
type: 'NP',
duration: '30',
skip_visit_type_validation: 'false',
skip_confidential_charts: 'false',
provider: 'provider-123',
secondary_provider: 'provider-456',
practice_id: 'practice-1',
mrn: '12345',
datetime: '2026-06-15T14:30:00-07:00',
callback_urls: ['https://example.com/webhooks/cobalt'],
expires_at: '2027-06-15T14:30:00Z'
})
};
fetch('https://api.usecobalt.com/v1/appointments/{id}', 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/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'status' => 'cancelled',
'note' => 'Purpose of visit: Annual checkup',
'cancellation_reason' => 'Patient Illness',
'confirmation_method' => 'LPV',
'type' => 'NP',
'duration' => '30',
'skip_visit_type_validation' => 'false',
'skip_confidential_charts' => 'false',
'provider' => 'provider-123',
'secondary_provider' => 'provider-456',
'practice_id' => 'practice-1',
'mrn' => '12345',
'datetime' => '2026-06-15T14:30:00-07:00',
'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/{id}"
payload := strings.NewReader("{\n \"status\": \"cancelled\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"cancellation_reason\": \"Patient Illness\",\n \"confirmation_method\": \"LPV\",\n \"type\": \"NP\",\n \"duration\": \"30\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"practice_id\": \"practice-1\",\n \"mrn\": \"12345\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\n \"callback_urls\": [\n \"https://example.com/webhooks/cobalt\"\n ],\n \"expires_at\": \"2027-06-15T14:30:00Z\"\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.usecobalt.com/v1/appointments/{id}")
.header("client_id", "<api-key>")
.header("client_secret", "<api-key>")
.header("access_token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"status\": \"cancelled\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"cancellation_reason\": \"Patient Illness\",\n \"confirmation_method\": \"LPV\",\n \"type\": \"NP\",\n \"duration\": \"30\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"practice_id\": \"practice-1\",\n \"mrn\": \"12345\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\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/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.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 \"status\": \"cancelled\",\n \"note\": \"Purpose of visit: Annual checkup\",\n \"cancellation_reason\": \"Patient Illness\",\n \"confirmation_method\": \"LPV\",\n \"type\": \"NP\",\n \"duration\": \"30\",\n \"skip_visit_type_validation\": \"false\",\n \"skip_confidential_charts\": \"false\",\n \"provider\": \"provider-123\",\n \"secondary_provider\": \"provider-456\",\n \"practice_id\": \"practice-1\",\n \"mrn\": \"12345\",\n \"datetime\": \"2026-06-15T14:30:00-07:00\",\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 update queued",
"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": "Internal server error"
}id in the URL path. The {id} parameter should be the Cobalt appointment ID returned from API responses or GET endpoints, not the EHR ID.1. Updating Pending or Failed Appointments
For appointments with a status of ‘pending’ or ‘failed’, you can update any attribute of the appointment.| Parameter | Type | Required | Description |
|---|---|---|---|
| Any appointment attribute | varies | No | Any attribute of the appointment can be updated. All fields are optional. |
2. Updating Scheduled Appointments
For appointments with a status of ‘scheduled’, you can update the status, note, or visit type.status to a cancelled status (from GET /visit-statuses), then create a new appointment at the new time with POST /v1/appointments.| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | No | Must be one of the available statuses associated with the linked account. You can find the available statuses by using our GET /visit-statuses endpoint |
| note | string | No | A note associated with the appointment. This is not a progress note or addition to the patient chart. This is primarily used for admin (eg. “patient might be 5 minutes late”). |
| type | string | No | The new visit type for the appointment. Must be one of the visit types from GET /v1/visit-types, and must be available for the appointment’s provider/resource in the EHR (skippable via skip_visit_type_validation). The appointment’s duration is updated to the new visit type’s configured duration unless duration is provided. |
| duration | string | No | Only valid together with type. The new appointment duration in minutes, as a positive integer string. Overrides the new visit type’s configured duration. Required when skip_visit_type_validation is "true". |
| skip_visit_type_validation | string | No | Only valid together with type. When "true", skips the check that the visit type is configured for the appointment’s provider/resource in the EHR. Use this for visit types that are bookable in the EHR (for example via availability slots) but not part of the resource’s configured visit type list. Requires duration. |
| skip_confidential_charts | string | No | Must accompany another update field. When "true", the update fails with an appointment.update_failed webhook (reason_code: "confidential_chart") instead of being applied if the patient chart is confidential in the EHR. |
Example Request for Updating a Pending/Failed Appointment
curl -X PATCH https://api.usecobalt.com/v1/appointments/23456789 \
-H 'Content-Type: application/json' \
-H 'client_id: ci_live_198908HJDKJSH98789OHKJL' \
-H 'client_secret: cs_live_9827hofdsklOYYHJLJh' \
-H 'access_token: 493JKLHIU98789hLKH9HHJH' \
-d '{
"appointment_datetime": "2024-01-15T14:00:00-07:00",
"location": "New Clinic",
"type": "f/u"
}'
Example Response
{
"success": true,
"message": "Appointment updated successfully"
}
Authorizations
Path Parameters
Cobalt appointment ID (UUID, with or without dashes).
"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
Body
New appointment status. Validated against the account's visit statuses.
"cancelled"
Replacement appointment note.
"Purpose of visit: Annual checkup"
Reason for a cancel / no-show / reschedule. Must accompany a status change to Cancelled, Rescheduled, or No Show, and must match one of the practice's configured reasons (eClinicalWorks only).
"Patient Illness"
Confirmation method code, passed through to PracticeFusion as-is. Must accompany a status change to Confirmed (PracticeFusion only).
"LPV"
New visit type code. On a scheduled appointment, eClinicalWorks only.
"NP"
New duration in minutes. On a scheduled appointment, updatable only together with type (eClinicalWorks only).
"30"
Skip visit-type validation on a type update (eClinicalWorks only).
true, false "false"
Fail the update instead of applying it when the patient chart is confidential (restricted) for the connected user. Must accompany another update field (eClinicalWorks only).
true, false "false"
New rendering provider EMR ID. On a scheduled appointment, eClinicalWorks only.
"provider-123"
New secondary provider / resource EMR ID. On a scheduled appointment, eClinicalWorks only.
"provider-456"
eClinicalWorks practice ID.
"practice-1"
Patient MRN (retry of a failed appointment only).
"12345"
New appointment datetime, ISO 8601 (retry of a failed appointment only).
"2026-06-15T14:30:00-07:00"
URL(s) to receive the completion webhook for this update.
["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"