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 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.',
                messageType: 'warning',
                ttl: 5000
            }
        };
    }

    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 hasStartUpdate = hasPatchField('startTimeUtc') || hasPatchField('startTime');
    const startAt = hasStartUpdate ? patchBody?.startTimeUtc ?? patchBody?.startTime ?? null : null;
    const durationMinutes = Number(patchBody?.durationMinutes ?? 0);
    const endAt = hasStartUpdate && 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 = hasPatchField('contacts') && Array.isArray(patchBody?.contacts);