updateAppointment
This interface updates an existing appointment in the connected CRM. It is called when a user edits an appointment from within App Connect's appointment panel.
Input parameters
| Parameter | Description |
|---|---|
user |
An object describing the Chrome extension user associated with the action that triggered this interface. |
authHeader |
The HTTP Authorization header to be transmitted with the API request to the target CRM. |
appointmentId |
The CRM ID of the appointment to update. |
patchBody |
An object containing the fields to update. See Patch body schema. |
Patch body schema
| Property | Type | Description |
|---|---|---|
title |
string | New title for the appointment. |
summary |
string | New notes or body text. |
startTimeUtc |
string | Updated ISO-8601 UTC start time. |
durationMinutes |
number | Updated duration in minutes. |
contacts |
array | (Optional) Replacement list of attendee IDs or contact objects. When provided, existing attendees not in this list are removed. |
The HTTP client sends these fields inside a patch wrapper:
{
"patch": {
"title": "Updated meeting",
"summary": "Discuss next steps",
"startTimeUtc": "2026-10-26T12:00:00.000Z",
"durationMinutes": 60,
"contacts": [{ "id": "2288275906", "type": "contact" }]
}
}
Send startTimeUtc and durationMinutes together when changing the time. Omit
both for edits that only change other fields. The server continues to accept the
deprecated startTime input for older clients; current clients send startTimeUtc.
Contact IDs may be non-empty strings, including UUIDs and alphanumeric CRM IDs, or positive integers. String IDs are not required to be numeric.
participantName, attendees, attendeeIds, contactId, and contactType are
display/response fields, not appointment patch fields. Use contacts for attendee
changes. Send a changed status through the separate status, confirm, or cancel
endpoint, rather than including status in the patch.
Return value(s)
An object with the following property:
| Parameter | Description |
|---|---|
appointment |
The full updated appointment object. |
If the appointment cannot be found or updated, return:
| Parameter | Description |
|---|---|
successful |
false |
returnMessage |
An object with message, messageType, and ttl. |
Example
return {
appointment: {
id: "12345",
thirdPartyAppointmentId: "12345",
title: "Updated intake call",
description: "Rescheduled",
startTimeUtc: "2024-03-16T10:00:00.000Z",
durationMinutes: 30,
status: "scheduled",
contactId: "",
attendees: []
}
};
Reference
headers: { 'Authorization': authHeader },
params: {
fields: 'id,summary,description,start_at,end_at,attendees{id,name,type},external_properties,calendar_owner_id'
}
}
);
return res?.data?.data ?? null;
}
async function upsertCalendarEntryExternalProperty({ user, authHeader, appointmentId, name, value }) {
const existing = await getCalendarEntryById({ user, authHeader, appointmentId });
const props = buildExternalPropertiesMap(existing?.external_properties);
const existingProp = props[name];
const externalPropertyPayload = existingProp?.id != null
? [{ id: existingProp.id, name, value: `${value}` }]
: [{ name, value: `${value}` }];
await axios.patch(
`https://${user.hostname}/api/v4/calendar_entries/${appointmentId}.json`,
{ data: { external_properties: externalPropertyPayload } },
{ headers: { 'Authorization': authHeader } }
);
}
async function listAppointments({ user, authHeader, range, mineOnly }) {
const listRes = await axios.get(
`https://${user.hostname}/api/v4/calendar_entries.json`,
{
headers: { 'Authorization': authHeader },
params: {
fields: 'id,summary,start_at,end_at,description,attendees{id,name,type}'
}
}
);
const entries = listRes?.data?.data ?? [];
const appointments = entries.map(e => {
const startUtc = e?.start_at ? moment.parseZone(e.start_at).utc() : null;
const endUtc = e?.end_at ? moment.parseZone(e.end_at).utc() : null;
const durationMinutes = (startUtc && endUtc)
? Math.max(0, Math.round(endUtc.diff(startUtc, 'minutes', true)))
: 0;
const id = e?.id != null ? `${e.id}` : null;
const attendees = (e?.attendees ?? [])
.map(a => (a?.id != null ? { id: a?.id, name: a?.name, type: a?.type } : null))
.filter(Boolean);
return {
thirdPartyAppointmentId: id,
id,
title: e?.summary ?? '',
description: e?.description ?? '',
participantName: '',
startTimeUtc: startUtc ? startUtc.toISOString() : null,
durationMinutes,
status: 'scheduled',
contactId: '',
attendees
};
});
const hasCustomRange = range
&& typeof range === 'object'
&& !Array.isArray(range)
&& typeof range.startDate === 'string'
&& typeof range.endDate === 'string';
if (!hasCustomRange) {
return { appointments };
}
const rangeStart = moment.utc(range.startDate).startOf('day');
const rangeEnd = moment.utc(range.endDate).endOf('day');
if (!rangeStart.isValid() || !rangeEnd.isValid()) {
return { appointments };
}
return {
appointments: appointments.filter(appointment => {
if (!appointment.startTimeUtc) return false;
const appointmentStart = moment.utc(appointment.startTimeUtc);
return appointmentStart.isValid()
&& appointmentStart.valueOf() >= rangeStart.valueOf()
&& appointmentStart.valueOf() <= rangeEnd.valueOf();