From bb9c4bb8f3f1585afcbd48a2e2faf0bc7de98b6f Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Date: Wed, 15 Oct 2025 23:38:10 -0400 Subject: [PATCH] feat: Update HubSpot meetings to CANCELLED status instead of archiving on booking cancellation (#24478) * feat: Update HubSpot meetings to CANCELLED status instead of archiving When a booking is cancelled in Cal.com, preserve the meeting record in HubSpot by updating it to a CANCELLED status rather than archiving it. This maintains the meeting history for better tracking and reporting. Changes: - Renamed hubspotDeleteMeeting to hubspotCancelMeeting - Updated the method to set hs_meeting_outcome to CANCELLED - Changed from archive() to update() API call - Removed unused CustomPublicObjectInput interface Co-Authored-By: joe@cal.com * fix: Remove return statement to match void return type in deleteEvent Co-Authored-By: joe@cal.com * refactor: Remove hs_timestamp from cancel meeting to keep it minimal Only update the meeting outcome status when cancelling, without modifying the timestamp field. Co-Authored-By: joe@cal.com * fix: Use correct HubSpot API value for canceled meetings Changed hs_meeting_outcome from "CANCELLED" to "CANCELED" to match HubSpot API specification. HubSpot uses lowercase American spelling "CANCELED" as the valid enum value for canceled meeting outcomes. Co-Authored-By: joe@cal.com --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- packages/app-store/hubspot/lib/CrmService.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/app-store/hubspot/lib/CrmService.ts b/packages/app-store/hubspot/lib/CrmService.ts index 92b1168271..e94849f34d 100644 --- a/packages/app-store/hubspot/lib/CrmService.ts +++ b/packages/app-store/hubspot/lib/CrmService.ts @@ -21,10 +21,6 @@ import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug"; import refreshOAuthTokens from "../../_utils/oauth/refreshOAuthTokens"; import type { HubspotToken } from "../api/callback"; -interface CustomPublicObjectInput extends SimplePublicObjectInput { - id?: string; -} - export default class HubspotCalendarService implements CRM { private url = ""; private integrationName = ""; @@ -117,8 +113,14 @@ export default class HubspotCalendarService implements CRM { return this.hubspotClient.crm.objects.meetings.basicApi.update(uid, simplePublicObjectInput); }; - private hubspotDeleteMeeting = async (uid: string) => { - return this.hubspotClient.crm.objects.meetings.basicApi.archive(uid); + private hubspotCancelMeeting = async (uid: string) => { + const simplePublicObjectInput: SimplePublicObjectInput = { + properties: { + hs_meeting_outcome: "CANCELED", + }, + }; + + return this.hubspotClient.crm.objects.meetings.basicApi.update(uid, simplePublicObjectInput); }; private hubspotAuth = async (credential: CredentialPayload) => { @@ -204,7 +206,6 @@ export default class HubspotCalendarService implements CRM { return await this.handleMeetingCreation(event, contacts); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any async updateEvent(uid: string, event: CalendarEvent): Promise { const auth = await this.auth; await auth.getToken(); @@ -214,7 +215,7 @@ export default class HubspotCalendarService implements CRM { async deleteEvent(uid: string): Promise { const auth = await this.auth; await auth.getToken(); - return await this.hubspotDeleteMeeting(uid); + await this.hubspotCancelMeeting(uid); } async getContacts({ emails }: { emails: string | string[] }): Promise {