diff --git a/components/integrations/CalendarListContainer.tsx b/components/integrations/CalendarListContainer.tsx
index cbab1c94..f07b769e 100644
--- a/components/integrations/CalendarListContainer.tsx
+++ b/components/integrations/CalendarListContainer.tsx
@@ -1,5 +1,6 @@
-import React, { Fragment } from "react";
+import React, { Fragment, useState } from "react";
 import { useMutation } from "react-query";
+import Select from "react-select";
 
 import { QueryCell } from "@lib/QueryCell";
 import { useLocale } from "@lib/hooks/useLocale";
@@ -98,59 +99,124 @@ function ConnectedCalendarsList(props: Props) {
     <QueryCell
       query={query}
       empty={() => null}
-      success={({ data }) => (
-        <List>
-          {data.map((item) => (
-            <Fragment key={item.credentialId}>
-              {item.calendars ? (
-                <IntegrationListItem
-                  {...item.integration}
-                  description={item.primary?.externalId || "No external Id"}
-                  actions={
-                    <DisconnectIntegration
-                      id={item.credentialId}
-                      render={(btnProps) => (
-                        <Button {...btnProps} color="warn">
-                          {t("disconnect")}
-                        </Button>
-                      )}
-                      onOpenChange={props.onChanged}
-                    />
-                  }>
-                  <ul className="p-4 space-y-2">
-                    {item.calendars.map((cal) => (
-                      <CalendarSwitch
-                        key={cal.externalId}
-                        externalId={cal.externalId as string}
-                        title={cal.name as string}
-                        type={item.integration.type}
-                        defaultSelected={cal.isSelected}
+      success={({ data }) => {
+        if (!data.connectedCalendars.length) {
+          return null;
+        }
+        return (
+          <List>
+            {data.connectedCalendars.map((item) => (
+              <Fragment key={item.credentialId}>
+                {item.calendars ? (
+                  <IntegrationListItem
+                    {...item.integration}
+                    description={item.primary?.externalId || "No external Id"}
+                    actions={
+                      <DisconnectIntegration
+                        id={item.credentialId}
+                        render={(btnProps) => (
+                          <Button {...btnProps} color="warn">
+                            {t("disconnect")}
+                          </Button>
+                        )}
+                        onOpenChange={props.onChanged}
                       />
-                    ))}
-                  </ul>
-                </IntegrationListItem>
-              ) : (
-                <Alert
-                  severity="warning"
-                  title="Something went wrong"
-                  message={item.error?.message}
-                  actions={
-                    <DisconnectIntegration
-                      id={item.credentialId}
-                      render={(btnProps) => (
-                        <Button {...btnProps} color="warn">
-                          {t("disconnect")}
-                        </Button>
-                      )}
-                      onOpenChange={() => props.onChanged()}
-                    />
-                  }
-                />
-              )}
-            </Fragment>
-          ))}
-        </List>
-      )}
+                    }>
+                    <ul className="p-4 space-y-2">
+                      {item.calendars.map((cal) => (
+                        <CalendarSwitch
+                          key={cal.externalId}
+                          externalId={cal.externalId as string}
+                          title={cal.name as string}
+                          type={item.integration.type}
+                          defaultSelected={cal.isSelected}
+                        />
+                      ))}
+                    </ul>
+                  </IntegrationListItem>
+                ) : (
+                  <Alert
+                    severity="warning"
+                    title="Something went wrong"
+                    message={item.error?.message}
+                    actions={
+                      <DisconnectIntegration
+                        id={item.credentialId}
+                        render={(btnProps) => (
+                          <Button {...btnProps} color="warn">
+                            Disconnect
+                          </Button>
+                        )}
+                        onOpenChange={() => props.onChanged()}
+                      />
+                    }
+                  />
+                )}
+              </Fragment>
+            ))}
+          </List>
+        );
+      }}
+    />
+  );
+}
+
+function PrimaryCalendarSelector() {
+  const query = trpc.useQuery(["viewer.connectedCalendars"], {
+    suspense: true,
+  });
+  const [selectedOption, setSelectedOption] = useState(() => {
+    const selected = query.data?.connectedCalendars
+      .map((connected) => connected.calendars ?? [])
+      .flat()
+      .find((cal) => cal.externalId === query.data.destinationCalendar?.externalId);
+
+    if (!selected) {
+      return null;
+    }
+
+    return {
+      value: `${selected.integration}:${selected.externalId}`,
+      label: selected.name,
+    };
+  });
+
+  const mutation = trpc.useMutation("viewer.setUserDestinationCalendar");
+
+  if (!query.data?.connectedCalendars.length) {
+    return null;
+  }
+  const options =
+    query.data.connectedCalendars.map((selectedCalendar) => ({
+      key: selectedCalendar.credentialId,
+      label: `${selectedCalendar.integration.title} (${selectedCalendar.primary?.name})`,
+      options: (selectedCalendar.calendars ?? []).map((cal) => ({
+        label: cal.name || "",
+        value: `${cal.integration}:${cal.externalId}`,
+      })),
+    })) ?? [];
+  return (
+    <Select
+      name={"primarySelectedCalendar"}
+      options={options}
+      isSearchable={false}
+      className="flex-1 block w-full min-w-0 mt-1 mb-2 border-gray-300 rounded-none focus:ring-primary-500 focus:border-primary-500 rounded-r-md sm:text-sm"
+      onChange={(option) => {
+        setSelectedOption(option);
+        if (!option) {
+          return;
+        }
+
+        /* Split only the first `:`, since Apple uses the full URL as externalId */
+        const [integration, externalId] = option.value.split(/:(.+)/);
+
+        mutation.mutate({
+          integration,
+          externalId,
+        });
+      }}
+      isLoading={mutation.isLoading}
+      value={selectedOption}
     />
   );
 }
