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.

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

    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 existing = await getCalendarEntryById({ user, authHeader, appointmentId });
    if (!existing) {
        return {
            successful: false,
            returnMessage: {
                message: 'Appointment not found in Clio.',
                messageType: 'warning',
                ttl: 5000
            }
        };
    }

    const existingAttendees = existing?.attendees ?? [];

    const startAt = patchBody?.startTimeUtc ?? patchBody?.startTime ?? null;
    const durationMinutes = Number(patchBody?.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 hasAttendeeUpdate = Array.isArray(patchBody?.contacts);

    const attendees = (() => {
        if (!hasAttendeeUpdate) return [];

        const desiredAttendeeSource = patchBody.contacts;

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

        const desiredAttendeeIdSet = new Set(desiredAttendeeRefs.map(a => `${a.id}`));

        const existingAttendeeRefs = (existingAttendees ?? [])
            .map(a => {
                if (a?.id == null) return null;
                const n = typeof a.id === 'number' ? a.id : Number(a.id);

                return { id: n, type: 'Contact' };
            })
            .filter(Boolean);

        // Only remove (destroy) those existing attendees that are NOT in the desired list.
        const removedAttendeeRefs = existingAttendeeRefs
            .filter(a => !desiredAttendeeIdSet.has(`${a.id}`))
            .map(a => ({ id: a.id, type: 'Contact', _destroy: true }));

        const mergedAttendeeRefs = [...removedAttendeeRefs, ...desiredAttendeeRefs];
        const seenKeys = new Set();
        return mergedAttendeeRefs.filter(a => {
            const key = `${a.id}:${a.type ?? 'Contact'}:${a._destroy ? '1' : '0'}`;
            if (seenKeys.has(key)) return false;
            seenKeys.add(key);
            return true;
        });
    })();
    const updateBody = {
        data: {
            summary: patchBody?.title ?? '',
            description: patchBody?.summary ?? '',
            start_at: startAt,