Skip to content

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.

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

    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();
        })
    };
}

async function createAppointment({ user, authHeader, payload }) {
    const calendarId = await getWriteableUserCalendarId({ user, authHeader });
    if (calendarId == null) {
        return {
            successful: false,
            returnMessage: {
                message: 'No writeable calendar found in Clio.',
                messageType: 'warning',
                ttl: 5000
            }
        };
    }

    const startAt = payload?.startTimeUtc ?? payload?.startTime ?? null;
    const durationMinutes = Number(payload?.durationMinutes ?? 0);
    const endAt = startAt ? moment.utc(startAt).add(durationMinutes, 'minutes').toISOString() : null;

    const toAttendee = (id) => {
        const n = typeof id === 'number' ? id : Number(id);
        if (!Number.isFinite(n)) return null;
        return { id: n, type: 'Contact' };
    };

    const attendees = (() => {
        if (Array.isArray(payload?.contacts) && payload.contacts.length) {
            return payload.contacts
                .map(c => (c && typeof c === 'object' ? toAttendee(c.id) : toAttendee(c)))
                .filter(Boolean);
        }
        return [];
    })();

    const data = {
        calendar_owner: { id: calendarId },
        summary: payload?.title ?? payload?.summary ?? 'Appointment',
        description: payload?.summary ?? '',
        start_at: startAt,
        end_at: endAt,
        send_email_notification: false,
        ...(attendees.length ? { attendees } : {})
    };

    const body = { data };

    const createRes = await axios.post(
        `https://${user.hostname}/api/v4/calendar_entries.json`,
        body,
        { headers: { 'Authorization': authHeader }, params: { fields: 'id,summary,description,start_at,end_at,attendees,external_properties,calendar_owner_id' } }
    );

    const calendarEntry = createRes?.data?.data ?? null;
    const appointment = normalizeCalendarEntryToAppointment(calendarEntry);
    return { appointmentId: appointment.id, appointment };
}

async function updateAppointment({ user, authHeader, appointmentId, patchBody }) {
    const hasPatchField = (field) => Object.prototype.hasOwnProperty.call(patchBody ?? {}, field);
    const hasSupportedMutation = ['title', 'summary', 'startTimeUtc', 'startTime', 'contacts']
        .some(hasPatchField);
    if (hasPatchField('status') && !hasSupportedMutation) {
        return {
            successful: false,
            returnMessage: {
                message: 'Clio does not support appointment status changes.',