@@ -201,12 +267,20 @@ export function CalendarListContainer(props: { heading?: false }) {
       {heading && (
         <ShellSubHeading
           className="mt-10"
-          title={<SubHeadingTitleWithConnections title={t("calendar")} numConnections={query.data?.length} />}
+          title={
+            <SubHeadingTitleWithConnections
+              title="Calendars"
+              numConnections={query.data?.connectedCalendars.length}
+            />
+          }
           subtitle={t("configure_how_your_event_types_interact")}
+          actions={<div className="block"></div>}
         />
       )}
+      <p className="mr-4 text-sm text-neutral-500">{t("select_destination_calendar")}</p>
+      <PrimaryCalendarSelector />
       <ConnectedCalendarsList onChanged={onChanged} />
-      {!!query.data?.length && (
+      {!!query.data?.connectedCalendars.length && (
         <ShellSubHeading
           className="mt-6"
           title={<SubHeadingTitleWithConnections title={t("connect_an_additional_calendar")} />}
diff --git a/ee/lib/stripe/server.ts b/ee/lib/stripe/server.ts
index fd97252a..979396c8 100644
--- a/ee/lib/stripe/server.ts
+++ b/ee/lib/stripe/server.ts
@@ -1,6 +1,5 @@
-import { PaymentType } from "@prisma/client";
+import { PaymentType, Prisma } from "@prisma/client";
 import Stripe from "stripe";
-import { JsonValue } from "type-fest";
 import { v4 as uuidv4 } from "uuid";
 
 import { CalendarEvent } from "@lib/calendarClient";
@@ -39,7 +38,7 @@ export async function handlePayment(
     price: number;
     currency: string;
   },
-  stripeCredential: { key: JsonValue },
+  stripeCredential: { key: Prisma.JsonValue },
   booking: {
     user: { email: string | null; name: string | null; timeZone: string } | null;
     id: number;
@@ -74,7 +73,7 @@ export async function handlePayment(
       data: Object.assign({}, paymentIntent, {
         stripe_publishable_key,
         stripeAccount: stripe_user_id,
-      }) as PaymentData as unknown as JsonValue,
+      }) as PaymentData as unknown as Prisma.JsonValue,
       externalId: paymentIntent.id,
     },
   });
@@ -103,7 +102,7 @@ export async function refund(
       success: boolean;
       refunded: boolean;
       externalId: string;
-      data: JsonValue;
+      data: Prisma.JsonValue;
       type: PaymentType;
     }[];
   },
@@ -113,7 +112,7 @@ export async function refund(
     const payment = booking.payment.find((e) => e.success && !e.refunded);
     if (!payment) return;
 
-    if (payment.type != PaymentType.STRIPE) {
+    if (payment.type !== PaymentType.STRIPE) {
       await handleRefundError({
         event: calEvent,
         reason: "cannot refund non Stripe payment",
diff --git a/ee/pages/api/integrations/stripepayment/webhook.ts b/ee/pages/api/integrations/stripepayment/webhook.ts
index 459db97f..126f6998 100644
--- a/ee/pages/api/integrations/stripepayment/webhook.ts
+++ b/ee/pages/api/integrations/stripepayment/webhook.ts
@@ -57,6 +57,7 @@ async function handlePaymentSuccess(event: Stripe.Event) {
               email: true,
               name: true,
               locale: true,
+              destinationCalendar: true,
             },
           },
         },
@@ -91,7 +92,7 @@ async function handlePaymentSuccess(event: Stripe.Event) {
   if (booking.location) evt.location = booking.location;
 
   if (booking.confirmed) {
-    const eventManager = new EventManager(user.credentials);
+    const eventManager = new EventManager(user);
     const scheduleResult = await eventManager.create(evt);
 
     await prisma.booking.update({
diff --git a/jest.playwright.config.js b/jest.playwright.config.js
index e4654d46..f73715d3 100644
--- a/jest.playwright.config.js
+++ b/jest.playwright.config.js
@@ -3,6 +3,7 @@ const opts = {
   headless: !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS,
   collectCoverage: false, // not possible in Next.js 12
   executablePath: process.env.PLAYWRIGHT_CHROME_EXECUTABLE_PATH,
+  locale: "en", // So tests won't fail if local machine is not in english
 };
 
 console.log("⚙️ Playwright options:", JSON.stringify(opts, null, 4));
diff --git a/lib/BaseCalendarApiAdapter.ts b/lib/BaseCalendarApiAdapter.ts
new file mode 100644
index 00000000..2adc6a38
--- /dev/null
+++ b/lib/BaseCalendarApiAdapter.ts
@@ -0,0 +1,349 @@
+import { Credential } from "@prisma/client";
+import dayjs from "dayjs";
+import utc from "dayjs/plugin/utc";
+import ICAL from "ical.js";
+import { Attendee, createEvent, DateArray, DurationObject } from "ics";
+import {
+  createAccount,
+  createCalendarObject,
+  deleteCalendarObject,
+  fetchCalendarObjects,
+  fetchCalendars,
+  getBasicAuthHeaders,
+  updateCalendarObject,
+} from "tsdav";
+import { v4 as uuidv4 } from "uuid";
+
+import { getLocation, getRichDescription } from "@lib/CalEventParser";
+import { symmetricDecrypt } from "@lib/crypto";
+import logger from "@lib/logger";
+
+import { CalendarEvent, IntegrationCalendar } from "./calendarClient";
+
+dayjs.extend(utc);
+
+export type Person = { name: string; email: string; timeZone: string };
+
+export class BaseCalendarApiAdapter {
+  private url: string;
+  private credentials: Record<string, string>;
+  private headers: Record<string, string>;
+  private integrationName = "";
+
+  constructor(credential: Credential, integrationName: string, url?: string) {
+    const decryptedCredential = JSON.parse(
+      symmetricDecrypt(credential.key as string, process.env.CALENDSO_ENCRYPTION_KEY!)
+    );
+    const username = decryptedCredential.username;
+    const password = decryptedCredential.password;
+    this.url = url || decryptedCredential.url;
+    this.integrationName = integrationName;
+    this.credentials = { username, password };
+    this.headers = getBasicAuthHeaders({ username, password });
+  }
+
+  log = logger.getChildLogger({ prefix: [`[[lib] ${this.integrationName}`] });
+
+  convertDate(date: string): DateArray {
+    return dayjs(date)
+      .utc()
+      .toArray()
+      .slice(0, 6)
+      .map((v, i) => (i === 1 ? v + 1 : v)) as DateArray;
+  }
+
+  getDuration(start: string, end: string): DurationObject {
+    return {
+      minutes: dayjs(end).diff(dayjs(start), "minute"),
+    };
+  }
+
+  getAttendees(attendees: Person[]): Attendee[] {
+    return attendees.map(({ email, name }) => ({ name, email, partstat: "NEEDS-ACTION" }));
+  }
+
+  async createEvent(event: CalendarEvent) {
+    try {
+      const calendars = await this.listCalendars(event);
+      const uid = uuidv4();
+      /** We create local ICS files */
+      const { error, value: iCalString } = createEvent({
+        uid,
+        startInputType: "utc",
+        start: this.convertDate(event.startTime),
+        duration: this.getDuration(event.startTime, event.endTime),
+        title: event.title,
+        description: getRichDescription(event),
+        location: getLocation(event),
+        organizer: { email: event.organizer.email, name: event.organizer.name },
+        attendees: this.getAttendees(event.attendees),
+      });
+
+      if (error) throw new Error("Error creating iCalString");
+
+      if (!iCalString) throw new Error("Error creating iCalString");
+
+      /** We create the event directly on iCal */
+      await Promise.all(
+        calendars
+          .filter((c) =>
+            event.destinationCalendar?.externalId
+              ? c.externalId === event.destinationCalendar.externalId
+              : true
+          )
+          .map((calendar) =>
+            createCalendarObject({
+              calendar: {
+                url: calendar.externalId,
+              },
+              filename: `${uid}.ics`,
+              iCalString,
+              headers: this.headers,
+            })
+          )
+      );
+
+      return {
+        uid,
+        id: uid,
+        type: this.integrationName,
+        password: "",
+        url: "",
+      };
+    } catch (reason) {
+      console.error(reason);
+      throw reason;
+    }
+  }
+
+  async updateEvent(uid: string, event: CalendarEvent) {
+    try {
+      const calendars = await this.listCalendars();
+      const events = [];
+
+      for (const cal of calendars) {
+        const calEvents = await this.getEvents(cal.externalId, null, null, [`${cal.externalId}${uid}.ics`]);
+
+        for (const ev of calEvents) {
+          events.push(ev);
+        }
+      }
+
+      const { error, value: iCalString } = createEvent({
+        uid,
+        startInputType: "utc",
+        start: this.convertDate(event.startTime),
+        duration: this.getDuration(event.startTime, event.endTime),
+        title: event.title,
+        description: getRichDescription(event),
+        location: getLocation(event),
+        organizer: { email: event.organizer.email, name: event.organizer.name },
+        attendees: this.getAttendees(event.attendees),
+      });
+
+      if (error) {
+        this.log.debug("Error creating iCalString");
+        return {};
+      }
+
+      const eventsToUpdate = events.filter((event) => event.uid === uid);
+
+      return await Promise.all(
+        eventsToUpdate.map((event) => {
+          return updateCalendarObject({
+            calendarObject: {
+              url: event.url,
+              data: iCalString,
+              etag: event?.etag,
+            },
+            headers: this.headers,
+          });
+        })
+      );
+    } catch (reason) {
+      console.error(reason);
+      throw reason;
+    }
+  }
+
+  async deleteEvent(uid: string): Promise<void> {
+    try {
+      const calendars = await this.listCalendars();
+      const events = [];
+
+      for (const cal of calendars) {
+        const calEvents = await this.getEvents(cal.externalId, null, null, [`${cal.externalId}${uid}.ics`]);
+
+        for (const ev of calEvents) {
+          events.push(ev);
+        }
+      }
+
+      const eventsToUpdate = events.filter((event) => event.uid === uid);
+
+      await Promise.all(
+        eventsToUpdate.map((event) => {
+          return deleteCalendarObject({
+            calendarObject: {
+              url: event.url,
+              etag: event?.etag,
+            },
+            headers: this.headers,
+          });
+        })
+      );
+    } catch (reason) {
+      console.error(reason);
+      throw reason;
+    }
+  }
+
+  async getAvailability(dateFrom: string, dateTo: string, selectedCalendars: IntegrationCalendar[]) {
+    try {
+      const selectedCalendarIds = selectedCalendars
+        .filter((e) => e.integration === this.integrationName)
+        .map((e) => e.externalId);
+      if (selectedCalendarIds.length == 0 && selectedCalendars.length > 0) {
+        // Only calendars of other integrations selected
+        return Promise.resolve([]);
+      }
+
+      return (
+        selectedCalendarIds.length === 0
+          ? this.listCalendars().then((calendars) => calendars.map((calendar) => calendar.externalId))
+          : Promise.resolve(selectedCalendarIds)
+      ).then(async (ids: string[]) => {
+        if (ids.length === 0) {
+          return Promise.resolve([]);
+        }
+
+        return (
+          await Promise.all(
+            ids.map(async (calId) => {
+              return (await this.getEvents(calId, dateFrom, dateTo)).map((event) => {
+                return {
+                  start: event.startDate.toISOString(),
+                  end: event.endDate.toISOString(),
+                };
+              });
+            })
+          )
+        ).flatMap((event) => event);
+      });
+    } catch (reason) {
+      this.log.error(reason);
+      throw reason;
+    }
+  }
+
+  async listCalendars(event?: CalendarEvent): Promise<IntegrationCalendar[]> {
+    try {
+      const account = await this.getAccount();
+      const calendars = await fetchCalendars({
+        account,
+        headers: this.headers,
+      });
+
+      return calendars.reduce<IntegrationCalendar[]>((newCalendars, calendar) => {
+        if (!calendar.components?.includes("VEVENT")) return newCalendars;
+        newCalendars.push({
+          externalId: calendar.url,
+          name: calendar.displayName ?? "",
+          primary: event?.destinationCalendar?.externalId
+            ? event.destinationCalendar.externalId === calendar.url
+            : false,
+          integration: this.integrationName,
+        });
+        return newCalendars;
+      }, []);
+    } catch (reason) {
+      console.error(reason);
+      throw reason;
+    }
+  }
+
+  async getEvents(
+    calId: string,
+    dateFrom: string | null,
+    dateTo: string | null,
+    objectUrls?: string[] | null
+  ) {
+    try {
+      const objects = await fetchCalendarObjects({
+        calendar: {
+          url: calId,
+        },
+        objectUrls: objectUrls ? objectUrls : undefined,
+        timeRange:
+          dateFrom && dateTo
+            ? {
+                start: dayjs(dateFrom).utc().format("YYYY-MM-DDTHH:mm:ss[Z]"),
+                end: dayjs(dateTo).utc().format("YYYY-MM-DDTHH:mm:ss[Z]"),
+              }
+            : undefined,
+        headers: this.headers,
+      });
+
+      const events = objects
+        .filter((e) => !!e.data)
+        .map((object) => {
+          const jcalData = ICAL.parse(object.data);
+          const vcalendar = new ICAL.Component(jcalData);
+          const vevent = vcalendar.getFirstSubcomponent("vevent");
+          const event = new ICAL.Event(vevent);
+
+          const calendarTimezone =
+            vcalendar.getFirstSubcomponent("vtimezone")?.getFirstPropertyValue("tzid") || "";
+
+          const startDate = calendarTimezone
+            ? dayjs(event.startDate.toJSDate()).tz(calendarTimezone)
+            : new Date(event.startDate.toUnixTime() * 1000);
+          const endDate = calendarTimezone
+            ? dayjs(event.endDate.toJSDate()).tz(calendarTimezone)
+            : new Date(event.endDate.toUnixTime() * 1000);
+
+          return {
+            uid: event.uid,
+            etag: object.etag,
+            url: object.url,
+            summary: event.summary,
+            description: event.description,
+            location: event.location,
+            sequence: event.sequence,
+            startDate,
+            endDate,
+            duration: {
+              weeks: event.duration.weeks,
+              days: event.duration.days,
+              hours: event.duration.hours,
+              minutes: event.duration.minutes,
+              seconds: event.duration.seconds,
+              isNegative: event.duration.isNegative,
+            },
+            organizer: event.organizer,
+            attendees: event.attendees.map((a) => a.getValues()),
+            recurrenceId: event.recurrenceId,
+            timezone: calendarTimezone,
+          };
+        });
+
+      return events;
+    } catch (reason) {
+      console.error(reason);
+      throw reason;
+    }
+  }
+
+  private async getAccount() {
+    const account = await createAccount({
+      account: {
+        serverUrl: this.url,
+        accountType: "caldav",
+        credentials: this.credentials,
+      },
+      headers: this.headers,
+    });
+
+    return account;
+  }
+}
diff --git a/lib/CalEventParser.ts b/lib/CalEventParser.ts
index 92ba977c..b32aae68 100644
--- a/lib/CalEventParser.ts
+++ b/lib/CalEventParser.ts
@@ -68,7 +68,7 @@ export const getLocation = (calEvent: CalendarEvent) => {
     return calEvent.additionInformation.hangoutLink;
   }
 
-  return providerName || calEvent.location;
+  return providerName || calEvent.location || "";
 };
 
 export const getManageLink = (calEvent: CalendarEvent) => {
diff --git a/lib/calendarClient.ts b/lib/calendarClient.ts
index ee6841f6..89914a00 100644
--- a/lib/calendarClient.ts
+++ b/lib/calendarClient.ts
@@ -1,5 +1,4 @@
-/* eslint-disable @typescript-eslint/ban-ts-comment */
-import { Credential, SelectedCalendar } from "@prisma/client";
+import { Credential, DestinationCalendar, SelectedCalendar } from "@prisma/client";
 import { TFunction } from "next-i18next";
 
 import { PaymentInfo } from "@ee/lib/stripe/server";
@@ -9,16 +8,14 @@ import { Event, EventResult } from "@lib/events/EventManager";
 import { AppleCalendar } from "@lib/integrations/Apple/AppleCalendarAdapter";
 import { CalDavCalendar } from "@lib/integrations/CalDav/CalDavCalendarAdapter";
 import {
-  GoogleCalendarApiAdapter,
   ConferenceData,
+  GoogleCalendarApiAdapter,
 } from "@lib/integrations/GoogleCalendar/GoogleCalendarApiAdapter";
-import {
-  Office365CalendarApiAdapter,
-  BufferedBusyTime,
-} from "@lib/integrations/Office365Calendar/Office365CalendarApiAdapter";
+import { Office365CalendarApiAdapter } from "@lib/integrations/Office365Calendar/Office365CalendarApiAdapter";
 import logger from "@lib/logger";
 import { VideoCallData } from "@lib/videoClient";
 
+import notEmpty from "./notEmpty";
 import { Ensure } from "./types/utils";
 
 const log = logger.getChildLogger({ prefix: ["[lib] calendarClient"] });
@@ -61,6 +58,7 @@ export interface CalendarEvent {
   uid?: string | null;
   videoCallData?: VideoCallData;
   paymentInfo?: PaymentInfo | null;
+  destinationCalendar?: DestinationCalendar | null;
 }
 
 export interface IntegrationCalendar extends Ensure<Partial<SelectedCalendar>, "externalId"> {
@@ -68,6 +66,8 @@ export interface IntegrationCalendar extends Ensure<Partial<SelectedCalendar>, "
   name?: string;
 }
 
+type EventBusyDate = Record<"start" | "end", Date | string>;
+
 export interface CalendarApiAdapter {
   createEvent(event: CalendarEvent): Promise<Event>;
 
@@ -79,7 +79,7 @@ export interface CalendarApiAdapter {
     dateFrom: string,
     dateTo: string,
     selectedCalendars: IntegrationCalendar[]
-  ): Promise<BufferedBusyTime[]>;
+  ): Promise<EventBusyDate[]>;
 
   listCalendars(): Promise<IntegrationCalendar[]>;
 }
@@ -98,72 +98,32 @@ function getCalendarAdapterOrNull(credential: Credential): CalendarApiAdapter |
   return null;
 }
 
-/**
- * @deprecated
- */
-const calendars = (withCredentials: Credential[]): CalendarApiAdapter[] =>
-  withCredentials
-    .map((cred) => {
-      switch (cred.type) {
-        case "google_calendar":
-          return GoogleCalendarApiAdapter(cred);
-        case "office365_calendar":
-          return Office365CalendarApiAdapter(cred);
-        case "caldav_calendar":
-          return new CalDavCalendar(cred);
-        case "apple_calendar":
-          return new AppleCalendar(cred);
-        default:
-          return; // unknown credential, could be legacy? In any case, ignore
-      }
-    })
-    .flatMap((item) => (item ? [item as CalendarApiAdapter] : []));
-
-const getBusyCalendarTimes = (
+const getBusyCalendarTimes = async (
   withCredentials: Credential[],
   dateFrom: string,
   dateTo: string,
   selectedCalendars: SelectedCalendar[]
-) =>
-  Promise.all(
-    calendars(withCredentials).map((c) => c.getAvailability(dateFrom, dateTo, selectedCalendars))
-  ).then((results) => {
-    return results.reduce((acc, availability) => acc.concat(availability), []);
-  });
-
-/**
- *
- * @param withCredentials
- * @deprecated
- */
-const listCalendars = (withCredentials: Credential[]) =>
-  Promise.all(calendars(withCredentials).map((c) => c.listCalendars())).then((results) =>
-    results.reduce((acc, calendars) => acc.concat(calendars), []).filter((c) => c != null)
+) => {
+  const adapters = withCredentials.map(getCalendarAdapterOrNull).filter(notEmpty);
+  const results = await Promise.all(
+    adapters.map((c) => c.getAvailability(dateFrom, dateTo, selectedCalendars))
   );
+  return results.reduce((acc, availability) => acc.concat(availability), []);
+};
 
 const createEvent = async (credential: Credential, calEvent: CalendarEvent): Promise<EventResult> => {
   const uid: string = getUid(calEvent);
+  const adapter = getCalendarAdapterOrNull(credential);
   let success = true;
 
-  const creationResult = credential
-    ? await calendars([credential])[0]
-        .createEvent(calEvent)
-        .catch((e) => {
-          log.error("createEvent failed", e, calEvent);
-          success = false;
-          return undefined;
-        })
+  const creationResult = adapter
+    ? await adapter.createEvent(calEvent).catch((e) => {
+        log.error("createEvent failed", e, calEvent);
+        success = false;
+        return undefined;
+      })
     : undefined;
 
-  if (!creationResult) {
-    return {
-      type: credential.type,
-      success,
-      uid,
-      originalEvent: calEvent,
-    };
-  }
-
   return {
     type: credential.type,
     success,
@@ -179,28 +139,18 @@ const updateEvent = async (
   bookingRefUid: string | null
 ): Promise<EventResult> => {
   const uid = getUid(calEvent);
+  const adapter = getCalendarAdapterOrNull(credential);
   let success = true;
 
   const updatedResult =
-    credential && bookingRefUid
-      ? await calendars([credential])[0]
-          .updateEvent(bookingRefUid, calEvent)
-          .catch((e) => {
-            log.error("updateEvent failed", e, calEvent);
-            success = false;
-            return undefined;
-          })
+    adapter && bookingRefUid
+      ? await adapter.updateEvent(bookingRefUid, calEvent).catch((e) => {
+          log.error("updateEvent failed", e, calEvent);
+          success = false;
+          return undefined;
+        })
       : undefined;
 
-  if (!updatedResult) {
-    return {
-      type: credential.type,
-      success,
-      uid,
-      originalEvent: calEvent,
-    };
-  }
-
   return {
     type: credential.type,
     success,
@@ -211,18 +161,12 @@ const updateEvent = async (
 };
 
 const deleteEvent = (credential: Credential, uid: string): Promise<unknown> => {
-  if (credential) {
-    return calendars([credential])[0].deleteEvent(uid);
+  const adapter = getCalendarAdapterOrNull(credential);
+  if (adapter) {
+    return adapter.deleteEvent(uid);
   }
 
   return Promise.resolve({});
 };
 
-export {
-  getBusyCalendarTimes,
-  createEvent,
-  updateEvent,
-  deleteEvent,
-  listCalendars,
-  getCalendarAdapterOrNull,
-};
+export { getBusyCalendarTimes, createEvent, updateEvent, deleteEvent, getCalendarAdapterOrNull };
diff --git a/lib/events/EventManager.ts b/lib/events/EventManager.ts
index 790e6e2e..f6bf7206 100644
--- a/lib/events/EventManager.ts
+++ b/lib/events/EventManager.ts
@@ -1,4 +1,4 @@
-import { Credential } from "@prisma/client";
+import { Credential, DestinationCalendar } from "@prisma/client";
 import async from "async";
 import merge from "lodash/merge";
 import { v5 as uuidv5 } from "uuid";
@@ -86,18 +86,22 @@ export const processLocation = (event: CalendarEvent): CalendarEvent => {
   return event;
 };
 
+type EventManagerUser = {
+  credentials: Credential[];
+  destinationCalendar: DestinationCalendar | null;
+};
 export default class EventManager {
-  calendarCredentials: Array<Credential>;
-  videoCredentials: Array<Credential>;
+  calendarCredentials: Credential[];
+  videoCredentials: Credential[];
 
   /**
    * Takes an array of credentials and initializes a new instance of the EventManager.
    *
    * @param credentials
    */
-  constructor(credentials: Array<Credential>) {
-    this.calendarCredentials = credentials.filter((cred) => cred.type.endsWith("_calendar"));
-    this.videoCredentials = credentials.filter((cred) => cred.type.endsWith("_video"));
+  constructor(user: EventManagerUser) {
+    this.calendarCredentials = user.credentials.filter((cred) => cred.type.endsWith("_calendar"));
+    this.videoCredentials = user.credentials.filter((cred) => cred.type.endsWith("_video"));
 
     //for  Daily.co video, temporarily pushes a credential for the daily-video-client
     const hasDailyIntegration = process.env.DAILY_API_KEY;
@@ -180,6 +184,7 @@ export default class EventManager {
             meetingUrl: true,
           },
         },
+        destinationCalendar: true,
       },
     });
 
@@ -194,6 +199,7 @@ export default class EventManager {
       const result = await this.updateVideoEvent(evt, booking);
       if (result.updatedEvent) {
         evt.videoCallData = result.updatedEvent;
+        evt.location = result.updatedEvent.url;
       }
       results.push(result);
     }
@@ -240,13 +246,21 @@ export default class EventManager {
    * @param noMail
    * @private
    */
-
   private async createAllCalendarEvents(event: CalendarEvent): Promise<Array<EventResult>> {
-    const [firstCalendar] = this.calendarCredentials;
-    if (!firstCalendar) {
+    /** Can I use destinationCalendar here? */
+    /* How can I link a DC to a cred? */
+    if (event.destinationCalendar) {
+      const destinationCalendarCredentials = this.calendarCredentials.filter(
+        (c) => c.type === event.destinationCalendar?.integration
+      );
+      return Promise.all(destinationCalendarCredentials.map(async (c) => await createEvent(c, event)));
+    }
+
+    const [credential] = this.calendarCredentials;
+    if (!credential) {
       return [];
     }
-    return [await createEvent(firstCalendar, event)];
+    return [await createEvent(credential, event)];
   }
 
   /**
diff --git a/lib/integrations/Apple/AppleCalendarAdapter.ts b/lib/integrations/Apple/AppleCalendarAdapter.ts
index f23187f6..61d25839 100644
--- a/lib/integrations/Apple/AppleCalendarAdapter.ts
+++ b/lib/integrations/Apple/AppleCalendarAdapter.ts
@@ -1,346 +1,10 @@
 import { Credential } from "@prisma/client";
-import dayjs from "dayjs";
-import utc from "dayjs/plugin/utc";
-import ICAL from "ical.js";
-import { createEvent, DurationObject, Attendee, Person } from "ics";
-import {
-  createAccount,
-  fetchCalendars,
-  fetchCalendarObjects,
-  getBasicAuthHeaders,
-  createCalendarObject,
-  updateCalendarObject,
-  deleteCalendarObject,
-} from "tsdav";
-import { v4 as uuidv4 } from "uuid";
 
-import { getLocation, getRichDescription } from "@lib/CalEventParser";
-import { symmetricDecrypt } from "@lib/crypto";
-import logger from "@lib/logger";
-
-import { IntegrationCalendar, CalendarApiAdapter, CalendarEvent } from "../../calendarClient";
-
-dayjs.extend(utc);
-
-const log = logger.getChildLogger({ prefix: ["[[lib] apple calendar"] });
-
-export class AppleCalendar implements CalendarApiAdapter {
-  private url: string;
-  private credentials: Record<string, string>;
-  private headers: Record<string, string>;
-  private readonly integrationName: string = "apple_calendar";
+import { BaseCalendarApiAdapter } from "@lib/BaseCalendarApiAdapter";
+import { CalendarApiAdapter } from "@lib/calendarClient";
 
+export class AppleCalendar extends BaseCalendarApiAdapter implements CalendarApiAdapter {
   constructor(credential: Credential) {
-    const decryptedCredential = JSON.parse(
-      symmetricDecrypt(credential.key as string, process.env.CALENDSO_ENCRYPTION_KEY!)
-    );
-    const username = decryptedCredential.username;
-    const password = decryptedCredential.password;
-
-    this.url = "https://caldav.icloud.com";
-
-    this.credentials = {
-      username,
-      password,
-    };
-
-    this.headers = getBasicAuthHeaders({
-      username,
-      password,
-    });
-  }
-
-  convertDate(date: string): [number, number, number] {
-    return dayjs(date)
-      .utc()
-      .toArray()
-      .slice(0, 6)
-      .map((v, i) => (i === 1 ? v + 1 : v)) as [number, number, number];
-  }
-
-  getDuration(start: string, end: string): DurationObject {
-    return {
-      minutes: dayjs(end).diff(dayjs(start), "minute"),
-    };
-  }
-
-  getAttendees(attendees: Person[]): Attendee[] {
-    return attendees.map(({ email, name }) => ({ name, email, partstat: "NEEDS-ACTION" }));
-  }
-
-  async createEvent(event: CalendarEvent) {
-    try {
-      const calendars = await this.listCalendars();
-      const uid = uuidv4();
-      const { error, value: iCalString } = createEvent({
-        uid,
-        startInputType: "utc",
-        start: this.convertDate(event.startTime),
-        duration: this.getDuration(event.startTime, event.endTime),
-        title: event.title,
-        description: getRichDescription(event),
-        location: getLocation(event),
-        organizer: { email: event.organizer.email, name: event.organizer.name },
-        attendees: this.getAttendees(event.attendees),
-      });
-
-      if (error) throw new Error("Error creating iCalString");
-
-      if (!iCalString) throw new Error("Error creating iCalString");
-
-      await Promise.all(
-        calendars.map((calendar) => {
-          return createCalendarObject({
-            calendar: {
-              url: calendar.externalId,
-            },
-            filename: `${uid}.ics`,
-            iCalString: iCalString,
-            headers: this.headers,
-          });
-        })
-      );
-
-      return {
-        uid,
-        id: uid,
-        type: "apple_calendar",
-        password: "",
-        url: "",
-      };
-    } catch (reason) {
-      console.error(reason);
-      throw reason;
-    }
-  }
-
-  async updateEvent(uid: string, event: CalendarEvent): Promise<unknown> {
-    try {
-      const calendars = await this.listCalendars();
-      const events = [];
-
-      for (const cal of calendars) {
-        const calEvents = await this.getEvents(cal.externalId, null, null, [`${cal.externalId}${uid}.ics`]);
-
-        for (const ev of calEvents) {
-          events.push(ev);
-        }
-      }
-
-      const { error, value: iCalString } = createEvent({
-        uid,
-        startInputType: "utc",
-        start: this.convertDate(event.startTime),
-        duration: this.getDuration(event.startTime, event.endTime),
-        title: event.title,
-        description: getRichDescription(event),
-        location: getLocation(event),
-        organizer: { email: event.organizer.email, name: event.organizer.name },
-        attendees: this.getAttendees(event.attendees),
-      });
-
-      if (error) {
-        log.debug("Error creating iCalString");
-        return {};
-      }
-
-      const eventsToUpdate = events.filter((event) => event.uid === uid);
-
-      return await Promise.all(
-        eventsToUpdate.map((event) => {
-          return updateCalendarObject({
-            calendarObject: {
-              url: event.url,
-              data: iCalString,
-              etag: event?.etag,
-            },
-            headers: this.headers,
-          });
-        })
-      );
-    } catch (reason) {
-      console.error(reason);
-      throw reason;
-    }
-  }
-
-  async deleteEvent(uid: string): Promise<void> {
-    try {
-      const calendars = await this.listCalendars();
-      const events = [];
-
-      for (const cal of calendars) {
-        const calEvents = await this.getEvents(cal.externalId, null, null, [`${cal.externalId}${uid}.ics`]);
-
-        for (const ev of calEvents) {
-          events.push(ev);
-        }
-      }
-
-      const eventsToUpdate = events.filter((event) => event.uid === uid);
-
-      await Promise.all(
-        eventsToUpdate.map((event) => {
-          return deleteCalendarObject({
-            calendarObject: {
-              url: event.url,
-              etag: event?.etag,
-            },
-            headers: this.headers,
-          });
-        })
-      );
-    } catch (reason) {
-      console.error(reason);
-      throw reason;
-    }
-  }
-
-  async getAvailability(dateFrom: string, dateTo: string, selectedCalendars: IntegrationCalendar[]) {
-    try {
-      const selectedCalendarIds = selectedCalendars
-        .filter((e) => e.integration === this.integrationName)
-        .map((e) => e.externalId);
-      if (selectedCalendarIds.length == 0 && selectedCalendars.length > 0) {
-        // Only calendars of other integrations selected
-        return Promise.resolve([]);
-      }
-
-      return (
-        selectedCalendarIds.length === 0
-          ? this.listCalendars().then((calendars) => calendars.map((calendar) => calendar.externalId))
-          : Promise.resolve(selectedCalendarIds)
-      ).then(async (ids: string[]) => {
-        if (ids.length === 0) {
-          return Promise.resolve([]);
-        }
-
-        return (
-          await Promise.all(
-            ids.map(async (calId) => {
-              return (await this.getEvents(calId, dateFrom, dateTo)).map((event) => {
-                return {
-                  start: event.startDate.toISOString(),
-                  end: event.endDate.toISOString(),
-                };
-              });
-            })
-          )
-        ).flatMap((event) => event);
-      });
-    } catch (reason) {
-      log.error(reason);
-      throw reason;
-    }
-  }
-
-  async listCalendars(): Promise<IntegrationCalendar[]> {
-    try {
-      const account = await this.getAccount();
-      const calendars = await fetchCalendars({
-        account,
-        headers: this.headers,
-      });
-
-      return calendars
-        .filter((calendar) => {
-          return calendar.components?.includes("VEVENT");
-        })
-        .map((calendar, index) => ({
-          externalId: calendar.url,
-          name: calendar.displayName ?? "",
-          // FIXME Find a better way to set the primary calendar
-          primary: index === 0,
-          integration: this.integrationName,
-        }));
-    } catch (reason) {
-      console.error(reason);
-      throw reason;
-    }
-  }
-
-  async getEvents(
-    calId: string,
-    dateFrom: string | null,
-    dateTo: string | null,
-    objectUrls?: string[] | null
-  ) {
-    try {
-      const objects = await fetchCalendarObjects({
-        calendar: {
-          url: calId,
-        },
-        objectUrls: objectUrls ? objectUrls : undefined,
-        timeRange:
-          dateFrom && dateTo
-            ? {
-                start: dayjs(dateFrom).utc().format("YYYY-MM-DDTHH:mm:ss[Z]"),
-                end: dayjs(dateTo).utc().format("YYYY-MM-DDTHH:mm:ss[Z]"),
-              }
-            : undefined,
-        headers: this.headers,
-      });
-
-      const events = objects
-        .filter((e) => !!e.data)
-        .map((object) => {
-          const jcalData = ICAL.parse(object.data);
-          const vcalendar = new ICAL.Component(jcalData);
-          const vevent = vcalendar.getFirstSubcomponent("vevent");
-          const event = new ICAL.Event(vevent);
-
-          const calendarTimezone =
-            vcalendar.getFirstSubcomponent("vtimezone")?.getFirstPropertyValue("tzid") || "";
-
-          const startDate = calendarTimezone
-            ? dayjs(event.startDate.toJSDate()).tz(calendarTimezone)
-            : new Date(event.startDate.toUnixTime() * 1000);
-          const endDate = calendarTimezone
-            ? dayjs(event.endDate.toJSDate()).tz(calendarTimezone)
-            : new Date(event.endDate.toUnixTime() * 1000);
-
-          return {
-            uid: event.uid,
-            etag: object.etag,
-            url: object.url,
-            summary: event.summary,
-            description: event.description,
-            location: event.location,
-            sequence: event.sequence,
-            startDate,
-            endDate,
-            duration: {
-              weeks: event.duration.weeks,
-              days: event.duration.days,
-              hours: event.duration.hours,
-              minutes: event.duration.minutes,
-              seconds: event.duration.seconds,
-              isNegative: event.duration.isNegative,
-            },
-            organizer: event.organizer,
-            attendees: event.attendees.map((a) => a.getValues()),
-            recurrenceId: event.recurrenceId,
-            timezone: calendarTimezone,
-          };
-        });
-
-      return events;
-    } catch (reason) {
-      console.error(reason);
-      throw reason;
-    }
-  }
-
-  private async getAccount() {
-    const account = await createAccount({
-      account: {
-        serverUrl: this.url,
-        accountType: "caldav",
-        credentials: this.credentials,
-      },
-      headers: this.headers,
-    });
-
-    return account;
+    super(credential, "apple_calendar", "https://caldav.icloud.com");
   }
 }
diff --git a/lib/integrations/CalDav/CalDavCalendarAdapter.ts b/lib/integrations/CalDav/CalDavCalendarAdapter.ts
index dc3e02ac..e7361bf8 100644
--- a/lib/integrations/CalDav/CalDavCalendarAdapter.ts
+++ b/lib/integrations/CalDav/CalDavCalendarAdapter.ts
@@ -1,347 +1,10 @@
 import { Credential } from "@prisma/client";
-import dayjs from "dayjs";
-import utc from "dayjs/plugin/utc";
-import ICAL from "ical.js";
-import { Attendee, createEvent, DurationObject, Person } from "ics";
-import {
-  createAccount,
-  createCalendarObject,
-  deleteCalendarObject,
-  fetchCalendarObjects,
-  fetchCalendars,
-  getBasicAuthHeaders,
-  updateCalendarObject,
-} from "tsdav";
-import { v4 as uuidv4 } from "uuid";
 
-import { getLocation, getRichDescription } from "@lib/CalEventParser";
-import { symmetricDecrypt } from "@lib/crypto";
-import logger from "@lib/logger";
-
-import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "../../calendarClient";
-
-dayjs.extend(utc);
-
-const log = logger.getChildLogger({ prefix: ["[lib] caldav"] });
-
-export class CalDavCalendar implements CalendarApiAdapter {
-  private url: string;
-  private credentials: Record<string, string>;
-  private headers: Record<string, string>;
-  private readonly integrationName: string = "caldav_calendar";
+import { BaseCalendarApiAdapter } from "@lib/BaseCalendarApiAdapter";
+import { CalendarApiAdapter } from "@lib/calendarClient";
 
+export class CalDavCalendar extends BaseCalendarApiAdapter implements CalendarApiAdapter {
   constructor(credential: Credential) {
-    const decryptedCredential = JSON.parse(
-      symmetricDecrypt(credential.key as string, process.env.CALENDSO_ENCRYPTION_KEY!)
-    );
-    const username = decryptedCredential.username;
-    const url = decryptedCredential.url;
-    const password = decryptedCredential.password;
-
-    this.url = url;
-
-    this.credentials = {
-      username,
-      password,
-    };
-
-    this.headers = getBasicAuthHeaders({
-      username,
-      password,
-    });
-  }
-
-  convertDate(date: string): [number, number, number] {
-    return dayjs(date)
-      .utc()
-      .toArray()
-      .slice(0, 6)
-      .map((v, i) => (i === 1 ? v + 1 : v)) as [number, number, number];
-  }
-
-  getDuration(start: string, end: string): DurationObject {
-    return {
-      minutes: dayjs(end).diff(dayjs(start), "minute"),
-    };
-  }
-
-  getAttendees(attendees: Person[]): Attendee[] {
-    return attendees.map(({ email, name }) => ({ name, email, partstat: "NEEDS-ACTION" }));
-  }
-
-  async createEvent(event: CalendarEvent) {
-    try {
-      const calendars = await this.listCalendars();
-      const uid = uuidv4();
-
-      const { error, value: iCalString } = createEvent({
-        uid,
-        startInputType: "utc",
-        start: this.convertDate(event.startTime),
-        duration: this.getDuration(event.startTime, event.endTime),
-        title: event.title,
-        description: getRichDescription(event),
-        location: getLocation(event),
-        organizer: { email: event.organizer.email, name: event.organizer.name },
-        attendees: this.getAttendees(event.attendees),
-      });
-
-      if (error) throw new Error("Error creating iCalString");
-
-      if (!iCalString) throw new Error("Error creating iCalString");
-
-      await Promise.all(
-        calendars.map((calendar) => {
-          return createCalendarObject({
-            calendar: {
-              url: calendar.externalId,
-            },
-            filename: `${uid}.ics`,
-            iCalString: iCalString,
-            headers: this.headers,
-          });
-        })
-      );
-
-      return {
-        uid,
-        id: uid,
-        type: "caldav_calendar",
-        password: "",
-        url: "",
-      };
-    } catch (reason) {
-      log.error(reason);
-      throw reason;
-    }
-  }
-
-  async updateEvent(uid: string, event: CalendarEvent): Promise<unknown> {
-    try {
-      const calendars = await this.listCalendars();
-      const events = [];
-
-      for (const cal of calendars) {
-        const calEvents = await this.getEvents(cal.externalId, null, null);
-
-        for (const ev of calEvents) {
-          events.push(ev);
-        }
-      }
-
-      const { error, value: iCalString } = await createEvent({
-        uid,
-        startInputType: "utc",
-        start: this.convertDate(event.startTime),
-        duration: this.getDuration(event.startTime, event.endTime),
-        title: event.title,
-        description: getRichDescription(event),
-        location: getLocation(event),
-        organizer: { email: event.organizer.email, name: event.organizer.name },
-        attendees: this.getAttendees(event.attendees),
-      });
-
-      if (error) {
-        log.debug("Error creating iCalString");
-        return {};
-      }
-
-      const eventsToUpdate = events.filter((event) => event.uid === uid);
-
-      return await Promise.all(
-        eventsToUpdate.map((event) => {
-          return updateCalendarObject({
-            calendarObject: {
-              url: event.url,
-              data: iCalString,
-              etag: event?.etag,
-            },
-            headers: this.headers,
-          });
-        })
-      );
-    } catch (reason) {
-      log.error(reason);
-      throw reason;
-    }
-  }
-
-  async deleteEvent(uid: string): Promise<void> {
-    try {
-      const calendars = await this.listCalendars();
-      const events = [];
-
-      for (const cal of calendars) {
-        const calEvents = await this.getEvents(cal.externalId, null, null);
-
-        for (const ev of calEvents) {
-          events.push(ev);
-        }
-      }
-
-      const eventsToUpdate = events.filter((event) => event.uid === uid);
-
-      await Promise.all(
-        eventsToUpdate.map((event) => {
-          return deleteCalendarObject({
-            calendarObject: {
-              url: event.url,
-              etag: event?.etag,
-            },
-            headers: this.headers,
-          });
-        })
-      );
-    } catch (reason) {
-      log.error(reason);
-      throw reason;
-    }
-  }
-
-  async getAvailability(dateFrom: string, dateTo: string, selectedCalendars: IntegrationCalendar[]) {
-    try {
-      const selectedCalendarIds = selectedCalendars
-        .filter((e) => e.integration === this.integrationName)
-        .map((e) => e.externalId);
-
-      if (selectedCalendarIds.length == 0 && selectedCalendars.length > 0) {
-        // Only calendars of other integrations selected
-        return Promise.resolve([]);
-      }
-
-      return (
-        selectedCalendarIds.length === 0
-          ? this.listCalendars().then((calendars) => calendars.map((calendar) => calendar.externalId))
-          : Promise.resolve(selectedCalendarIds)
-      ).then(async (ids: string[]) => {
-        if (ids.length === 0) {
-          return Promise.resolve([]);
-        }
-
-        return (
-          await Promise.all(
-            ids.map(async (calId) => {
-              return (await this.getEvents(calId, dateFrom, dateTo)).map((event) => {
-                return {
-                  start: event.startDate.toISOString(),
-                  end: event.endDate.toISOString(),
-                };
-              });
-            })
-          )
-        ).flatMap((event) => event);
-      });
-    } catch (reason) {
-      log.error(reason);
-      throw reason;
-    }
-  }
-
-  async listCalendars(): Promise<IntegrationCalendar[]> {
-    try {
-      const account = await this.getAccount();
-      const calendars = await fetchCalendars({
-        account,
-        headers: this.headers,
-      });
-
-      return calendars
-        .filter((calendar) => {
-          return calendar.components?.includes("VEVENT");
-        })
-        .map((calendar, index) => ({
-          externalId: calendar.url,
-          name: calendar.displayName ?? "",
-          // FIXME Find a better way to set the primary calendar
-          primary: index === 0,
-          integration: this.integrationName,
-        }));
-    } catch (reason) {
-      log.error(reason);
-      throw reason;
-    }
-  }
-
-  async getEvents(calId: string, dateFrom: string | null, dateTo: string | null) {
-    try {
-      const objects = await fetchCalendarObjects({
-        calendar: {
-          url: calId,
-        },
-        timeRange:
-          dateFrom && dateTo
-            ? {
-                start: dayjs(dateFrom).utc().format("YYYY-MM-DDTHH:mm:ss[Z]"),
-                end: dayjs(dateTo).utc().format("YYYY-MM-DDTHH:mm:ss[Z]"),
-              }
-            : undefined,
-        headers: this.headers,
-      });
-
-      if (!objects || objects?.length === 0) {
-        return [];
-      }
-
-      const events = objects
-        .filter((e) => !!e.data)
-        .map((object) => {
-          const jcalData = ICAL.parse(object.data);
-          const vcalendar = new ICAL.Component(jcalData);
-          const vevent = vcalendar.getFirstSubcomponent("vevent");
-          const event = new ICAL.Event(vevent);
-
-          const calendarTimezone =
-            vcalendar.getFirstSubcomponent("vtimezone")?.getFirstPropertyValue("tzid") || "";
-
-          const startDate = calendarTimezone
-            ? dayjs(event.startDate.toJSDate()).tz(calendarTimezone)
-            : new Date(event.startDate.toUnixTime() * 1000);
-          const endDate = calendarTimezone
-            ? dayjs(event.endDate.toJSDate()).tz(calendarTimezone)
-            : new Date(event.endDate.toUnixTime() * 1000);
-
-          return {
-            uid: event.uid,
-            etag: object.etag,
-            url: object.url,
-            summary: event.summary,
-            description: event.description,
-            location: event.location,
-            sequence: event.sequence,
-            startDate,
-            endDate,
-            duration: {
-              weeks: event.duration.weeks,
-              days: event.duration.days,
-              hours: event.duration.hours,
-              minutes: event.duration.minutes,
-              seconds: event.duration.seconds,
-              isNegative: event.duration.isNegative,
-            },
-            organizer: event.organizer,
-            attendees: event.attendees.map((a) => a.getValues()),
-            recurrenceId: event.recurrenceId,
-            timezone: calendarTimezone,
-          };
-        });
-
-      return events;
-    } catch (reason) {
-      log.error(reason);
-      throw reason;
-    }
-  }
-
-  private async getAccount() {
-    const account = await createAccount({
-      account: {
-        serverUrl: `${this.url}`,
-        accountType: "caldav",
-        credentials: this.credentials,
-      },
-      headers: this.headers,
-    });
-
-    return account;
+    super(credential, "caldav_calendar");
   }
 }
diff --git a/lib/integrations/GoogleCalendar/GoogleCalendarApiAdapter.ts b/lib/integrations/GoogleCalendar/GoogleCalendarApiAdapter.ts
index b9395bd8..3a89a864 100644
--- a/lib/integrations/GoogleCalendar/GoogleCalendarApiAdapter.ts
+++ b/lib/integrations/GoogleCalendar/GoogleCalendarApiAdapter.ts
@@ -10,18 +10,30 @@ export interface ConferenceData {
   createRequest?: calendar_v3.Schema$CreateConferenceRequest;
 }
 
+class MyGoogleAuth extends google.auth.OAuth2 {
+  constructor(client_id: string, client_secret: string, redirect_uri: string) {
+    super(client_id, client_secret, redirect_uri);
+  }
+
+  isTokenExpiring() {
+    return super.isTokenExpiring();
+  }
+
+  async refreshToken(token: string | null | undefined) {
+    return super.refreshToken(token);
+  }
+}
+
 const googleAuth = (credential: Credential) => {
   const { client_secret, client_id, redirect_uris } = JSON.parse(process.env.GOOGLE_API_CREDENTIALS!).web;
-  const myGoogleAuth = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
+  const myGoogleAuth = new MyGoogleAuth(client_id, client_secret, redirect_uris[0]);
   const googleCredentials = credential.key as Auth.Credentials;
   myGoogleAuth.setCredentials(googleCredentials);
 
-  // FIXME - type errors IDK Why this is a protected method ¯\_(ツ)_/¯
   const isExpired = () => myGoogleAuth.isTokenExpiring();
 
   const refreshAccessToken = () =>
     myGoogleAuth
-      // FIXME - type errors IDK Why this is a protected method ¯\_(ツ)_/¯
       .refreshToken(googleCredentials.refresh_token)
       .then((res: GetTokenResponse) => {
         const token = res.res?.data;
@@ -149,7 +161,9 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda
           calendar.events.insert(
             {
               auth: myGoogleAuth,
-              calendarId: "primary",
+              calendarId: event.destinationCalendar?.externalId
+                ? event.destinationCalendar.externalId
+                : "primary",
               requestBody: payload,
               conferenceDataVersion: 1,
             },
@@ -201,7 +215,9 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda
           calendar.events.update(
             {
               auth: myGoogleAuth,
-              calendarId: "primary",
+              calendarId: event.destinationCalendar?.externalId
+                ? event.destinationCalendar.externalId
+                : "primary",
               eventId: uid,
               sendNotifications: true,
               sendUpdates: "all",
diff --git a/lib/integrations/Office365Calendar/Office365CalendarApiAdapter.ts b/lib/integrations/Office365Calendar/Office365CalendarApiAdapter.ts
index e0843a80..6369e644 100644
--- a/lib/integrations/Office365Calendar/Office365CalendarApiAdapter.ts
+++ b/lib/integrations/Office365Calendar/Office365CalendarApiAdapter.ts
@@ -182,16 +182,19 @@ export const Office365CalendarApiAdapter = (credential: Credential): CalendarApi
         });
     },
     createEvent: (event: CalendarEvent) =>
-      auth.getToken().then((accessToken) =>
-        fetch("https://graph.microsoft.com/v1.0/me/calendar/events", {
+      auth.getToken().then((accessToken) => {
+        const calendarId = event.destinationCalendar?.externalId
+          ? `${event.destinationCalendar.externalId}/`
+          : "";
+        return fetch(`https://graph.microsoft.com/v1.0/me/calendar/${calendarId}events`, {
           method: "POST",
           headers: {
             Authorization: "Bearer " + accessToken,
             "Content-Type": "application/json",
           },
           body: JSON.stringify(translateEvent(event)),
-        }).then(handleErrorsJson)
-      ),
+        }).then(handleErrorsJson);
+      }),
     deleteEvent: (uid: string) =>
       auth.getToken().then((accessToken) =>
         fetch("https://graph.microsoft.com/v1.0/me/calendar/events/" + uid, {
diff --git a/lib/notEmpty.ts b/lib/notEmpty.ts
new file mode 100644
index 00000000..ed8480b5
--- /dev/null
+++ b/lib/notEmpty.ts
@@ -0,0 +1,3 @@
+const notEmpty = <T>(value: T): value is NonNullable<typeof value> => !!value;
+
+export default notEmpty;
diff --git a/lib/videoClient.ts b/lib/videoClient.ts
index 9e811da4..e57d2347 100644
--- a/lib/videoClient.ts
+++ b/lib/videoClient.ts
@@ -51,7 +51,7 @@ const getVideoAdapters = (withCredentials: Credential[]): VideoApiAdapter[] =>
     return acc;
   }, []);
 
-const getBusyVideoTimes: (withCredentials: Credential[]) => Promise<unknown[]> = (withCredentials) =>
+const getBusyVideoTimes = (withCredentials: Credential[]) =>
   Promise.all(getVideoAdapters(withCredentials).map((c) => c.getAvailability())).then((results) =>
     results.reduce((acc, availability) => acc.concat(availability), [])
   );
diff --git a/pages/[user].tsx b/pages/[user].tsx
index 0a9fd3d2..8d42c8f5 100644
--- a/pages/[user].tsx
+++ b/pages/[user].tsx
@@ -20,6 +20,8 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
   const { user, eventTypes } = props;
   const { t } = useLocale();
   const router = useRouter();
+  const query = { ...router.query };
+  delete query.user; // So it doesn't display in the Link (and make tests fail)
 
   const nameOrUsername = user.name || user.username || "";
 
@@ -54,9 +56,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
                   <Link
                     href={{
                       pathname: `/${user.username}/${type.slug}`,
-                      query: {
-                        ...router.query,
-                      },
+                      query,
                     }}>
                     <a className="block px-6 py-4" data-testid="event-type-link">
                       <h2 className="font-semibold text-neutral-900 dark:text-white">{type.title}</h2>
diff --git a/pages/api/availability/calendar.ts b/pages/api/availability/calendar.ts
index 6f976a70..3e88e09f 100644
--- a/pages/api/availability/calendar.ts
+++ b/pages/api/availability/calendar.ts
@@ -1,19 +1,21 @@
 import type { NextApiRequest, NextApiResponse } from "next";
 
 import { getSession } from "@lib/auth";
+import notEmpty from "@lib/notEmpty";
+import prisma from "@lib/prisma";
 
-import { IntegrationCalendar, listCalendars } from "../../../lib/calendarClient";
-import prisma from "../../../lib/prisma";
+import getCalendarCredentials from "@server/integrations/getCalendarCredentials";
+import getConnectedCalendars from "@server/integrations/getConnectedCalendars";
 
 export default async function handler(req: NextApiRequest, res: NextApiResponse) {
-  const session = await getSession({ req: req });
+  const session = await getSession({ req });
 
   if (!session?.user?.id) {
     res.status(401).json({ message: "Not authenticated" });
     return;
   }
 
-  const currentUser = await prisma.user.findUnique({
+  const user = await prisma.user.findUnique({
     where: {
       id: session.user.id,
     },
@@ -21,25 +23,26 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
       credentials: true,
       timeZone: true,
       id: true,
+      selectedCalendars: true,
     },
   });
 
-  if (!currentUser) {
+  if (!user) {
     res.status(401).json({ message: "Not authenticated" });
     return;
   }
 
-  if (req.method == "POST") {
+  if (req.method === "POST") {
     await prisma.selectedCalendar.upsert({
       where: {
         userId_integration_externalId: {
-          userId: currentUser.id,
+          userId: user.id,
           integration: req.body.integration,
           externalId: req.body.externalId,
         },
       },
       create: {
-        userId: currentUser.id,
+        userId: user.id,
         integration: req.body.integration,
         externalId: req.body.externalId,
       },
@@ -49,11 +52,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
     res.status(200).json({ message: "Calendar Selection Saved" });
   }
 
-  if (req.method == "DELETE") {
+  if (req.method === "DELETE") {
     await prisma.selectedCalendar.delete({
       where: {
         userId_integration_externalId: {
-          userId: currentUser.id,
+          userId: user.id,
           externalId: req.body.externalId,
           integration: req.body.integration,
         },
@@ -63,17 +66,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
     res.status(200).json({ message: "Calendar Selection Saved" });
   }
 
-  if (req.method == "GET") {
+  if (req.method === "GET") {
     const selectedCalendarIds = await prisma.selectedCalendar.findMany({
       where: {
-        userId: currentUser.id,
+        userId: user.id,
       },
       select: {
         externalId: true,
       },
     });
 
-    const calendars: IntegrationCalendar[] = await listCalendars(currentUser.credentials);
+    // get user's credentials + their connected integrations
+    const calendarCredentials = getCalendarCredentials(user.credentials, user.id);
+    // get all the connected integrations' calendars (from third party)
+    const connectedCalendars = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
+    const calendars = connectedCalendars.flatMap((c) => c.calendars).filter(notEmpty);
     const selectableCalendars = calendars.map((cal) => {
       return { selected: selectedCalendarIds.findIndex((s) => s.externalId === cal.externalId) > -1, ...cal };
     });
diff --git a/pages/api/book/confirm.ts b/pages/api/book/confirm.ts
index 7aa599bb..6c72466d 100644
--- a/pages/api/book/confirm.ts
+++ b/pages/api/book/confirm.ts
@@ -1,4 +1,4 @@
-import { User, Booking, SchedulingType, BookingStatus } from "@prisma/client";
+import { Prisma, User, Booking, SchedulingType, BookingStatus } from "@prisma/client";
 import type { NextApiRequest, NextApiResponse } from "next";
 
 import { refund } from "@ee/lib/stripe/server";
@@ -70,6 +70,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
       email: true,
       name: true,
       username: true,
+      destinationCalendar: true,
     },
   });
 
@@ -77,7 +78,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
     return res.status(404).json({ message: "User not found" });
   }
 
-  if (req.method == "PATCH") {
+  if (req.method === "PATCH") {
     const booking = await prisma.booking.findFirst({
       where: {
         id: bookingId,
@@ -128,7 +129,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
     };
 
     if (reqBody.confirmed) {
-      const eventManager = new EventManager(currentUser.credentials);
+      const eventManager = new EventManager(currentUser);
       const scheduleResult = await eventManager.create(evt);
 
       const results = scheduleResult.results;
diff --git a/pages/api/book/event.ts b/pages/api/book/event.ts
index 7bbcf195..23f08cc6 100644
--- a/pages/api/book/event.ts
+++ b/pages/api/book/event.ts
@@ -23,6 +23,7 @@ import { getEventName } from "@lib/event";
 import EventManager, { EventResult, PartialReference } from "@lib/events/EventManager";
 import { BufferedBusyTime } from "@lib/integrations/Office365Calendar/Office365CalendarApiAdapter";
 import logger from "@lib/logger";
+import notEmpty from "@lib/notEmpty";
 import prisma from "@lib/prisma";
 import { BookingCreateBody } from "@lib/types/booking";
 import { getBusyVideoTimes } from "@lib/videoClient";
@@ -133,6 +134,7 @@ const userSelect = Prisma.validator<Prisma.UserArgs>()({
     timeZone: true,
     credentials: true,
     bufferTime: true,
+    destinationCalendar: true,
   },
 });
 
@@ -301,6 +303,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
     attendees: attendeesList,
     location: reqBody.location, // Will be processed by the EventManager later.
     language: t,
+    /** For team events, we will need to handle each member destinationCalendar eventually */
+    destinationCalendar: users[0].destinationCalendar,
   };
 
   if (eventType.schedulingType === SchedulingType.COLLECTIVE) {
@@ -368,6 +372,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
   let referencesToCreate: PartialReference[] = [];
   let user: User | null = null;
 
+  /** Let's start cheking for availability */
   for (const currentUser of users) {
     if (!currentUser) {
       console.error(`currentUser not found`);
@@ -390,8 +395,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
         selectedCalendars
       );
 
-      const videoBusyTimes = (await getBusyVideoTimes(credentials)).filter((time) => time);
-      calendarBusyTimes.push(...(videoBusyTimes as any[])); // FIXME add types
+      const videoBusyTimes = (await getBusyVideoTimes(credentials)).filter(notEmpty);
+      calendarBusyTimes.push(...videoBusyTimes);
       console.log("calendarBusyTimes==>>>", calendarBusyTimes);
 
       const bufferedBusyTimes: BufferedBusyTimes = calendarBusyTimes.map((a) => ({
@@ -449,7 +454,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
   if (!user) throw Error("Can't continue, user not found.");
 
   // After polling videoBusyTimes, credentials might have been changed due to refreshment, so query them again.
-  const eventManager = new EventManager(await refreshCredentials(user.credentials));
+  const credentials = await refreshCredentials(user.credentials);
+  const eventManager = new EventManager({ ...user, credentials });
 
   if (rescheduleUid) {
     // Use EventManager to conditionally use all needed integrations.
diff --git a/pages/team/[slug]/[type].tsx b/pages/team/[slug]/[type].tsx
index 8f9e8725..7a8ac34a 100644
--- a/pages/team/[slug]/[type].tsx
+++ b/pages/team/[slug]/[type].tsx
@@ -46,6 +46,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
               timeZone: true,
               hideBranding: true,
               plan: true,
+              brandColor: true,
             },
           },
           title: true,
@@ -97,6 +98,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
         image: team.logo,
         theme: null,
         weekStart: "Sunday",
+        brandColor: "" /* TODO: Add a way to set a brand color for Teams */,
       },
       date: dateParam,
       eventType: eventTypeObject,
diff --git a/playwright/integrations.test.ts b/playwright/integrations.test.ts
index 2bca930d..86dcfa7b 100644
--- a/playwright/integrations.test.ts
+++ b/playwright/integrations.test.ts
@@ -37,7 +37,7 @@ describe("webhooks", () => {
     // --- Book the first available day next month in the pro user's "30min"-event
     await page.goto(`http://localhost:3000/pro/30min`);
     await page.click('[data-testid="incrementMonth"]');
-    await page.click('[data-testid="day"]');
+    await page.click('[data-testid="day"][data-disabled="false"]');
     await page.click('[data-testid="time"]');
 
     // --- fill form
@@ -80,7 +80,9 @@ describe("webhooks", () => {
           },
         ],
         "description": "",
+        "destinationCalendar": null,
         "endTime": "[redacted/dynamic]",
+        "metadata": Object {},
         "organizer": Object {
           "email": "pro@example.com",
           "name": "Pro Example",
diff --git a/prisma/migrations/20211207010154_add_destination_calendar/migration.sql b/prisma/migrations/20211207010154_add_destination_calendar/migration.sql
new file mode 100644
index 00000000..6ab2fd19
--- /dev/null
+++ b/prisma/migrations/20211207010154_add_destination_calendar/migration.sql
@@ -0,0 +1,29 @@
+-- CreateTable
+CREATE TABLE "DestinationCalendar" (
+    "id" SERIAL NOT NULL,
+    "integration" TEXT NOT NULL,
+    "externalId" TEXT NOT NULL,
+    "userId" INTEGER,
+    "bookingId" INTEGER,
+    "eventTypeId" INTEGER,
+
+    PRIMARY KEY ("id")
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "DestinationCalendar.userId_unique" ON "DestinationCalendar"("userId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "DestinationCalendar.bookingId_unique" ON "DestinationCalendar"("bookingId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "DestinationCalendar.eventTypeId_unique" ON "DestinationCalendar"("eventTypeId");
+
+-- AddForeignKey
+ALTER TABLE "DestinationCalendar" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "DestinationCalendar" ADD FOREIGN KEY ("bookingId") REFERENCES "Booking"("id") ON DELETE SET NULL ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "DestinationCalendar" ADD FOREIGN KEY ("eventTypeId") REFERENCES "EventType"("id") ON DELETE SET NULL ON UPDATE CASCADE;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index ba956b78..69201ca4 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -37,6 +37,7 @@ model EventType {
   teamId                  Int?
   bookings                Booking[]
   availability            Availability[]
+  destinationCalendar     DestinationCalendar[]
   eventName               String?
   customInputs            EventTypeCustomInput[]
   timeZone                String?
@@ -70,39 +71,53 @@ enum UserPlan {
   PRO
 }
 
+model DestinationCalendar {
+  id          Int        @id @default(autoincrement())
+  integration String
+  externalId  String
+  user        User?      @relation(fields: [userId], references: [id])
+  userId      Int?       @unique
+  booking     Booking?   @relation(fields: [bookingId], references: [id])
+  bookingId   Int?       @unique
+  eventType   EventType? @relation(fields: [eventTypeId], references: [id])
+  eventTypeId Int?       @unique
+}
+
 model User {
-  id                  Int                @id @default(autoincrement())
-  username            String?            @unique
+  id                  Int                  @id @default(autoincrement())
+  username            String?              @unique
   name                String?
-  email               String             @unique
+  email               String               @unique
   emailVerified       DateTime?
   password            String?
   bio                 String?
   avatar              String?
-  timeZone            String             @default("Europe/London")
-  weekStart           String             @default("Sunday")
+  timeZone            String               @default("Europe/London")
+  weekStart           String               @default("Sunday")
   // DEPRECATED - TO BE REMOVED
-  startTime           Int                @default(0)
-  endTime             Int                @default(1440)
+  startTime           Int                  @default(0)
+  endTime             Int                  @default(1440)
   // </DEPRECATED>
-  bufferTime          Int                @default(0)
-  hideBranding        Boolean            @default(false)
+  bufferTime          Int                  @default(0)
+  hideBranding        Boolean              @default(false)
   theme               String?
-  createdDate         DateTime           @default(now()) @map(name: "created")
-  eventTypes          EventType[]        @relation("user_eventtype")
+  createdDate         DateTime             @default(now()) @map(name: "created")
+  eventTypes          EventType[]          @relation("user_eventtype")
   credentials         Credential[]
   teams               Membership[]
   bookings            Booking[]
   availability        Availability[]
   selectedCalendars   SelectedCalendar[]
-  completedOnboarding Boolean            @default(false)
+  completedOnboarding Boolean              @default(false)
   locale              String?
   twoFactorSecret     String?
-  twoFactorEnabled    Boolean            @default(false)
-  plan                UserPlan           @default(PRO)
+  twoFactorEnabled    Boolean              @default(false)
+  plan                UserPlan             @default(PRO)
   Schedule            Schedule[]
   webhooks            Webhook[]
-  brandColor          String             @default("#292929")
+  brandColor          String               @default("#292929")
+  // the location where the events will end up
+  destinationCalendar DestinationCalendar?
 
   @@map(name: "users")
 }
@@ -181,31 +196,28 @@ model DailyEventReference {
 }
 
 model Booking {
-  id          Int                @id @default(autoincrement())
-  uid         String             @unique
-  user        User?              @relation(fields: [userId], references: [id])
-  userId      Int?
-  references  BookingReference[]
-  eventType   EventType?         @relation(fields: [eventTypeId], references: [id])
-  eventTypeId Int?
-
-  title       String
-  description String?
-  startTime   DateTime
-  endTime     DateTime
-
-  attendees Attendee[]
-  location  String?
-
-  dailyRef DailyEventReference?
-
-  createdAt DateTime      @default(now())
-  updatedAt DateTime?
-  confirmed Boolean       @default(true)
-  rejected  Boolean       @default(false)
-  status    BookingStatus @default(ACCEPTED)
-  paid      Boolean       @default(false)
-  payment   Payment[]
+  id                  Int                  @id @default(autoincrement())
+  uid                 String               @unique
+  user                User?                @relation(fields: [userId], references: [id])
+  userId              Int?
+  references          BookingReference[]
+  eventType           EventType?           @relation(fields: [eventTypeId], references: [id])
+  eventTypeId         Int?
+  title               String
+  description         String?
+  startTime           DateTime
+  endTime             DateTime
+  attendees           Attendee[]
+  location            String?
+  dailyRef            DailyEventReference?
+  createdAt           DateTime             @default(now())
+  updatedAt           DateTime?
+  confirmed           Boolean              @default(true)
+  rejected            Boolean              @default(false)
+  status              BookingStatus        @default(ACCEPTED)
+  paid                Boolean              @default(false)
+  payment             Payment[]
+  destinationCalendar DestinationCalendar?
 }
 
 model Schedule {
diff --git a/public/static/locales/en/common.json b/public/static/locales/en/common.json
index 0ad5e887..4af8c46c 100644
--- a/public/static/locales/en/common.json
+++ b/public/static/locales/en/common.json
@@ -545,6 +545,7 @@
   "connect_your_favourite_apps": "Connect your favourite apps.",
   "automation": "Automation",
   "configure_how_your_event_types_interact": "Configure how your event types should interact with your calendars.",
+  "select_destination_calendar": "Select a destination calendar for your bookings.",
   "connect_an_additional_calendar": "Connect an additional calendar",
   "conferencing": "Conferencing",
   "calendar": "Calendar",
diff --git a/scripts/seed.ts b/scripts/seed.ts
index 089a186b..e75b453a 100644
--- a/scripts/seed.ts
+++ b/scripts/seed.ts
@@ -28,6 +28,7 @@ async function createUserAndEventType(opts: {
     password: await hashPassword(opts.user.password),
     emailVerified: new Date(),
     completedOnboarding: opts.user.completedOnboarding ?? true,
+    locale: "en",
     availability: {
       createMany: {
         data: getAvailabilityFromSchedule(DEFAULT_SCHEDULE),
diff --git a/server/createContext.ts b/server/createContext.ts
index e7423984..1e68e9ff 100644
--- a/server/createContext.ts
+++ b/server/createContext.ts
@@ -61,6 +61,7 @@ async function getUserFromSession({
         },
       },
       completedOnboarding: true,
+      destinationCalendar: true,
       locale: true,
     },
   });
diff --git a/server/routers/viewer.tsx b/server/routers/viewer.tsx
index a3cd0d5e..faec9e2a 100644
--- a/server/routers/viewer.tsx
+++ b/server/routers/viewer.tsx
@@ -376,7 +376,42 @@ const loggedInViewerRouter = createProtectedRouter()
       // get all the connected integrations' calendars (from third party)
       const connectedCalendars = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
 
-      return connectedCalendars;
+      return {
+        connectedCalendars,
+        destinationCalendar: user.destinationCalendar,
+      };
+    },
+  })
+  .mutation("setUserDestinationCalendar", {
+    input: z.object({
+      integration: z.string(),
+      externalId: z.string(),
+    }),
+    async resolve({ ctx, input }) {
+      const { user } = ctx;
+      const userId = ctx.user.id;
+      const calendarCredentials = getCalendarCredentials(user.credentials, user.id);
+      const connectedCalendars = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
+      const allCals = connectedCalendars.map((cal) => cal.calendars ?? []).flat();
+
+      if (
+        !allCals.find((cal) => cal.externalId === input.externalId && cal.integration === input.integration)
+      ) {
+        throw new TRPCError({ code: "BAD_REQUEST", message: `Could not find calendar ${input.externalId}` });
+      }
+      await ctx.prisma.destinationCalendar.upsert({
+        where: {
+          userId,
+        },
+        update: {
+          ...input,
+          userId,
+        },
+        create: {
+          ...input,
+          userId,
+        },
+      });
     },
   })
   .query("integrations", {