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 <j.auyeung419@gmail.com>

* fix: Remove return statement to match void return type in deleteEvent

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.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 <j.auyeung419@gmail.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 <j.auyeung419@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Joe Au-Yeung
2025-10-16 03:38:10 +00:00
committed by GitHub
co-authored by Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 02fdc1361c
commit bb9c4bb8f3
+9 -8
View File
@@ -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<any> {
const auth = await this.auth;
await auth.getToken();
@@ -214,7 +215,7 @@ export default class HubspotCalendarService implements CRM {
async deleteEvent(uid: string): Promise<void> {
const auth = await this.auth;
await auth.getToken();
return await this.hubspotDeleteMeeting(uid);
await this.hubspotCancelMeeting(uid);
}
async getContacts({ emails }: { emails: string | string[] }): Promise<Contact[]> {