diff --git a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts
index 1c61125d8b..2792214c8d 100644
--- a/apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts
+++ b/apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/user-bookings.e2e-spec.ts
@@ -2667,6 +2667,12 @@ describe("Bookings Endpoints 2024-08-13", () => {
jest
.spyOn(EventManager.prototype, "create")
.mockImplementation(() => Promise.resolve({ results: [], referencesToCreate: [] }));
+ jest
+ .spyOn(EventManager.prototype, "createAllCalendarEvents")
+ .mockImplementation(() => Promise.resolve([]));
+ jest
+ .spyOn(EventManager.prototype, "createAllCRMEvents")
+ .mockImplementation(() => Promise.resolve([]));
});
describe("platform oAuth client has calendar events enabled", () => {
@@ -2837,7 +2843,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.id).toBeDefined();
expect(data.uid).toBeDefined();
- expect(EventManager.prototype.create).toHaveBeenCalledTimes(0);
+ expect(EventManager.prototype.createAllCalendarEvents).toHaveBeenCalledTimes(0);
+ expect(EventManager.prototype.createAllCRMEvents).toHaveBeenCalledTimes(0);
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibly recurring bookings"
@@ -2867,7 +2874,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
- expect(EventManager.prototype.create).toHaveBeenCalledTimes(0);
+ expect(EventManager.prototype.createAllCalendarEvents).toHaveBeenCalledTimes(0);
+ expect(EventManager.prototype.createAllCRMEvents).toHaveBeenCalledTimes(0);
});
});
@@ -2901,7 +2909,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.id).toBeDefined();
expect(data.uid).toBeDefined();
- expect(EventManager.prototype.create).toHaveBeenCalledTimes(0);
+ expect(EventManager.prototype.createAllCalendarEvents).toHaveBeenCalledTimes(0);
+ expect(EventManager.prototype.createAllCRMEvents).toHaveBeenCalledTimes(0);
bookingThatRequiresConfirmationUid = data.uid;
} else {
throw new Error(
@@ -2926,7 +2935,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.id).toBeDefined();
expect(data.uid).toBeDefined();
- expect(EventManager.prototype.create).toHaveBeenCalledTimes(0);
+ expect(EventManager.prototype.createAllCalendarEvents).toHaveBeenCalledTimes(0);
+ expect(EventManager.prototype.createAllCRMEvents).toHaveBeenCalledTimes(0);
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibly recurring bookings"
diff --git a/apps/web/components/settings/platform/oauth-clients/oauth-client-form/index.tsx b/apps/web/components/settings/platform/oauth-clients/oauth-client-form/index.tsx
index 220b217a34..06faf2c0ec 100644
--- a/apps/web/components/settings/platform/oauth-clients/oauth-client-form/index.tsx
+++ b/apps/web/components/settings/platform/oauth-clients/oauth-client-form/index.tsx
@@ -231,6 +231,7 @@ export const OAuthClientForm = ({
+
{t("calendar_events_disabled_video_limitation")}
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index 59a8a17d91..9f3bef3843 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -4517,5 +4517,6 @@
"error_enabling_feature": "Error enabling feature. Please try again.",
"set_organizer_as_contact_owner": "Set booking organizer as contact owner",
"overwrite_existing_contact_owner": "Overwrite existing contact owner",
+ "calendar_events_disabled_video_limitation": "When calendar events are disabled, meeting links cannot be generated for apps whose link generation is tied to calendar event creation, for example, Google Meet and Microsoft Teams. Other video apps like Cal Video will continue to work.",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS":"↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
diff --git a/packages/features/bookings/lib/EventManager.ts b/packages/features/bookings/lib/EventManager.ts
index 9c216e0005..eebd9807b2 100644
--- a/packages/features/bookings/lib/EventManager.ts
+++ b/packages/features/bookings/lib/EventManager.ts
@@ -280,8 +280,15 @@ export default class EventManager {
* event will be scheduled for it as well.
*
* @param event
+ * @param options.skipCalendarEvent - When true, skips calendar event creation but still creates video meetings.
+ * This is useful for platform customers who manage their own calendar events but still want Cal.com to create
+ * video meetings for third-party video apps like Daily.co.
*/
- public async create(event: CalendarEvent): Promise {
+ public async create(
+ event: CalendarEvent,
+ options?: { skipCalendarEvent?: boolean }
+ ): Promise {
+ const { skipCalendarEvent = false } = options ?? {};
// TODO this method shouldn't be modifying the event object that's passed in
const evt = processLocation(event);
@@ -357,7 +364,9 @@ export default class EventManager {
// Some calendar libraries may edit the original event so let's clone it
const clonedCalEvent = cloneDeep(event);
// Create the calendar event with the proper video call data
- results.push(...(await this.createAllCalendarEvents(clonedCalEvent)));
+ if (!skipCalendarEvent) {
+ results.push(...(await this.createAllCalendarEvents(clonedCalEvent)));
+ }
if (evt.location === MSTeamsLocationType) {
this.updateMSTeamsVideoCallData(evt, results);
@@ -371,7 +380,7 @@ export default class EventManager {
return result.type.includes("_calendar");
};
- const createdCRMEvents = await this.createAllCRMEvents(evt);
+ const createdCRMEvents = skipCalendarEvent ? [] : await this.createAllCRMEvents(evt);
results.push(...createdCRMEvents);
diff --git a/packages/features/bookings/lib/handleConfirmation.ts b/packages/features/bookings/lib/handleConfirmation.ts
index 6f36f94264..f12a7dacf5 100644
--- a/packages/features/bookings/lib/handleConfirmation.ts
+++ b/packages/features/bookings/lib/handleConfirmation.ts
@@ -151,7 +151,7 @@ export async function handleConfirmation(args: {
const apps = eventTypeAppMetadataOptionalSchema.parse(eventTypeMetadata?.apps);
const eventManager = new EventManager(user, apps);
const areCalendarEventsEnabled = platformClientParams?.areCalendarEventsEnabled ?? true;
- const scheduleResult = areCalendarEventsEnabled ? await eventManager.create(evt) : placeholderCreatedEvent;
+ const scheduleResult = await eventManager.create(evt, { skipCalendarEvent: !areCalendarEventsEnabled });
const results = scheduleResult.results;
const metadata: AdditionalInformation = {};
const workflows = await getAllWorkflowsFromEventType(eventType, booking.userId);
diff --git a/packages/features/bookings/lib/service/RegularBookingService.ts b/packages/features/bookings/lib/service/RegularBookingService.ts
index 6ce8b12347..e6431fce26 100644
--- a/packages/features/bookings/lib/service/RegularBookingService.ts
+++ b/packages/features/bookings/lib/service/RegularBookingService.ts
@@ -2290,11 +2290,8 @@ async function handler(
// If it's not a reschedule, doesn't require confirmation and there's no price,
// Create a booking
} else if (isConfirmedByDefault) {
- // Use EventManager to conditionally use all needed integrations.
- const createManager =
- areCalendarEventsEnabled && !skipCalendarSyncTaskCreation
- ? await eventManager.create(evt)
- : placeholderCreatedEvent;
+ const shouldSkipCalendarEvents = !areCalendarEventsEnabled || skipCalendarSyncTaskCreation;
+ const createManager = await eventManager.create(evt, { skipCalendarEvent: shouldSkipCalendarEvents });
if (evt.location) {
booking.location = evt.location;
}
@@ -2479,6 +2476,8 @@ async function handler(
tracingLogger,
});
+ const webhookLocation = metadata?.videoCallUrl || evt.location;
+
const webhookData: EventPayloadType = {
...evt,
...eventTypeInfo,
@@ -2496,6 +2495,7 @@ async function handler(
status: "ACCEPTED",
smsReminderNumber: booking?.smsReminderNumber || undefined,
rescheduledBy: reqBody.rescheduledBy,
+ location: webhookLocation,
...(assignmentReason ? { assignmentReason: [assignmentReason] } : {}),
};