---
title: "Snapshot a space as a reusable template (listed under GET /space/templates)."
method: POST
path: "/space/{spaceId}/save-as-template"
tags: ["space"]
---

# Snapshot a space as a reusable template (listed under GET /space/templates).

`POST /space/{spaceId}/save-as-template`

## Request

- Base URL: `https://developer.meetergo.com` (the document declares no server; this is the origin it was published from)
- URL: `https://developer.meetergo.com/space/{spaceId}/save-as-template`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer
  - API key in header `x-meetergo-api-user-id`

## Path parameters

- `spaceId` string, required

## Request body

- CloneSpaceDto
  - `name` string: Name of the copy. Defaults to the source name with a suffix

## Response `201`

- Space
  - `name` string, required
  - `description` string, nullable
  - `slug` string, nullable
  - `status` 'draft' | 'published' | 'closed' | 'frozen', required
  - `settings` SpaceSettings, required
    - `contentModuleEnabled` boolean, required
    - `showBranding` boolean, required
    - `defaultItemAccess` 'inherit' | 'hidden' | 'ask', required
    - `welcome` SpaceWelcome
      - `enabled` boolean, required
      - `title` string
      - `message` string
      - `ctaLabel` string
      - `image` string
    - `team` SpaceTeam
      - `enabled` boolean, required
      - `members` SpaceTeamMember[], required
        - `id` string, required
        - `name` string, required
        - `role` string
        - `email` string
        - `side` 'us' | 'them', required
        - `avatarUrl` string
    - `showInviteeCard` boolean
    - `isTemplate` boolean
    - `assistantEnabled` boolean
    - `assistant` SpaceAssistantConfig
      - `profileId` object, nullable
      - `name` string
      - `welcomeMessage` string
      - `customPrompt` string
      - `humanHandoff` boolean
      - `aiDisclosure` boolean
    - `accountOwner` SpaceAccountOwner
      - `userId` string, required
      - `name` string, required
      - `role` string
      - `email` string
      - `avatarUrl` string
      - `userSlug` string, required
      - `meetingTypeId` string
      - `meetingTypeSlug` string
    - `tags` string[]
    - `newsSubject` string
    - `newsBody` string
  - `internalNotes` string, nullable
  - `brandOverrides` SpaceBrandOverrides, required
    - `logo` string
    - `primaryColor` string
    - `secondaryColor` string
    - `backdropUrl` string
    - `heroStyle` 'image' | 'skyline' | 'aurora'
  - `isFrozen` boolean, required
  - `frozenAt` string, date-time, nullable
  - `archiveHash` string, nullable
  - `archiveUrl` string, nullable
  - `createdAt` string, date-time, required
  - `updatedAt` string, date-time, required
  - `deletedAt` string, date-time, nullable
  - `id` string, required
  - `companyId` string, required
  - `ownerId` string, nullable
  - `customerCompany` CustomerCompany
    - `name` string, required
    - `logo` string
    - `primaryColor` string
    - `secondaryColor` string
    - `website` string
    - `crmCompany` CrmCompany
      - `id` string, required
      - `name` string, required
      - `domain` string
      - `industry` string
      - `size` '1-10' | '11-50' | '51-200' | '201-500' | '501-1000' | '1001+'
      - `website` string
      - `iconUrl` string: Cached square brand mark, resolved once from the domain via Brand.dev. The icon rather than the wordmark: every surface that shows this is a small square tile on a card or list row, where a wide logo renders illegibly. Stored rather than fetched per render — a pipeline board draws a hundred cards, and a lookup per card would be a hundred provider calls for a picture. `iconFetchedAt` records the attempt, not the success, so a domain with no mark is asked once and then left alone.
      - `iconFetchedAt` string, date-time
      - `phoneNumber` string
      - `address` Address, required
      - `notes` string
      - `description` string: What the company says it does, from its own website. Deliberately not `notes`: that column is the seller's own writing about this account, and enrichment overwriting it would destroy the one thing on the record a machine did not put there.
      - `enrichedAt` string, date-time: When firmographics were last looked up, successful or not. Separate from `iconFetchedAt`, which only means "we asked for a picture". Stamped on every attempt so a domain the provider knows nothing about is asked once rather than on every sweep forever.
      - `owner` User
        - `zoomEncryptionType` object: Zoom meeting encryption type preference. - 'enhanced_encryption': Allows web browser joining (recommended) - 'e2ee': End-to-end encryption, requires Zoom client - null: Use Zoom account default settings
        - `timezone` string, required: User timezone affects times shown in notifications, UI etc. It is set to the chosen timezone after onboarding is completed. `utc` timezone may suggest that onboarding was not completed yet or for the user.
        - `language` 'en' | 'de' | 'fr' | 'es' | 'no' | 'nl' | 'it' | 'pl' | 'se' | 'tr' | 'da', required
        - `seatTier` 'basic' | 'essentials' | 'growth' | 'teams' | 'light' | 'suite' | 'premium' | 'enterprise': The tier this seat is billed and entitled at, when it is lower than the company's own. `null` — the overwhelming majority, and every user that existed before this was added — means the seat simply follows the company tier, which is how billing worked when there was only one tier per account. A value here pins the seat lower: a Suite or Premium account should not have to buy Suite for a colleague who only books meetings. Only ever below the account tier. Nothing here can raise a seat above what the company pays for, because entitlements are the intersection of the two (see `mergeSeatAccess`).
        - `viewerScope` 'company' | 'teams': How much a {@link UserType.VIEWER} may read. Ignored for every other type. `company` sees the whole account, `teams` only the members of the workspaces they are assigned to via `analyticsViewerWorkspaces`. Explicit rather than inferred from "has any assigned workspace": an admin who creates a team-scoped viewer and has not picked the teams yet would otherwise be handing out company-wide access by omission. A `teams` viewer with nothing assigned sees nothing, which is the safe end of that mistake.
        - `notetakerEnabled` boolean, required: Send the notetaker to this host's meetings unless something more specific says otherwise. The bottom of the chain — a meeting type or a single meeting can still override it in either direction. Defaults to false and must stay that way: the bot is a visible third participant recording people who did not book the call, and Germany is a two-party-consent country. Recording is something a host turns on.
        - `notetakerRecordExternal` boolean, required: Whether the notetaker also covers meetings read off this host's connected calendars, or only meetergo bookings. On by default, which keeps today's behaviour: switching the notetaker on has always meant the whole calendar. Off narrows `notetakerEnabled` to bookings — and stops the calendar mirror storing this host's external meetings at all, so the narrower choice is also the more private one. Meaningless while `notetakerEnabled` is false.
        - `notetakerLanguage` 'en' | 'de' | 'fr' | 'es' | 'no' | 'nl' | 'it' | 'pl' | 'se' | 'tr' | 'da'
        - `notetakerEmailTranscript` boolean, required: Mail this host the recording when it is ready. On by default, which is the opposite of `notetakerEnabled` above and for a different question. That switch decides whether a bot joins somebody else's call, so its absence has to read as no. This one decides whether a host gets their own recording in their own inbox after they already asked for it, and a host who switched recording on and hears nothing afterwards assumes it did not work. The mail goes to the host alone and carries the summary and a link, never the transcript itself or a copy to the other people on the call: those are decisions about somebody else's words, and they belong to a sharing action rather than to a default.
        - `notetakerAdvanceNotice` boolean, required: Email attendees ahead of this host's recorded meetings. On by default: the advance notice is the disclosure attendees get when nothing else reaches them, so silence is something a host opts into, not something that happens to them. Off only quiets the dedicated advance email — the confirmation footer, the calendar sentence and the notice in the meeting itself are not this setting's to remove.
        - `preCallBriefEmail` boolean, nullable: Email me the pre-call brief before each of my meetings. Three states, not two. Null means the host has never answered, so the workspace's own default decides; true and false are the host overriding it either way. Without the null an admin switching the workspace on could never reach anybody, because every existing row would already read as a deliberate "no".
        - `meetingTypes` MeetingType[]: Meeting types that user owns
          - `meetingInfo` MeetingInfo, required
            - `name` string, required: Name of the meeting type
            - `description` string, required: Description of the meeting type
            - `nameLocalised` LocalisedContent
              - …
            - `descriptionLocalised` LocalisedContent
              - …
            - `enableRedirect` boolean, required: Enable redirect after booking completion
            - `redirect` string, required: Redirect URL to navigate after booking
            - `passEventDetailsToRedirect` boolean, required: Pass event details (invitee name, email, form answers) as query params to redirect URL
            - `duration` number, required: Primary duration in minutes
            - `channel` 'local' | 'local-attendee' | 'google' | 'zoom' | 'phone' | 'phone-incoming' | 'whatsapp' | 'connect' | 'webex' | 'skypeForConsumer' | 'skypeForBusiness' | 'teamsForBusiness' | 'teamsForBusiness2' | 'teamsForExchange' | 'teams2ForExchange' | 'custom' | 'resource' | 'whereby' | 'kmeet' | 'zava' | 'jitsi' | 'nextcloudTalk' | 'openTalk' | 'alfaview', required: Meeting channel/location type
            - `customChannelName` string, required: Custom name for the meeting channel
            - `customChannelLink` string, required: Custom link for the meeting channel
            - `connectChannelName` string, required: Custom display name for the connect channel
            - `groupBooking` boolean, required: Enable group booking (multiple attendees per slot)
            - `showAvailableSlots` boolean, required: Show available spots count on the booking page
            - `enrichInvitee` boolean, required: Enable invitee data enrichment
            - `autoCreateSpace` boolean, required: Auto-create a Space when someone books
            - `spacePresetId` object, nullable: Space preset id used for the auto-created Space
            - `spaceAutoInvite` boolean, required: Publish the auto-created Space and invite the booker
            - `confirmationButton` ConfirmationButton, required
              - …
            - `bufferBefore` number, required: Buffer time before meeting in minutes
            - `bufferAfter` number, required: Buffer time after meeting in minutes
            - `bufferMustStayWithinWorkingHours` boolean, required: Only offer slots where buffer fits within working hours (no spillover into breaks or after-hours)
            - `rollingSlots` boolean, required: Restart the slot grid after each appointment instead of keeping a fixed grid per working block
            - `color` object, nullable: Color for the meeting type in RGB format, e.g. "rgb(0, 153, 255)"
          - `meetingOptions` MeetingOptions, required
            - `allowedDurations` number[], required: Additional duration options in minutes (shown alongside primary duration)
            - `allowSelectChannel` boolean, required: Allow attendees to select meeting channel/location
            - `allowHostSelection` boolean, required: Allow attendees to select specific host (round-robin only)
            - `forceHostSelection` boolean, required: Force host selection (no "any host" option available)
            - `allowedChannels` MeetingTypeChannel[], required: Available channel options when allowSelectChannel is enabled
            - `channelAvailabilities` object: Per-channel availability overrides. Each value can be a UUID string (existing availability) or an inline availability object with "name", "timezone", and optional "schedule" and "exceptions" for creation.
            - `allowAddingGuests` boolean, required: Allow attendees to invite additional guests
            - `doubleOptIn` string, required: Double opt-in confirmation setting
            - `requireHostConfirmation` string, required: Require host confirmation for bookings
            - `enableClusterMeeting` boolean, required: Show only time slots adjacent to existing meetings
            - `enableBufferCarryOver` boolean, required: Carry over buffer time to next available slot if needed
            - `maxBufferCarryOverDelay` number, required: Maximum hours for buffer carry-over delay (0-168)
            - `allowPhoneOnlyBooking` boolean, required: Allow phone-only bookings without email (API use case, requires phoneOnlyBooking in request)
            - `hideReassignedHostDetails` boolean, required: Hide the previous host's details from attendee reassignment emails
            - `confirmationPageCssSetting` CssSetting, required
              - …
            - `bookingPagePassword` BookingPagePasswordSetting, required
              - …
          - `timeslotInterval` number, required
          - `spots` number, required
          - `enabled` boolean, required
          - `isHidden` boolean, required: Marks meeting types that should be hidden from user-facing UIs. Used for system-generated meeting types (e.g., calgent auto-bookings). When true: - Not shown in meeting type lists (filtered in all queries) - Cannot be directly edited by users via UI - May be managed by automated processes
          - `slug` string, required
          - `bookingMinimum` number, required: Time in minutes. Default 30 mins
          - `roundRobin` string[], required: Used to store user latest booked host ids Last element - lastest booked host There shouldn't be duplicates
          - `schedule` WeeklySchedule, required
            - `monday` DaySchedule, required
              - …
            - `tuesday` DaySchedule, required
              - …
            - `wednesday` DaySchedule, required
              - …
            - `thursday` DaySchedule, required
              - …
            - `friday` DaySchedule, required
              - …
            - `saturday` DaySchedule, required
              - …
            - `sunday` DaySchedule, required
              - …
          - `hideAddressField` boolean, required: Hide address field for LOCAL_ATTENDEE channel type
          - `form` Form
            - `id` string, required
            - `name` string, required
            - `title` object, required: Title to be displayed in booking in multiple languages. Key is Language Code, Value is title in specified locale.
            - `actionButtonText` object, required: Button text to be displayed in booking in multiple languages. Key is Language Code, Value is title in specified locale.
            - `blockedEmails` string[], required
            - `fields` FormField[], required
              - …
            - `funnelSteps` FunnelStep[], required
              - …
            - `default` boolean, required: Indicates default form that can be modified, but should not be deleted
            - `funnelConditions` FunnelQualifier[], required
              - …
            - `useFullName` boolean, required
            - `skipForm` boolean, required
            - `showProgressBar` boolean
            - `useMeetingTypeSelection` boolean, required
            - `selectMeetingTypes` MeetingType[], required
            - `selectMeetingTypeOrder` string[]: Stores the order of meeting types for selection pages. If empty/null, use default order from selectMeetingTypeIds.
            - `selectionPageLayout` 'grid' | 'list' | 'cards' | 'tiles' | 'spotlight' | 'minimal' | 'showcase', required
            - `selectionPageDescription` object, required: Intro text shown under the selection page title, per language code.
            - `selectionPageSocialLinks` object, required: Social and contact links shown on the selection page.
            - `meetingType` MeetingType (recursive)
            - `meetingTypeTemplate` MeetingTypeTemplate
              - …
            - `standaloneKind` 'routing' | 'selection' | 'locationFinder': Explicit discriminator for standalone public experiences. Null preserves the legacy relation-based classification during the mixed-version rollout.
            - `qualifiers` Qualifier[]
              - …
            - `workspace` Workspace
              - …
            - `formTheme` FormTheme
              - …
            - `structureType` 'FORM_ONLY' | 'FUNNEL_WITH_FORM' | 'FUNNEL_ONLY', required: slug used in routing form only.
            - `sendRoutingFormSubmissionNotification` boolean, required
            - `notificationUserId` string, nullable
            - `notificationEmail` string, nullable
            - `contactTags` string[], required: Tags written onto the contact a submission creates or updates. Additive: a submission never removes a tag. This is where a newsletter audience grows by itself, and where a sequence trigger gets its tag from (`functions/contact-tags.ts`, configured in the Newsletter app).
            - `marketingConsentDoubleOptIn` boolean, required: Whether a ticked marketing-consent field is only recorded after the contact confirms it from their inbox. Off: the tick itself is the consent. On: the submission sends a confirmation mail and the ledger row is written when its link is used (`FormMarketingConsentService`).
            - `redirectAfterSubmission` boolean, required
            - `redirectUrl` string, nullable
            - `allowSaveAndReturn` boolean, required
            - `prefillLock` PrefillLock
              - …
            - `salesforceAccountMatch` SalesforceAccountMatch
              - …
            - `showCompanyLogo` boolean, required
            - `webhookUrl` string, nullable
            - `cssWorkspaceId` string, nullable: Which workspace's CSS to use when cssSetting.mode is Company. If null, falls back to the form's own workspace, then company CSS.
            - `hasPassword` boolean, required
            - `submissionDeadline` string, date-time, nullable
            - `maxSubmissions` number, nullable
            - `defaultRecipientExpiryDays` number, nullable
            - `allowRecipientResubmission` boolean: Whether a personal recipient link may be submitted more than once. False (the default) is the survey/one-response-per-person shape: the second submission through the same link is refused. True is the shape where a form carries several outcomes a recipient reaches on separate visits (book a call now, sign the contract later), so the answers may come again. This governs ANSWERS only. The outcomes the platform itself owns stay once-per-recipient either way: a signature cannot be collected twice (`assertSignatureNotCollected`) and an active booking is not made twice (`assertNoActiveRecipientBooking`). Those are properties of the outcome, not preferences of the form.
            - `publishedAt` string, date-time, nullable
            - `requiresPublish` boolean, required: New v2-created forms set this true: the form is not publicly visible until its first publish. Existing forms stay false (grandfathered: the live entities keep serving until the owner first publishes).
            - `allowCopy` boolean, required: Opt-in gate for copying this form's structure. Default false: a public link does NOT make a form copyable. Owner must turn this on (or publish to the marketplace, which implies consent). Enforced in the import path and the marketplace "use template" path. Stops the one-click clone; it cannot stop someone manually recreating fields off the public page.
            - `marketplaceStatus` 'none' | 'pending' | 'approved' | 'rejected', required: Marketplace lifecycle. Only `approved` forms appear in the public read API and the in-app gallery. See {@link MarketplaceStatus}.
            - `marketplaceOfficial` boolean, required: "by meetergo" curated form. Operator-set. Controls badge + sort order.
            - `marketplaceCategory` string, nullable: Category slug for marketplace filtering (e.g. "lead-gen", "recruiting").
            - `marketplaceDescription` string, nullable: Public listing description used for the gallery card and SEO copy.
            - `marketplaceListedAt` string, date-time, nullable: When the form was first approved/listed in the marketplace.
            - `marketplaceUseCount` number, required: How many times this form has been copied from the marketplace.
            - `reminders` Reminder[]
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `blockEmails` boolean, required
            - `fieldIds` string[], required
            - `funnelStepIds` string[], required
            - `disableModalInEmbed` boolean, required
            - `language` string, nullable: Language of the public form's own wording (buttons, placeholders, date pickers). Null follows the company language, which is what every form did before this column; set it when a form's questions are in another language, as a PDF import in German for an English account.
            - `selectMeetingTypeIds` string[], required
            - `meetingTypeId` string
            - `meetingTypeTemplateId` string
            - `locationFinderId` string, nullable, required
            - `locationFinderVersionId` string, nullable, required
            - `companyId` string, required: Shouldn't be nullable?
            - `qualifierIds` string[], required
            - `workspaceId` string, nullable
            - `userId` string
            - `crmOptions` MeetingCRMOptions, required
              - …
            - `cssSetting` CssSetting, required
              - …
            - `slug` string
          - `emailQualityGuard` boolean, required: Email Quality Guard - Blocks disposable emails and verifies email addresses exist Only applies when formFirst is enabled (Lead Capture Mode)
          - `enableWaitlist` boolean, required
          - `connectGuestJoinMinutesBeforeStart` number, nullable: Guest may enter this many minutes before a scheduled Connect call.
          - `connectGuestJoinMinutesAfterEnd` number, nullable: Guest may enter until this many minutes after a scheduled Connect call.
          - `runAsWebinar` boolean, required: Run bookings of this meeting type as a Connect webinar (roles, view-only attendees, moderated Q&A, attendance reporting) instead of an ordinary meeting. Only applies when `meetingInfo.channel` is CONNECT. Default false, so every existing meeting type keeps producing ordinary Connect meetings.
          - `webinarDefaults` object, nullable: Default webinar policy stamped onto each booking's Connect room. Null means the product defaults. Only read when `runAsWebinar` is on.
          - `notetakerRecording` 'on' | 'off', nullable: Whether the notetaker joins bookings of this meeting type. Null means the host's own default decides — which is not the same as `off`, and is why this is not a boolean. Stored as `varchar` rather than a Postgres enum for the same reason the notetaker's other vocabularies are: adding a word must never need a migration that locks a table.
          - `availability` Availability
            - `timezone` string, required
            - `id` string, required
            - `name` string, required
            - `schedule` WeeklySchedule, required
              - …
            - `useExplicitAvailability` boolean, required: When enabled, calendar events with the specified title are offered as bookable slots IN ADDITION to the weekly schedule (they behave like positive exceptions, see `UserAvailabilityService`). The weekly grid stays editable. Not to be confused with `scheduleSource`, which decides where the grid itself comes from.
            - `scheduleSource` object, required: Where the weekly schedule comes from. `manual` is the user typing it; `outlook` means it is read from the mailbox's Outlook working hours and the editor refuses direct schedule/timezone edits until switched back. Never read during slot calculation: to the availability engine an Outlook-sourced schedule is just a schedule.
            - `scheduleSourceConnectionId` string, nullable
            - `scheduleSyncedAt` string, date-time, nullable: Last time Outlook was consulted for this schedule (success or not).
            - `scheduleSyncError` object, nullable: Why the last consultation of Outlook could not update the schedule.
            - `explicitAvailabilityEventTitle` string, nullable: The calendar event title to look for when useExplicitAvailability is enabled. Events with this title will be treated as available booking slots.
            - `userId` string, required
            - `availabilityExceptions` AvailabilityException[]
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, date-time, nullable
            - `meetingType` MeetingType (recursive)
            - `meetingTypeTemplate` MeetingTypeTemplate
              - …
            - `resourceChannels` ResourceChannel[], required
              - …
          - `tags` string[], nullable: User-defined tags for organizing meeting types in the dashboard. Free-form strings, OR semantics for filtering. Mirrors the contact tags pattern.
          - `appointments` Appointment[]
            - `start` string, required
            - `end` string, required
            - `isCancelled` boolean, required: Determines whether appointment was cancelled
            - `spots` string, nullable: Per-appointment capacity for group bookings. Copied from meetingType.spots at creation time. Can be reduced to limit new bookees without cancelling existing attendees. When null, falls back to meetingType.spots. TODO: Ideally spots should be part of MeetingInfo (alongside groupBooking) so it gets copied naturally via the meetingInfo spread. Kept as top-level for now to avoid refactoring the existing meetingType.spots column + V4 API DTOs.
            - `rescheduledAt` string, date-time: Timestamp when appointment was rescheduled. Null if appointment is not rescheduled.
            - `rescheduledBy` string, nullable: Email address of the attendee, host, or operator who last rescheduled it.
            - `rescheduleCount` number, required: Number of times this appointment has been rescheduled. Used as the ICS SEQUENCE number so calendar clients recognize updates.
            - `bookedByUserId` string, nullable
            - `emailDeliveryIssues` object[], nullable: Mails the provider could not deliver for this appointment, one entry per address and kind. Written from AhaSend bounce webhooks so a typo in the attendee's address is visible before the appointment is missed.
              - …
            - `location` string
            - `scheduledReminders` ScheduledReminder[]
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `couponCode` string: Coupon code applied to this booking's payment (null if no coupon used)
            - `couponDiscountAmount` number: Discount amount applied via coupon (in the payment currency)
            - `note` string
            - `transcription` string, nullable: Full meeting transcription in markdown format
            - `summary` string, nullable: AI-generated summary of the meeting in markdown format
            - `actualStartAt` string, date-time, nullable: When the conversation actually ran, as whatever was in the call witnessed it: the first captured word for a recording, the first person in the room for a Connect call. `start` and `end` say when the meeting was booked for; these say what happened. Null until something that was there reports, and permanently null for a call nothing attended.
            - `actualEndAt` string, date-time, nullable
            - `attendanceCheckedAt` string, date-time, nullable: When something that could have witnessed the call finished looking. What tells "nobody came" apart from "nothing was watching": a past booking with this set and no `actualStartAt` is one a notetaker or the Connect room waited for and nobody joined, which the dashboard offers as a no-show to confirm. Never marks the no-show itself — that reaches the contact and their automations, and stays a person's decision.
            - `miraBrief` string, nullable: Mira's pre-meeting brief: who is coming, what was discussed last time, what is open. Written once on first request and kept, so opening the booking twice does not pay for the model twice.
            - `miraBriefAt` string, date-time, nullable
            - `occurrenceNumber` number, nullable: Position of this appointment within the recurring series (1-based). Null for non-recurring appointments.
            - `isBundleBooking` boolean, required
            - `id` string, required
            - `secret` string, required
            - `ics` string, required
            - `meetingInfo` MeetingInfo, required
              - …
            - `hostingInfo` HostingInfo, required
              - …
            - `cancel` BookingActionInfo, required
              - …
            - `crmOptions` MeetingCRMOptions, required
              - …
            - `bookedBy` User (recursive)
            - `utmSource` string, nullable
            - `utmMedium` string, nullable
            - `utmCampaign` string, nullable
            - `utmContent` string, nullable
            - `utmTerm` string, nullable
            - `referrerUrl` string, nullable
            - `sourceChannel` object, nullable
            - `formRecipientId` string, nullable: `FormRecipient.id` when a personal form link routed this booking. Without it nothing connects an appointment to the person a printed link was minted for, so a returning recipient could only be offered a second calendar. With it they are shown the booking they already hold. Plain uuid, no FK: an appointment is a real commitment and must survive the deletion of the form that routed it.
            - `attendees` Attendee[]: `cascade: true` exists to INSERT attendees together with a new appointment. Never `save()` an EXISTING appointment with a loaded `attendees` array: the cascade diffs against a stale snapshot and orphan-nullifies attendees linked concurrently (group-booking joins). Link/unlink attendee rows individually instead.
              - …
            - `attendeeIds` string[], required
            - `roundRobinEntries` RoundRobinEntry[]
              - …
            - `meetingType` MeetingType (recursive)
            - `meetingTypeId` string, required: @todo should not be nullable, but prod has some null values also recently
            - `bookingBlockId` string, nullable, required: The booking block this appointment was booked into, set only when the invitee arrived through that block's customer link. It is the sole answer to "which bookings are in this reserved day", and deleting the day cancels exactly these. Matching on host and time instead would sweep in ordinary meetings that merely overlap. Deliberately no foreign key: the row must outlive the block it names.
            - `hostIds` string[], required
            - `companyId` string, required
            - `resolvableErrors` AppointmentResolvableError[]
              - …
            - `bufferReservations` BufferReservation[]
              - …
            - `paymentId` string
            - `recapSharedAt` string, date-time, nullable: When the host turned this meeting's recap into a shareable link, or null while it is private. A timestamp rather than a boolean because "when did we start sharing this" is the question asked after a recap turns up somewhere unexpected. Sharing is off until the host asks for it: a transcript is the most sensitive thing the product stores, and a link that exists by default is a link that leaks by default.
            - `notetakerNoticeEmailedAt` string, date-time, nullable: When the invitees were told by email that this meeting will be recorded. Stamped by the confirmation email when its footer carried the recording disclosure, and otherwise by the advance-notice sweep when it sends the dedicated notice 24 hours ahead. One column, two writers, one rule: a null here on a meeting that will be recorded is a notice still owed.
            - `preCallBriefWarmedAt` string, date-time, nullable: When the structured Connect brief was generated ahead of the call. Bookkeeping for the warming sweep, not a third brief: the text itself lives in the cache the in-call panel already reads, and this is only how the sweep knows it has done a booking. Null means the panel would still pay for the model on first open, which is the wait this removes.
            - `recurringSeries` RecurringSeries
              - …
            - `recurringSeriesId` string, nullable
            - `bundle` MeetingTypeBundle
              - …
            - `bundleId` string, nullable
            - `bundleItems` AppointmentBundleItem[]: Bundle items for this appointment (details of each meeting type in the bundle). Only populated for bundle bookings.
              - …
          - `qualifiers` Qualifier[], nullable
            - `index` number, required
            - `routingAction` 'customPage' | 'externalRedirect' | 'eventRedirect' | 'requestCallback' | 'contactForm' | 'instantCall' | 'formRedirect' | 'teamPageRedirect', required
            - `isFallback` boolean, required
            - `queue` Queue
              - …
            - `meetingType` MeetingType (recursive)
            - `routeToCrmOwner` boolean, required: Route to the Salesforce owner of the record matched by attendee email: when true and the routing lookup matched a Contact/Lead, the owner's meetergo user is pinned as host (same mechanism as `meetingTypeUser`, which — being an explicit choice — still wins when set). Falls back to the normal queue when the owner can't be resolved to an eligible host.
            - `crmOwnerField` string, nullable: Which Salesforce User-lookup field supplies the host when `routeToCrmOwner` is on, as a condition-style path (`Contact.OwnerId`, `Account.OwnerId`, `Contact.SDR__c`). Null means "owner of whichever record matched", which is what every rule written before this column existed meant — so null must keep behaving exactly that way.
            - `meetingTypeUser` User (recursive)
            - `form` Form, required
              - …
            - `redirectForm` Form
              - …
            - `teamPage` Workspace
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `id` string, required
            - `contactFormEmail` string
            - `externalRedirectLink` string
            - `customPageContent` LocalisedContent
              - …
            - `customFormTitle` LocalisedContent
              - …
            - `showThankYouOnCustomPage` boolean, required
            - `expression` Expression, required
              - …
            - `queueId` string
            - `meetingTypeId` string
            - `meetingTypeUserId` string
            - `formId` string, required
            - `redirectFormId` string
            - `teamPageId` string
          - `reminders` Reminder[]
            - `meetingTypes` MeetingType[]
            - `meetingTypeTemplates` MeetingTypeTemplate[]
              - …
            - `forms` Form[]
              - …
            - `pipelines` Pipeline[]
              - …
            - `workspace` Workspace
              - …
            - `scheduledReminders` ScheduledReminder[]
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `id` number, required
            - `name` string, required
            - `enabled` boolean, required
            - `scope` object, required
            - `triggers` ReminderTrigger[], required
              - …
            - `companyId` string, required
            - `workspaceId` string
            - `userId` string: Maybe shouldnt be nullable
          - `formMeetingTypes` Form[]
            - `id` string, required
            - `name` string, required
            - `title` object, required: Title to be displayed in booking in multiple languages. Key is Language Code, Value is title in specified locale.
            - `actionButtonText` object, required: Button text to be displayed in booking in multiple languages. Key is Language Code, Value is title in specified locale.
            - `blockedEmails` string[], required
            - `fields` FormField[], required
              - …
            - `funnelSteps` FunnelStep[], required
              - …
            - `default` boolean, required: Indicates default form that can be modified, but should not be deleted
            - `funnelConditions` FunnelQualifier[], required
              - …
            - `useFullName` boolean, required
            - `skipForm` boolean, required
            - `showProgressBar` boolean
            - `useMeetingTypeSelection` boolean, required
            - `selectMeetingTypes` MeetingType[], required
            - `selectMeetingTypeOrder` string[]: Stores the order of meeting types for selection pages. If empty/null, use default order from selectMeetingTypeIds.
            - `selectionPageLayout` 'grid' | 'list' | 'cards' | 'tiles' | 'spotlight' | 'minimal' | 'showcase', required
            - `selectionPageDescription` object, required: Intro text shown under the selection page title, per language code.
            - `selectionPageSocialLinks` object, required: Social and contact links shown on the selection page.
            - `meetingType` MeetingType (recursive)
            - `meetingTypeTemplate` MeetingTypeTemplate
              - …
            - `standaloneKind` 'routing' | 'selection' | 'locationFinder': Explicit discriminator for standalone public experiences. Null preserves the legacy relation-based classification during the mixed-version rollout.
            - `qualifiers` Qualifier[]
              - …
            - `workspace` Workspace
              - …
            - `formTheme` FormTheme
              - …
            - `structureType` 'FORM_ONLY' | 'FUNNEL_WITH_FORM' | 'FUNNEL_ONLY', required: slug used in routing form only.
            - `sendRoutingFormSubmissionNotification` boolean, required
            - `notificationUserId` string, nullable
            - `notificationEmail` string, nullable
            - `contactTags` string[], required: Tags written onto the contact a submission creates or updates. Additive: a submission never removes a tag. This is where a newsletter audience grows by itself, and where a sequence trigger gets its tag from (`functions/contact-tags.ts`, configured in the Newsletter app).
            - `marketingConsentDoubleOptIn` boolean, required: Whether a ticked marketing-consent field is only recorded after the contact confirms it from their inbox. Off: the tick itself is the consent. On: the submission sends a confirmation mail and the ledger row is written when its link is used (`FormMarketingConsentService`).
            - `redirectAfterSubmission` boolean, required
            - `redirectUrl` string, nullable
            - `allowSaveAndReturn` boolean, required
            - `prefillLock` PrefillLock
              - …
            - `salesforceAccountMatch` SalesforceAccountMatch
              - …
            - `showCompanyLogo` boolean, required
            - `webhookUrl` string, nullable
            - `cssWorkspaceId` string, nullable: Which workspace's CSS to use when cssSetting.mode is Company. If null, falls back to the form's own workspace, then company CSS.
            - `hasPassword` boolean, required
            - `submissionDeadline` string, date-time, nullable
            - `maxSubmissions` number, nullable
            - `defaultRecipientExpiryDays` number, nullable
            - `allowRecipientResubmission` boolean: Whether a personal recipient link may be submitted more than once. False (the default) is the survey/one-response-per-person shape: the second submission through the same link is refused. True is the shape where a form carries several outcomes a recipient reaches on separate visits (book a call now, sign the contract later), so the answers may come again. This governs ANSWERS only. The outcomes the platform itself owns stay once-per-recipient either way: a signature cannot be collected twice (`assertSignatureNotCollected`) and an active booking is not made twice (`assertNoActiveRecipientBooking`). Those are properties of the outcome, not preferences of the form.
            - `publishedAt` string, date-time, nullable
            - `requiresPublish` boolean, required: New v2-created forms set this true: the form is not publicly visible until its first publish. Existing forms stay false (grandfathered: the live entities keep serving until the owner first publishes).
            - `allowCopy` boolean, required: Opt-in gate for copying this form's structure. Default false: a public link does NOT make a form copyable. Owner must turn this on (or publish to the marketplace, which implies consent). Enforced in the import path and the marketplace "use template" path. Stops the one-click clone; it cannot stop someone manually recreating fields off the public page.
            - `marketplaceStatus` 'none' | 'pending' | 'approved' | 'rejected', required: Marketplace lifecycle. Only `approved` forms appear in the public read API and the in-app gallery. See {@link MarketplaceStatus}.
            - `marketplaceOfficial` boolean, required: "by meetergo" curated form. Operator-set. Controls badge + sort order.
            - `marketplaceCategory` string, nullable: Category slug for marketplace filtering (e.g. "lead-gen", "recruiting").
            - `marketplaceDescription` string, nullable: Public listing description used for the gallery card and SEO copy.
            - `marketplaceListedAt` string, date-time, nullable: When the form was first approved/listed in the marketplace.
            - `marketplaceUseCount` number, required: How many times this form has been copied from the marketplace.
            - `reminders` Reminder[]
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `blockEmails` boolean, required
            - `fieldIds` string[], required
            - `funnelStepIds` string[], required
            - `disableModalInEmbed` boolean, required
            - `language` string, nullable: Language of the public form's own wording (buttons, placeholders, date pickers). Null follows the company language, which is what every form did before this column; set it when a form's questions are in another language, as a PDF import in German for an English account.
            - `selectMeetingTypeIds` string[], required
            - `meetingTypeId` string
            - `meetingTypeTemplateId` string
            - `locationFinderId` string, nullable, required
            - `locationFinderVersionId` string, nullable, required
            - `companyId` string, required: Shouldn't be nullable?
            - `qualifierIds` string[], required
            - `workspaceId` string, nullable
            - `userId` string
            - `crmOptions` MeetingCRMOptions, required
              - …
            - `cssSetting` CssSetting, required
              - …
            - `slug` string
          - `locations` string[], required
          - `bookingFrequencies` BookingFrequency[]: @todo this doesn't need to be OneToMany and maybe not even a relation (unless different frequencies per user for a queue are needed)
            - `frequency` HostBookingFrequency, required
              - …
            - `userId` string, required
            - `meetingTypeId` string, required
          - `createdAt` string, date-time, required
          - `updatedAt` string, date-time, required
          - `deletedAt` string, nullable
          - `timeFormat` '12' | '24', required
          - `provisionalBookings` ProvisionalBookingPayload[]
            - `id` string, required: Generated ID
            - `dto` BookingDto, required
              - …
            - `locationSelectionContext` object, nullable, required: Trusted Finder selection retained across confirmation flows.
            - `type` 'doubleOptIn' | 'requireHostConfirmation', required
            - `company` Company, required
              - …
            - `hosts` User[]
            - `meetingType` MeetingType, required (recursive)
            - `Appointment` Appointment
              - …
            - `appointmentId` string
            - `queueBookingInfo` NextQueueHostsSelectionReasonDto
              - …
            - `createdAt` string, date-time, required
            - `deletedAt` string, date-time, nullable, required
            - `status` 'pending' | 'confirmed' | 'expired' | 'declined_by_host', required: Status of the provisional booking request
            - `declineReason` string: Reason provided by host when declining (optional)
            - `resolvedAt` string, date-time: Timestamp when the booking was declined or expired
            - `expiryWarningSent` boolean, required: Whether the "expiring soon" warning email has been sent to the host
            - `expiresAt` string, date-time: When this provisional booking expires. Set at creation time based on company's provisionalBookingExpiryHours or default 72h.
            - `start` string, date-time: Reserved slot start. Used by availability checks so pending payloads block the slot for other bookers until the payload is confirmed, declined, or expired.
            - `end` string, date-time: Reserved slot end.
          - `accountOwnerMatchingEnabled` boolean, required: Account-owner matching: route a round-robin booking to the user who already owns the booker's account. Owner is resolved via past meetergo bookings + connected CRMs (Pipedrive, HubSpot, Salesforce). Requires `featureAccess.accountOwnerMatching` (Premium).
          - `accountOwnerMatchingScope` object, required: Where to draw the eligible-owner pool from. Picks one scope per meeting type (no inheritance). - `meetingType`: uses `accountOwnerMatchingUserIds` below - `workspace`: every active member of the meeting type's workspace - `company`: every active member of the meeting type's company
          - `accountOwnerMatchingUserIds` string[], required: Eligible owners when scope is `meetingType`. Ignored otherwise.
          - `accountOwnerMatchingAllowNonQueueHosts` boolean, required: If false (default), matched owners must also be queue members for the round-robin to route to them. If true, a matched pool user can host even when not in the queue.
          - `accountOwnerMatchingFallback` object, required: Behaviour when no owner matches (or matched owner has no slots). - `normalRoundRobin`: fall back to standard host availability - `showEmpty`: show no slots
          - `salesforceHostPoolEnabled` boolean, required: Draw the bookable hosts for this meeting type from Salesforce instead of from the statically configured queue: the booking page supplies an account key, and the pool is the account's team as {@link salesforceHostPool} describes it. The resolved pool is applied as a queue subset, so round-robin, fairness and host validation all keep working on it unchanged. Every failure — no connection, no match, timeout, nobody left after filtering — falls back to the full queue. Wrong advisor is recoverable in the CRM, a lost lead is not. Requires `featureAccess.salesforceRouting`.
          - `salesforceHostPool` SalesforceHostPoolSetting
            - `object` string, required
            - `accountField` string, required
            - `accountKeyField` string, required
            - `userField` string, required
            - `activeField` object, nullable
            - `orderByField` object, nullable
            - `dateField` object, nullable
            - `skill` SalesforceHostPoolSkillSetting
              - …
          - `internalNotes` string, nullable: Internal note for organizers. Never shown to customers. Used for project keys, partner info, briefing notes, etc.
          - `replyTo` string, nullable: Override the Reply-To address for notification emails sent for this meeting type. When set, takes precedence over the company `useNoReplyAddress` flag and the default host email fallback. Useful for sales/support teams that want replies routed to a shared inbox instead of the individual host.
          - `enableWithdrawalButton` boolean, required: Show the EU/German withdrawal button ("Widerrufsbutton") on the booking confirmation page. Required from 19 June 2026 (§ 356a BGB) for B2C distance contracts with a right of withdrawal. Off by default. Hosts must opt in per meeting type. Disable for leisure-services tied to a fixed date (concerts, events) which are exempt under § 312g Abs. 2 Nr. 9 BGB.
          - `id` string, required
          - `activationSessionId` string, nullable, required
          - `activationResourceKey` string, nullable, required
          - `crmOptions` MeetingCRMOptions, required
            - `hubspotContactSync` boolean, required
            - `hubspotContactStage` string, required
            - `hubspotCompanySync` boolean, required
            - `hubspotCompanyStage` string, required
            - `hubspotCompanyMatchingField` string, required
            - `hubspotDealSync` boolean, required
            - `hubspotDealStage` string, required
            - `hubspotPipelineId` string, required
            - `pipedriveDealSync` boolean, required
            - `pipedriveContactSync` boolean, required
            - `pipedrivePipelineId` number, nullable, required
            - `pipedriveStageId` number, nullable, required
            - `pipedriveUseExistingDeal` boolean, required
            - `closeDealSync` boolean, required
            - `closeContactSync` boolean, required
            - `closePipelineId` object, nullable, required
            - `closeStatusId` object, nullable, required
            - `closeUseExistingDeal` boolean, required
            - `centralStationContactSync` boolean, required
            - `centralStationDealSync` boolean, required
            - `centralStationPipelineId` number, nullable, required
            - `centralStationStageId` number, nullable, required
            - `linkandoSyncEnabled` boolean, required
            - `linkandoPlaybookId` number, nullable, required
            - `linkandoTemplateId` number, nullable, required
            - `evalancheEnabled` boolean, required
            - `klicktippEnabled` boolean, required
            - `getResponseEnabled` boolean, required
            - `getResponseCampaignId` string, nullable, required
            - `brevoEnabled` boolean, required
            - `sevdeskEnabled` boolean, required
            - `sevdeskCategoryId` number, nullable, required
            - `lexwareEnabled` boolean, required
            - `professionalWorksEnabled` boolean, required
            - `abAgentaEnabled` boolean, required
            - `smartWeContactSync` boolean, required
            - `smartWeCompanySync` boolean, required
            - `smartWeDealSync` boolean, required
            - `smartWeDealStage` string, required
            - `salesforceSyncEnabled` boolean, required
            - `salesforceCaseSync` boolean, required
            - `salesforceLeadSync` boolean, required
            - `salesforceOpportunitySync` boolean, required
            - `salesforceLeadStatus` string, required
            - `salesforceCaseStatus` string, required
            - `salesforceOpportunityStage` string, required
            - `salesforceDefaultObjectType` string, required
            - `internalCrmPipelineId` object, nullable, required
            - `internalCrmStageId` object, nullable, required
            - `crmOptInFieldName` object, nullable, required
          - `ics` IcsInfo, required
            - `title` LocalisedContent, required
              - …
            - `description` LocalisedContent, required
              - …
            - `location` LocalisedContent, required
              - …
            - `requireGuestConfirmation` boolean
          - `daysIntoFuture` DaysIntoFutureSetting, required
            - `mode` string, required
            - `bookingLimit` number
            - `countMode` string
            - `rangeMin` string, date-time
            - `rangeMax` string, date-time
          - `enableCalendarInviteNotification` boolean, required: New notification setting - use calendar invites instead of email notifications. When this option is enabled, booking confirmation notification and cancellation notification are handled by calendar connection. A google/outlook connection is required. It will be used to create calendar invite with notification. All meeting types pre-migration use email notifications. Introduced in order to mitigate duplicate invites, cancellation handling in calendar and overall improve both host and attendee experience.
          - `useSchedule` boolean, required: Override individual host's availability If true, availability selected for that meeting type will be used as common availability for all hosts. Move to meetingOptions?
          - `useCompanyLogo` boolean, required: Move to meetingOptions?
          - `useTeamLogo` boolean, required: Show team logo instead of company logo (for queue/round-robin meeting types)
          - `useCustomInvite` boolean, required: Move to meetingOptions?
          - `disableActionButtons` boolean, required: Move to meetingOptions?
          - `showConfetti` boolean, required: Whether to show confetti animation on the confirmation page
          - `useCustomForm` boolean, required
          - `formId` string
          - `inCompanyPage` boolean, required
          - `formFirst` boolean, required: Move to meetingOptions?
          - `communicationSettings` CommunicationSettings
            - `attendeeMeetingConfirmationStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingConfirmation` EmailTemplate, required
              - …
            - `hostMeetingConfirmationStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `hostMeetingConfirmation` EmailTemplate, required
              - …
            - `attendeeMeetingCancelledStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingCancelled` EmailTemplate, required
              - …
            - `hostMeetingCancelledStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `hostMeetingCancelled` EmailTemplate, required
              - …
            - `attendeeMeetingRescheduledStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingRescheduled` EmailTemplate, required
              - …
            - `hostMeetingRescheduledStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `hostMeetingRescheduled` EmailTemplate, required
              - …
            - `bringalongMeetingConfirmationStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `bringalongMeetingConfirmation` EmailTemplate, required
              - …
            - `hostMeetingReassignedStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `hostMeetingReassigned` EmailTemplate, required
              - …
            - `attendeeMeetingHostChangedStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingHostChanged` EmailTemplate, required
              - …
            - `attendeeMeetingCancelRequestStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingCancelRequest` EmailTemplate, required
              - …
            - `attendeeMeetingRescheduleRequestStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingRescheduleRequest` EmailTemplate, required
              - …
            - `confirmationAttachmentConfig` object[]
              - …
            - `reminderTemplate` EmailTemplate, required
              - …
            - `reminderRecipient` 'guest-all' | 'host-all' | 'everyone' | 'company-user' | 'custom-email' | 'booked-by' | 'form-recipient' | 'form-sender' | 'deal-owner' | 'deal-primary-contact' | 'deal-contacts' | 'lead-contact', required
            - `followupRecipient` 'guest-all' | 'host-all' | 'everyone' | 'company-user' | 'custom-email' | 'booked-by' | 'form-recipient' | 'form-sender' | 'deal-owner' | 'deal-primary-contact' | 'deal-contacts' | 'lead-contact', required
            - `id` number, required
            - `meetingType` MeetingType (recursive)
            - `meetingTypeTemplate` MeetingTypeTemplate
              - …
            - `confirmation` EmailTemplateContent[], required
              - …
            - `reminder` MeetingTypeNotificationConfig, required
              - …
            - `followup` MeetingTypeNotificationConfig, required
              - …
            - `sms` MeetingTypeNotificationConfig, required
              - …
          - `communicationSettingsId` number, nullable, required
          - `availabilityId` string
          - `companyId` string, required: Not null
          - `isFavorite` boolean: Computed at query time per user — not persisted on the entity. Populated by the list endpoint via a join with meeting_type_favorite.
          - `queue` Queue
            - `type` 'simpleRoundRobin' | 'advancedRoundRobin' | 'equalDistribution', required
            - `roundRobinEntries` RoundRobinEntry[], required
              - …
            - `meetingType` MeetingType (recursive)
            - `qualifiers` Qualifier[], nullable
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `id` string, required
            - `workspace` Workspace
              - …
            - `workspaceId` string
            - `oldMeetingType` MeetingType (recursive)
            - `meetingTypeId` string, required: Currently, there are several fields with value null in prod. I'm not sure if that does anything. I suspect that all rows with null meeting type id are orphaned. @todo confirm theory and migrate to non nullable
            - `userCapacities` UserCapacity[], required
              - …
            - `userPriorities` UserPriority[], required
              - …
            - `userCustomLinks` UserCustomLink[], required: Per-host overrides resolved by `CalendarLinkService.addMeetingLink` (CUSTOM channel).
              - …
            - `maxLeadThreshold` number, required: Maximum number of bookings a host can be ahead of the least-booked host before their availability is hidden. Ensures fair distribution.
            - `distributionPeriod` object, required: Period over which bookings are counted for equal distribution. Counts reset at the start of each period.
            - `prioritizeNearTermAvailability` boolean, required: When true, if fairness filtering (maxLeadThreshold) would push the next available slot more than 14 days beyond the meeting type's minimum booking notice, temporarily include over-lead hosts so bookers aren't penalized by fairness enforcement. Disable only if strict fairness must never yield to booker-visible availability.
            - `userGroups` QueueUserGroup[], required
              - …
            - `userGroupIds` string[], required
            - `companyId` string, required: Not null
            - `users` User[]
            - `userIds` string[], required
          - `queueId` string: Not real value as queue is owner
          - `oldQueues` Queue[]
            - `type` 'simpleRoundRobin' | 'advancedRoundRobin' | 'equalDistribution', required
            - `roundRobinEntries` RoundRobinEntry[], required
              - …
            - `meetingType` MeetingType (recursive)
            - `qualifiers` Qualifier[], nullable
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `id` string, required
            - `workspace` Workspace
              - …
            - `workspaceId` string
            - `oldMeetingType` MeetingType (recursive)
            - `meetingTypeId` string, required: Currently, there are several fields with value null in prod. I'm not sure if that does anything. I suspect that all rows with null meeting type id are orphaned. @todo confirm theory and migrate to non nullable
            - `userCapacities` UserCapacity[], required
              - …
            - `userPriorities` UserPriority[], required
              - …
            - `userCustomLinks` UserCustomLink[], required: Per-host overrides resolved by `CalendarLinkService.addMeetingLink` (CUSTOM channel).
              - …
            - `maxLeadThreshold` number, required: Maximum number of bookings a host can be ahead of the least-booked host before their availability is hidden. Ensures fair distribution.
            - `distributionPeriod` object, required: Period over which bookings are counted for equal distribution. Counts reset at the start of each period.
            - `prioritizeNearTermAvailability` boolean, required: When true, if fairness filtering (maxLeadThreshold) would push the next available slot more than 14 days beyond the meeting type's minimum booking notice, temporarily include over-lead hosts so bookers aren't penalized by fairness enforcement. Disable only if strict fairness must never yield to booker-visible availability.
            - `userGroups` QueueUserGroup[], required
              - …
            - `userGroupIds` string[], required
            - `companyId` string, required: Not null
            - `users` User[]
            - `userIds` string[], required
          - `reminderIds` string[], required
          - `workspace` Workspace
            - `connectWaitingRoom` ConnectWaitingRoomSettings
              - …
            - `integrationPermissions` object: Workspace-level integration permission overrides. Can override company settings (allow or deny specific integrations).
            - `customBookingCSS` string: Custom CSS for booking and confirmation pages. When set, this overrides the company-level customBookingCSS for this workspace's meeting types. Applied to both booking pages and confirmation/reschedule/cancel pages.
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `id` string, required
            - `name` string, required
            - `slug` string, required: Slug must be unique
            - `picture` string: Full url to workspace picture hosted statically
            - `description` string
            - `primaryColor` string
            - `logoUrl` string
            - `language` 'en' | 'de' | 'fr' | 'es' | 'no' | 'nl' | 'it' | 'pl' | 'se' | 'tr' | 'da'
            - `salesforceStudioId` string: Salesforce Studio__c Record ID Used for Küche&Co integration to link Cases to Studios
            - `useCompanyDefaultColor` boolean, required
            - `useCompanyDefaultLanguage` boolean, required
            - `useCompanyDefaultLogo` boolean, required
            - `useCompanyDefaultContactInfo` boolean, required
            - `showTeamSection` boolean, required
            - `showServicesSection` boolean, required
            - `showCompanyDetails` boolean, required
            - `showContactInfo` boolean, required
            - `emailAddress` string
            - `phone` string
            - `website` string
            - `address` string
            - `privacyUrl` string
            - `imprintUrl` string
            - `emailSenderName` string: Email sender name. Only name part of sender string without email.
            - `emailSenderAddress` string: Email sender address. Only email part of sender string. Only active if custom smtp is active. Otherwise this is ignored.
            - `enableCustomLegalText` 'Enable' | 'Disable' | 'Inherit', required: Enable/disable custom legal text
            - `customLegalText` string: Custom legal text HTML, shown above booking button if enabled
            - `reviewSettings` WorkspaceReviewSettings
              - …
            - `company` Company
              - …
            - `companyId` string, required
            - `users` User[]
            - `adminUsers` User[]
            - `analyticsViewers` User[]: Analytics-only viewers of this team. They can see this workspace's analytics (scoped to its members) but get no admin or edit rights — deliberately separate from `adminUsers`, which the meeting-type edit gates key off. Used for the "analytics for employees" role.
            - `meetingTypes` MeetingType[]
            - `queues` Queue[]
              - …
            - `reminders` Reminder[]
              - …
            - `forms` Form[]
              - …
          - `workspaceId` string
          - `isGlobalQueue` boolean, required: If true, meeting type is round robin/collective, which means it has a queue. However, it doesn't belong to a workspace, so it is available to the whole company. May not be needed but using explicit value for clarity for now.
          - `user` User (recursive)
          - `userId` string, nullable
          - `createdBy` User (recursive)
          - `createdById` string, required
          - `compact` boolean, required
          - `disableTeamsEmail` boolean, required: If enabled, booking confirmation email won't be sent for Microsoft Teams meeting
          - `calendarGuestListVisible` boolean, required: Whether guests on the synced calendar event can see each other. Off by default so strangers on a group booking never see each other's address; on for teams that invite colleagues and known contacts who expect a normal shared invite. Only Google honours it; Outlook has no such flag.
          - `languageSetting` LanguageSetting, required
            - `enable` boolean, required
            - `default` string, required
            - `allowed` string[], required
          - `customTextSelectChannel` CustomTextSelectChannelSetting, required
            - `enable` boolean, required
            - `body` string, required
          - `timezonePreset` TimezonePresetSetting, required
            - `enable` boolean, required
            - `timezonePreset` string
          - `cssSetting` CssSetting, required
            - `mode` number, required
            - `themePreset` 'light' | 'dark' | 'eco' | 'charcoal' | 'bold'
          - `meetingPaymentInfo` MeetingPaymentInfo, required
            - `enable` boolean, required
            - `price` number
            - `currency` string
            - `paymentTerms` string
            - `suffix` string
            - `provider` 'paypal' | 'stripe' | 'mollie' | 'none': Selected payment provider for this booking. Required when the meeting type has multiple payment providers enabled. If not provided, falls back to the first enabled provider.
            - `providers` string[]: Enabled payment providers. When set, takes precedence over provider.
            - `collectBillingDetails` boolean
            - `autoInvoice` boolean
            - `invoiceOnlinePayment` boolean
            - `invoiceTaxExempt` boolean
            - `durationPrices` object: Price per duration in minutes
          - `template` MeetingType (recursive)
          - `templateId` string, nullable
          - `lockedFields` string[], required: Field keys locked by the template admin. Copied from the template during assignment and push. Users cannot modify fields in locked sections.
          - `metaSetting` MetaSetting, required
            - `nextBetaEnabled` boolean
            - `title` string
            - `body` string
            - `image` string
          - `metadata` object, nullable: Custom key-value metadata for API integrations. Similar to Stripe's metadata field. Included in webhook payloads.
          - `importSource` string: Defined if imported from another provider. Currently mainly statistical purpose.
          - `videoSetting` VideoSetting, required
            - `enable` boolean, required
            - `immersive` boolean, required
            - `src` string
            - `thumbnailUrl` string
          - `cancellationPolicy` CancellationPolicySetting, required
            - `enable` boolean, required
            - `mode` 'disabled' | 'hours' | 'days'
            - `duration` object
          - `reschedulingPolicy` ReschedulingPolicySetting, required
            - `enable` boolean, required
            - `mode` 'disabled' | 'hours' | 'days'
            - `duration` object
          - `recurringSetting` RecurringSetting, required
            - `enable` boolean, required
            - `mode` 'recurring' | 'multi_slot'
            - `frequency` 'weekly' | 'biweekly' | 'monthly'
            - `maxOccurrences` number
            - `minOccurrences` number
            - `maxSlots` number
            - `minSlots` number
          - `enableEmailBookingLimit` boolean, required
          - `maxBookingsPerEmail` number, nullable
          - `emailBookingLimitPeriod` object
          - `emailBookingLimitPeriodCount` number, nullable: How many `emailBookingLimitPeriod` units the limit spans, so a window like "6 months" is expressible. Null or 1 counts from the calendar boundary; above 1 counts back from now. See `email-booking-limit-window.ts`.
          - `resourceChannels` ResourceChannel[]
            - `name` string, nullable: Optional name to show for resource channel, fall back to user name
            - `resource` User, required (recursive)
            - `groupName` string: If resource channel is part of a group, this is the name of the group. Multiple resource channels can be part of the same group, in which case the name is the same. Multiple resources with the same group name are grouped and only the group name is shown as a resource. Any resource within the group can be selected according to the items `groupIndex`. If resource channel is the only group member, this has no effect.
            - `groupMemberIndex` number: If resource channel is part of a group, this is the zero-based index to determine order that resource channel will be chosen within the group. Group members with index 0 will be chosen first, then 1, then 2, etc. If `groupName` is null, this has no effect.
            - `availability` Availability
              - …
            - `meetingType` MeetingType (recursive)
            - `meetingTypeTemplate` MeetingTypeTemplate
              - …
            - `id` string, required
            - `resourceId` string, required
            - `availabilityId` string
            - `meetingTypeId` string
            - `meetingTypeTemplateId` string
        - `meetingTypeTemplates` MeetingTypeTemplate[]
          - `meetingInfo` MeetingInfo, required
            - `name` string, required: Name of the meeting type
            - `description` string, required: Description of the meeting type
            - `nameLocalised` LocalisedContent
              - …
            - `descriptionLocalised` LocalisedContent
              - …
            - `enableRedirect` boolean, required: Enable redirect after booking completion
            - `redirect` string, required: Redirect URL to navigate after booking
            - `passEventDetailsToRedirect` boolean, required: Pass event details (invitee name, email, form answers) as query params to redirect URL
            - `duration` number, required: Primary duration in minutes
            - `channel` 'local' | 'local-attendee' | 'google' | 'zoom' | 'phone' | 'phone-incoming' | 'whatsapp' | 'connect' | 'webex' | 'skypeForConsumer' | 'skypeForBusiness' | 'teamsForBusiness' | 'teamsForBusiness2' | 'teamsForExchange' | 'teams2ForExchange' | 'custom' | 'resource' | 'whereby' | 'kmeet' | 'zava' | 'jitsi' | 'nextcloudTalk' | 'openTalk' | 'alfaview', required: Meeting channel/location type
            - `customChannelName` string, required: Custom name for the meeting channel
            - `customChannelLink` string, required: Custom link for the meeting channel
            - `connectChannelName` string, required: Custom display name for the connect channel
            - `groupBooking` boolean, required: Enable group booking (multiple attendees per slot)
            - `showAvailableSlots` boolean, required: Show available spots count on the booking page
            - `enrichInvitee` boolean, required: Enable invitee data enrichment
            - `autoCreateSpace` boolean, required: Auto-create a Space when someone books
            - `spacePresetId` object, nullable: Space preset id used for the auto-created Space
            - `spaceAutoInvite` boolean, required: Publish the auto-created Space and invite the booker
            - `confirmationButton` ConfirmationButton, required
              - …
            - `bufferBefore` number, required: Buffer time before meeting in minutes
            - `bufferAfter` number, required: Buffer time after meeting in minutes
            - `bufferMustStayWithinWorkingHours` boolean, required: Only offer slots where buffer fits within working hours (no spillover into breaks or after-hours)
            - `rollingSlots` boolean, required: Restart the slot grid after each appointment instead of keeping a fixed grid per working block
            - `color` object, nullable: Color for the meeting type in RGB format, e.g. "rgb(0, 153, 255)"
          - `meetingOptions` MeetingOptions, required
            - `allowedDurations` number[], required: Additional duration options in minutes (shown alongside primary duration)
            - `allowSelectChannel` boolean, required: Allow attendees to select meeting channel/location
            - `allowHostSelection` boolean, required: Allow attendees to select specific host (round-robin only)
            - `forceHostSelection` boolean, required: Force host selection (no "any host" option available)
            - `allowedChannels` MeetingTypeChannel[], required: Available channel options when allowSelectChannel is enabled
            - `channelAvailabilities` object: Per-channel availability overrides. Each value can be a UUID string (existing availability) or an inline availability object with "name", "timezone", and optional "schedule" and "exceptions" for creation.
            - `allowAddingGuests` boolean, required: Allow attendees to invite additional guests
            - `doubleOptIn` string, required: Double opt-in confirmation setting
            - `requireHostConfirmation` string, required: Require host confirmation for bookings
            - `enableClusterMeeting` boolean, required: Show only time slots adjacent to existing meetings
            - `enableBufferCarryOver` boolean, required: Carry over buffer time to next available slot if needed
            - `maxBufferCarryOverDelay` number, required: Maximum hours for buffer carry-over delay (0-168)
            - `allowPhoneOnlyBooking` boolean, required: Allow phone-only bookings without email (API use case, requires phoneOnlyBooking in request)
            - `hideReassignedHostDetails` boolean, required: Hide the previous host's details from attendee reassignment emails
            - `confirmationPageCssSetting` CssSetting, required
              - …
            - `bookingPagePassword` BookingPagePasswordSetting, required
              - …
          - `timeslotInterval` number, required
          - `spots` number, required
          - `bookingMinimum` number, required: Time in minutes. Default 30 mins
          - `schedule` WeeklySchedule, required
            - `monday` DaySchedule, required
              - …
            - `tuesday` DaySchedule, required
              - …
            - `wednesday` DaySchedule, required
              - …
            - `thursday` DaySchedule, required
              - …
            - `friday` DaySchedule, required
              - …
            - `saturday` DaySchedule, required
              - …
            - `sunday` DaySchedule, required
              - …
          - `hideAddressField` boolean, required: Hide address field for LOCAL_ATTENDEE channel type
          - `form` Form
            - `id` string, required
            - `name` string, required
            - `title` object, required: Title to be displayed in booking in multiple languages. Key is Language Code, Value is title in specified locale.
            - `actionButtonText` object, required: Button text to be displayed in booking in multiple languages. Key is Language Code, Value is title in specified locale.
            - `blockedEmails` string[], required
            - `fields` FormField[], required
              - …
            - `funnelSteps` FunnelStep[], required
              - …
            - `default` boolean, required: Indicates default form that can be modified, but should not be deleted
            - `funnelConditions` FunnelQualifier[], required
              - …
            - `useFullName` boolean, required
            - `skipForm` boolean, required
            - `showProgressBar` boolean
            - `useMeetingTypeSelection` boolean, required
            - `selectMeetingTypes` MeetingType[], required
              - …
            - `selectMeetingTypeOrder` string[]: Stores the order of meeting types for selection pages. If empty/null, use default order from selectMeetingTypeIds.
            - `selectionPageLayout` 'grid' | 'list' | 'cards' | 'tiles' | 'spotlight' | 'minimal' | 'showcase', required
            - `selectionPageDescription` object, required: Intro text shown under the selection page title, per language code.
            - `selectionPageSocialLinks` object, required: Social and contact links shown on the selection page.
            - `meetingType` MeetingType
              - …
            - `meetingTypeTemplate` MeetingTypeTemplate (recursive)
            - `standaloneKind` 'routing' | 'selection' | 'locationFinder': Explicit discriminator for standalone public experiences. Null preserves the legacy relation-based classification during the mixed-version rollout.
            - `qualifiers` Qualifier[]
              - …
            - `workspace` Workspace
              - …
            - `formTheme` FormTheme
              - …
            - `structureType` 'FORM_ONLY' | 'FUNNEL_WITH_FORM' | 'FUNNEL_ONLY', required: slug used in routing form only.
            - `sendRoutingFormSubmissionNotification` boolean, required
            - `notificationUserId` string, nullable
            - `notificationEmail` string, nullable
            - `contactTags` string[], required: Tags written onto the contact a submission creates or updates. Additive: a submission never removes a tag. This is where a newsletter audience grows by itself, and where a sequence trigger gets its tag from (`functions/contact-tags.ts`, configured in the Newsletter app).
            - `marketingConsentDoubleOptIn` boolean, required: Whether a ticked marketing-consent field is only recorded after the contact confirms it from their inbox. Off: the tick itself is the consent. On: the submission sends a confirmation mail and the ledger row is written when its link is used (`FormMarketingConsentService`).
            - `redirectAfterSubmission` boolean, required
            - `redirectUrl` string, nullable
            - `allowSaveAndReturn` boolean, required
            - `prefillLock` PrefillLock
              - …
            - `salesforceAccountMatch` SalesforceAccountMatch
              - …
            - `showCompanyLogo` boolean, required
            - `webhookUrl` string, nullable
            - `cssWorkspaceId` string, nullable: Which workspace's CSS to use when cssSetting.mode is Company. If null, falls back to the form's own workspace, then company CSS.
            - `hasPassword` boolean, required
            - `submissionDeadline` string, date-time, nullable
            - `maxSubmissions` number, nullable
            - `defaultRecipientExpiryDays` number, nullable
            - `allowRecipientResubmission` boolean: Whether a personal recipient link may be submitted more than once. False (the default) is the survey/one-response-per-person shape: the second submission through the same link is refused. True is the shape where a form carries several outcomes a recipient reaches on separate visits (book a call now, sign the contract later), so the answers may come again. This governs ANSWERS only. The outcomes the platform itself owns stay once-per-recipient either way: a signature cannot be collected twice (`assertSignatureNotCollected`) and an active booking is not made twice (`assertNoActiveRecipientBooking`). Those are properties of the outcome, not preferences of the form.
            - `publishedAt` string, date-time, nullable
            - `requiresPublish` boolean, required: New v2-created forms set this true: the form is not publicly visible until its first publish. Existing forms stay false (grandfathered: the live entities keep serving until the owner first publishes).
            - `allowCopy` boolean, required: Opt-in gate for copying this form's structure. Default false: a public link does NOT make a form copyable. Owner must turn this on (or publish to the marketplace, which implies consent). Enforced in the import path and the marketplace "use template" path. Stops the one-click clone; it cannot stop someone manually recreating fields off the public page.
            - `marketplaceStatus` 'none' | 'pending' | 'approved' | 'rejected', required: Marketplace lifecycle. Only `approved` forms appear in the public read API and the in-app gallery. See {@link MarketplaceStatus}.
            - `marketplaceOfficial` boolean, required: "by meetergo" curated form. Operator-set. Controls badge + sort order.
            - `marketplaceCategory` string, nullable: Category slug for marketplace filtering (e.g. "lead-gen", "recruiting").
            - `marketplaceDescription` string, nullable: Public listing description used for the gallery card and SEO copy.
            - `marketplaceListedAt` string, date-time, nullable: When the form was first approved/listed in the marketplace.
            - `marketplaceUseCount` number, required: How many times this form has been copied from the marketplace.
            - `reminders` Reminder[]
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `blockEmails` boolean, required
            - `fieldIds` string[], required
            - `funnelStepIds` string[], required
            - `disableModalInEmbed` boolean, required
            - `language` string, nullable: Language of the public form's own wording (buttons, placeholders, date pickers). Null follows the company language, which is what every form did before this column; set it when a form's questions are in another language, as a PDF import in German for an English account.
            - `selectMeetingTypeIds` string[], required
            - `meetingTypeId` string
            - `meetingTypeTemplateId` string
            - `locationFinderId` string, nullable, required
            - `locationFinderVersionId` string, nullable, required
            - `companyId` string, required: Shouldn't be nullable?
            - `qualifierIds` string[], required
            - `workspaceId` string, nullable
            - `userId` string
            - `crmOptions` MeetingCRMOptions, required
              - …
            - `cssSetting` CssSetting, required
              - …
            - `slug` string
          - `availability` Availability
            - `timezone` string, required
            - `id` string, required
            - `name` string, required
            - `schedule` WeeklySchedule, required
              - …
            - `useExplicitAvailability` boolean, required: When enabled, calendar events with the specified title are offered as bookable slots IN ADDITION to the weekly schedule (they behave like positive exceptions, see `UserAvailabilityService`). The weekly grid stays editable. Not to be confused with `scheduleSource`, which decides where the grid itself comes from.
            - `scheduleSource` object, required: Where the weekly schedule comes from. `manual` is the user typing it; `outlook` means it is read from the mailbox's Outlook working hours and the editor refuses direct schedule/timezone edits until switched back. Never read during slot calculation: to the availability engine an Outlook-sourced schedule is just a schedule.
            - `scheduleSourceConnectionId` string, nullable
            - `scheduleSyncedAt` string, date-time, nullable: Last time Outlook was consulted for this schedule (success or not).
            - `scheduleSyncError` object, nullable: Why the last consultation of Outlook could not update the schedule.
            - `explicitAvailabilityEventTitle` string, nullable: The calendar event title to look for when useExplicitAvailability is enabled. Events with this title will be treated as available booking slots.
            - `userId` string, required
            - `availabilityExceptions` AvailabilityException[]
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, date-time, nullable
            - `meetingType` MeetingType
              - …
            - `meetingTypeTemplate` MeetingTypeTemplate (recursive)
            - `resourceChannels` ResourceChannel[], required
              - …
          - `reminders` Reminder[]
            - `meetingTypes` MeetingType[]
              - …
            - `meetingTypeTemplates` MeetingTypeTemplate[]
            - `forms` Form[]
              - …
            - `pipelines` Pipeline[]
              - …
            - `workspace` Workspace
              - …
            - `scheduledReminders` ScheduledReminder[]
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `id` number, required
            - `name` string, required
            - `enabled` boolean, required
            - `scope` object, required
            - `triggers` ReminderTrigger[], required
              - …
            - `companyId` string, required
            - `workspaceId` string
            - `userId` string: Maybe shouldnt be nullable
          - `locations` string[], required
          - `bookingFrequencies` HostBookingFrequency[]: @todo this doesn't need to be OneToMany and maybe not even a relation (unless different frequencies per user for a queue are needed)
            - `perDay` number: Maximum bookings allowed per day
            - `perWeek` number: Maximum bookings allowed per week
            - `perMonth` number: Maximum bookings allowed per month
            - `perYear` number: Maximum bookings allowed per year
          - `createdAt` string, date-time, required
          - `updatedAt` string, date-time, required
          - `deletedAt` string, nullable
          - `connectGuestJoinMinutesBeforeStart` number, nullable: Guest may enter this many minutes before a scheduled Connect call.
          - `connectGuestJoinMinutesAfterEnd` number, nullable: Guest may enter until this many minutes after a scheduled Connect call.
          - `timeFormat` '12' | '24', required
          - `useHostAvailability` boolean, required
          - `meetingTypes` MeetingType[]
            - `meetingInfo` MeetingInfo, required
              - …
            - `meetingOptions` MeetingOptions, required
              - …
            - `timeslotInterval` number, required
            - `spots` number, required
            - `enabled` boolean, required
            - `isHidden` boolean, required: Marks meeting types that should be hidden from user-facing UIs. Used for system-generated meeting types (e.g., calgent auto-bookings). When true: - Not shown in meeting type lists (filtered in all queries) - Cannot be directly edited by users via UI - May be managed by automated processes
            - `slug` string, required
            - `bookingMinimum` number, required: Time in minutes. Default 30 mins
            - `roundRobin` string[], required: Used to store user latest booked host ids Last element - lastest booked host There shouldn't be duplicates
            - `schedule` WeeklySchedule, required
              - …
            - `hideAddressField` boolean, required: Hide address field for LOCAL_ATTENDEE channel type
            - `form` Form
              - …
            - `emailQualityGuard` boolean, required: Email Quality Guard - Blocks disposable emails and verifies email addresses exist Only applies when formFirst is enabled (Lead Capture Mode)
            - `enableWaitlist` boolean, required
            - `connectGuestJoinMinutesBeforeStart` number, nullable: Guest may enter this many minutes before a scheduled Connect call.
            - `connectGuestJoinMinutesAfterEnd` number, nullable: Guest may enter until this many minutes after a scheduled Connect call.
            - `runAsWebinar` boolean, required: Run bookings of this meeting type as a Connect webinar (roles, view-only attendees, moderated Q&A, attendance reporting) instead of an ordinary meeting. Only applies when `meetingInfo.channel` is CONNECT. Default false, so every existing meeting type keeps producing ordinary Connect meetings.
            - `webinarDefaults` object, nullable: Default webinar policy stamped onto each booking's Connect room. Null means the product defaults. Only read when `runAsWebinar` is on.
            - `notetakerRecording` 'on' | 'off', nullable: Whether the notetaker joins bookings of this meeting type. Null means the host's own default decides — which is not the same as `off`, and is why this is not a boolean. Stored as `varchar` rather than a Postgres enum for the same reason the notetaker's other vocabularies are: adding a word must never need a migration that locks a table.
            - `availability` Availability
              - …
            - `tags` string[], nullable: User-defined tags for organizing meeting types in the dashboard. Free-form strings, OR semantics for filtering. Mirrors the contact tags pattern.
            - `appointments` Appointment[]
              - …
            - `qualifiers` Qualifier[], nullable
              - …
            - `reminders` Reminder[]
              - …
            - `formMeetingTypes` Form[]
              - …
            - `locations` string[], required
            - `bookingFrequencies` BookingFrequency[]: @todo this doesn't need to be OneToMany and maybe not even a relation (unless different frequencies per user for a queue are needed)
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `timeFormat` '12' | '24', required
            - `provisionalBookings` ProvisionalBookingPayload[]
              - …
            - `accountOwnerMatchingEnabled` boolean, required: Account-owner matching: route a round-robin booking to the user who already owns the booker's account. Owner is resolved via past meetergo bookings + connected CRMs (Pipedrive, HubSpot, Salesforce). Requires `featureAccess.accountOwnerMatching` (Premium).
            - `accountOwnerMatchingScope` object, required: Where to draw the eligible-owner pool from. Picks one scope per meeting type (no inheritance). - `meetingType`: uses `accountOwnerMatchingUserIds` below - `workspace`: every active member of the meeting type's workspace - `company`: every active member of the meeting type's company
            - `accountOwnerMatchingUserIds` string[], required: Eligible owners when scope is `meetingType`. Ignored otherwise.
            - `accountOwnerMatchingAllowNonQueueHosts` boolean, required: If false (default), matched owners must also be queue members for the round-robin to route to them. If true, a matched pool user can host even when not in the queue.
            - `accountOwnerMatchingFallback` object, required: Behaviour when no owner matches (or matched owner has no slots). - `normalRoundRobin`: fall back to standard host availability - `showEmpty`: show no slots
            - `salesforceHostPoolEnabled` boolean, required: Draw the bookable hosts for this meeting type from Salesforce instead of from the statically configured queue: the booking page supplies an account key, and the pool is the account's team as {@link salesforceHostPool} describes it. The resolved pool is applied as a queue subset, so round-robin, fairness and host validation all keep working on it unchanged. Every failure — no connection, no match, timeout, nobody left after filtering — falls back to the full queue. Wrong advisor is recoverable in the CRM, a lost lead is not. Requires `featureAccess.salesforceRouting`.
            - `salesforceHostPool` SalesforceHostPoolSetting
              - …
            - `internalNotes` string, nullable: Internal note for organizers. Never shown to customers. Used for project keys, partner info, briefing notes, etc.
            - `replyTo` string, nullable: Override the Reply-To address for notification emails sent for this meeting type. When set, takes precedence over the company `useNoReplyAddress` flag and the default host email fallback. Useful for sales/support teams that want replies routed to a shared inbox instead of the individual host.
            - `enableWithdrawalButton` boolean, required: Show the EU/German withdrawal button ("Widerrufsbutton") on the booking confirmation page. Required from 19 June 2026 (§ 356a BGB) for B2C distance contracts with a right of withdrawal. Off by default. Hosts must opt in per meeting type. Disable for leisure-services tied to a fixed date (concerts, events) which are exempt under § 312g Abs. 2 Nr. 9 BGB.
            - `id` string, required
            - `activationSessionId` string, nullable, required
            - `activationResourceKey` string, nullable, required
            - `crmOptions` MeetingCRMOptions, required
              - …
            - `ics` IcsInfo, required
              - …
            - `daysIntoFuture` DaysIntoFutureSetting, required
              - …
            - `enableCalendarInviteNotification` boolean, required: New notification setting - use calendar invites instead of email notifications. When this option is enabled, booking confirmation notification and cancellation notification are handled by calendar connection. A google/outlook connection is required. It will be used to create calendar invite with notification. All meeting types pre-migration use email notifications. Introduced in order to mitigate duplicate invites, cancellation handling in calendar and overall improve both host and attendee experience.
            - `useSchedule` boolean, required: Override individual host's availability If true, availability selected for that meeting type will be used as common availability for all hosts. Move to meetingOptions?
            - `useCompanyLogo` boolean, required: Move to meetingOptions?
            - `useTeamLogo` boolean, required: Show team logo instead of company logo (for queue/round-robin meeting types)
            - `useCustomInvite` boolean, required: Move to meetingOptions?
            - `disableActionButtons` boolean, required: Move to meetingOptions?
            - `showConfetti` boolean, required: Whether to show confetti animation on the confirmation page
            - `useCustomForm` boolean, required
            - `formId` string
            - `inCompanyPage` boolean, required
            - `formFirst` boolean, required: Move to meetingOptions?
            - `communicationSettings` CommunicationSettings
              - …
            - `communicationSettingsId` number, nullable, required
            - `availabilityId` string
            - `companyId` string, required: Not null
            - `isFavorite` boolean: Computed at query time per user — not persisted on the entity. Populated by the list endpoint via a join with meeting_type_favorite.
            - `queue` Queue
              - …
            - `queueId` string: Not real value as queue is owner
            - `oldQueues` Queue[]
              - …
            - `reminderIds` string[], required
            - `workspace` Workspace
              - …
            - `workspaceId` string
            - `isGlobalQueue` boolean, required: If true, meeting type is round robin/collective, which means it has a queue. However, it doesn't belong to a workspace, so it is available to the whole company. May not be needed but using explicit value for clarity for now.
            - `user` User (recursive)
            - `userId` string, nullable
            - `createdBy` User (recursive)
            - `createdById` string, required
            - `compact` boolean, required
            - `disableTeamsEmail` boolean, required: If enabled, booking confirmation email won't be sent for Microsoft Teams meeting
            - `calendarGuestListVisible` boolean, required: Whether guests on the synced calendar event can see each other. Off by default so strangers on a group booking never see each other's address; on for teams that invite colleagues and known contacts who expect a normal shared invite. Only Google honours it; Outlook has no such flag.
            - `languageSetting` LanguageSetting, required
              - …
            - `customTextSelectChannel` CustomTextSelectChannelSetting, required
              - …
            - `timezonePreset` TimezonePresetSetting, required
              - …
            - `cssSetting` CssSetting, required
              - …
            - `meetingPaymentInfo` MeetingPaymentInfo, required
              - …
            - `template` MeetingType (recursive)
            - `templateId` string, nullable
            - `lockedFields` string[], required: Field keys locked by the template admin. Copied from the template during assignment and push. Users cannot modify fields in locked sections.
            - `metaSetting` MetaSetting, required
              - …
            - `metadata` object, nullable: Custom key-value metadata for API integrations. Similar to Stripe's metadata field. Included in webhook payloads.
            - `importSource` string: Defined if imported from another provider. Currently mainly statistical purpose.
            - `videoSetting` VideoSetting, required
              - …
            - `cancellationPolicy` CancellationPolicySetting, required
              - …
            - `reschedulingPolicy` ReschedulingPolicySetting, required
              - …
            - `recurringSetting` RecurringSetting, required
              - …
            - `enableEmailBookingLimit` boolean, required
            - `maxBookingsPerEmail` number, nullable
            - `emailBookingLimitPeriod` object
            - `emailBookingLimitPeriodCount` number, nullable: How many `emailBookingLimitPeriod` units the limit spans, so a window like "6 months" is expressible. Null or 1 counts from the calendar boundary; above 1 counts back from now. See `email-booking-limit-window.ts`.
            - `resourceChannels` ResourceChannel[]
              - …
          - `id` string, required
          - `crmOptions` MeetingCRMOptions, required
            - `hubspotContactSync` boolean, required
            - `hubspotContactStage` string, required
            - `hubspotCompanySync` boolean, required
            - `hubspotCompanyStage` string, required
            - `hubspotCompanyMatchingField` string, required
            - `hubspotDealSync` boolean, required
            - `hubspotDealStage` string, required
            - `hubspotPipelineId` string, required
            - `pipedriveDealSync` boolean, required
            - `pipedriveContactSync` boolean, required
            - `pipedrivePipelineId` number, nullable, required
            - `pipedriveStageId` number, nullable, required
            - `pipedriveUseExistingDeal` boolean, required
            - `closeDealSync` boolean, required
            - `closeContactSync` boolean, required
            - `closePipelineId` object, nullable, required
            - `closeStatusId` object, nullable, required
            - `closeUseExistingDeal` boolean, required
            - `centralStationContactSync` boolean, required
            - `centralStationDealSync` boolean, required
            - `centralStationPipelineId` number, nullable, required
            - `centralStationStageId` number, nullable, required
            - `linkandoSyncEnabled` boolean, required
            - `linkandoPlaybookId` number, nullable, required
            - `linkandoTemplateId` number, nullable, required
            - `evalancheEnabled` boolean, required
            - `klicktippEnabled` boolean, required
            - `getResponseEnabled` boolean, required
            - `getResponseCampaignId` string, nullable, required
            - `brevoEnabled` boolean, required
            - `sevdeskEnabled` boolean, required
            - `sevdeskCategoryId` number, nullable, required
            - `lexwareEnabled` boolean, required
            - `professionalWorksEnabled` boolean, required
            - `abAgentaEnabled` boolean, required
            - `smartWeContactSync` boolean, required
            - `smartWeCompanySync` boolean, required
            - `smartWeDealSync` boolean, required
            - `smartWeDealStage` string, required
            - `salesforceSyncEnabled` boolean, required
            - `salesforceCaseSync` boolean, required
            - `salesforceLeadSync` boolean, required
            - `salesforceOpportunitySync` boolean, required
            - `salesforceLeadStatus` string, required
            - `salesforceCaseStatus` string, required
            - `salesforceOpportunityStage` string, required
            - `salesforceDefaultObjectType` string, required
            - `internalCrmPipelineId` object, nullable, required
            - `internalCrmStageId` object, nullable, required
            - `crmOptInFieldName` object, nullable, required
          - `ics` IcsInfo, required
            - `title` LocalisedContent, required
              - …
            - `description` LocalisedContent, required
              - …
            - `location` LocalisedContent, required
              - …
            - `requireGuestConfirmation` boolean
          - `daysIntoFuture` DaysIntoFutureSetting, required
            - `mode` string, required
            - `bookingLimit` number
            - `countMode` string
            - `rangeMin` string, date-time
            - `rangeMax` string, date-time
          - `enableCalendarInviteNotification` boolean, required: New notification setting - use calendar invites instead of email notifications. When this option is enabled, booking confirmation notification and cancellation notification are handled by calendar connection. A google/outlook connection is required. It will be used to create calendar invite with notification. All meeting types pre-migration use email notifications. Introduced in order to mitigate duplicate invites, cancellation handling in calendar and overall improve both host and attendee experience.
          - `useSchedule` boolean, required: Override individual host's availability If true, availability selected for that meeting type will be used as common availability for all hosts. Move to meetingOptions?
          - `useCompanyLogo` boolean, required: Move to meetingOptions?
          - `useTeamLogo` boolean, required: Show team logo instead of company logo (for queue/round-robin meeting types)
          - `useCustomInvite` boolean, required: Move to meetingOptions?
          - `disableActionButtons` boolean, required: Move to meetingOptions?
          - `showConfetti` boolean, required: Whether to show confetti animation on the confirmation page
          - `enableWithdrawalButton` boolean, required: Show the EU/German withdrawal button on the booking confirmation page (§ 356a BGB, from 19.06.2026)
          - `useCustomForm` boolean, required
          - `formId` string
          - `inCompanyPage` boolean, required
          - `formFirst` boolean, required: Move to meetingOptions?
          - `communicationSettings` CommunicationSettings
            - `attendeeMeetingConfirmationStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingConfirmation` EmailTemplate, required
              - …
            - `hostMeetingConfirmationStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `hostMeetingConfirmation` EmailTemplate, required
              - …
            - `attendeeMeetingCancelledStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingCancelled` EmailTemplate, required
              - …
            - `hostMeetingCancelledStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `hostMeetingCancelled` EmailTemplate, required
              - …
            - `attendeeMeetingRescheduledStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingRescheduled` EmailTemplate, required
              - …
            - `hostMeetingRescheduledStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `hostMeetingRescheduled` EmailTemplate, required
              - …
            - `bringalongMeetingConfirmationStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `bringalongMeetingConfirmation` EmailTemplate, required
              - …
            - `hostMeetingReassignedStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `hostMeetingReassigned` EmailTemplate, required
              - …
            - `attendeeMeetingHostChangedStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingHostChanged` EmailTemplate, required
              - …
            - `attendeeMeetingCancelRequestStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingCancelRequest` EmailTemplate, required
              - …
            - `attendeeMeetingRescheduleRequestStatus` 'useCompanyTemplate' | 'useMeetingTypeTemplate' | 'disabled', required: @todo migrate to boolean, only disabled does something
            - `attendeeMeetingRescheduleRequest` EmailTemplate, required
              - …
            - `confirmationAttachmentConfig` object[]
              - …
            - `reminderTemplate` EmailTemplate, required
              - …
            - `reminderRecipient` 'guest-all' | 'host-all' | 'everyone' | 'company-user' | 'custom-email' | 'booked-by' | 'form-recipient' | 'form-sender' | 'deal-owner' | 'deal-primary-contact' | 'deal-contacts' | 'lead-contact', required
            - `followupRecipient` 'guest-all' | 'host-all' | 'everyone' | 'company-user' | 'custom-email' | 'booked-by' | 'form-recipient' | 'form-sender' | 'deal-owner' | 'deal-primary-contact' | 'deal-contacts' | 'lead-contact', required
            - `id` number, required
            - `meetingType` MeetingType
              - …
            - `meetingTypeTemplate` MeetingTypeTemplate (recursive)
            - `confirmation` EmailTemplateContent[], required
              - …
            - `reminder` MeetingTypeNotificationConfig, required
              - …
            - `followup` MeetingTypeNotificationConfig, required
              - …
            - `sms` MeetingTypeNotificationConfig, required
              - …
          - `communicationSettingsId` number, nullable, required
          - `availabilityId` string
          - `companyId` string, required: Not null
          - `reminderIds` string[], required
          - `createdBy` User (recursive)
          - `createdById` string, required
          - `compact` boolean, required
          - `disableTeamsEmail` boolean, required: If enabled, booking confirmation email won't be sent for Microsoft Teams meeting
          - `calendarGuestListVisible` boolean, required: Whether guests on the synced calendar event can see each other. Off by default so strangers on a group booking never see each other's address; on for teams that invite colleagues and known contacts who expect a normal shared invite. Only Google honours it; Outlook has no such flag.
          - `enableWaitlist` boolean, required
          - `runAsWebinar` boolean, required: Run bookings of this meeting type as a Connect webinar. Stamped onto members' meeting types like every other shared setting — a setting that lived only on MeetingType would silently drop when a template is applied.
          - `webinarDefaults` WebinarSettings, required
            - `accessPolicy` 'registered-only' | 'public-link', required
            - `attendeeMicrophone` boolean, required
            - `attendeeCamera` boolean, required
            - `attendeeScreenShare` boolean, required
            - `attendeeChat` boolean, required
            - `handRaising` boolean, required
            - `attendeeListVisible` boolean, required
            - `autoAdmit` boolean, required
            - `qaEnabled` boolean, required
            - `qaRequiresApproval` boolean, required
            - `qaUpvotes` boolean, required
            - `capacity` number, nullable
          - `notetakerRecording` 'on' | 'off', nullable, required: Whether the notetaker joins bookings of members' meeting types. Null lets each host's own default decide. Stamped like every other shared setting.
          - `languageSetting` LanguageSetting, required
            - `enable` boolean, required
            - `default` string, required
            - `allowed` string[], required
          - `customTextSelectChannel` CustomTextSelectChannelSetting, required
            - `enable` boolean, required
            - `body` string, required
          - `timezonePreset` TimezonePresetSetting, required
            - `enable` boolean, required
            - `timezonePreset` string
          - `cssSetting` CssSetting, required
            - `mode` number, required
            - `themePreset` 'light' | 'dark' | 'eco' | 'charcoal' | 'bold'
          - `meetingPaymentInfo` MeetingPaymentInfo, required
            - `enable` boolean, required
            - `price` number
            - `currency` string
            - `paymentTerms` string
            - `suffix` string
            - `provider` 'paypal' | 'stripe' | 'mollie' | 'none': Selected payment provider for this booking. Required when the meeting type has multiple payment providers enabled. If not provided, falls back to the first enabled provider.
            - `providers` string[]: Enabled payment providers. When set, takes precedence over provider.
            - `collectBillingDetails` boolean
            - `autoInvoice` boolean
            - `invoiceOnlinePayment` boolean
            - `invoiceTaxExempt` boolean
            - `durationPrices` object: Price per duration in minutes
          - `videoSetting` VideoSetting, required
            - `enable` boolean, required
            - `immersive` boolean, required
            - `src` string
            - `thumbnailUrl` string
          - `cancellationPolicy` CancellationPolicySetting, required
            - `enable` boolean, required
            - `mode` 'disabled' | 'hours' | 'days'
            - `duration` object
          - `reschedulingPolicy` ReschedulingPolicySetting, required
            - `enable` boolean, required
            - `mode` 'disabled' | 'hours' | 'days'
            - `duration` object
          - `recurringSetting` RecurringSetting, required
            - `enable` boolean, required
            - `mode` 'recurring' | 'multi_slot'
            - `frequency` 'weekly' | 'biweekly' | 'monthly'
            - `maxOccurrences` number
            - `minOccurrences` number
            - `maxSlots` number
            - `minSlots` number
          - `templateType` 'individual' | 'team', required
          - `queueType` 'simpleRoundRobin' | 'advancedRoundRobin' | 'equalDistribution', nullable
          - `assignedToCount` number: number of meetings assigned to this template
          - `lockedFields` string[], required: Field keys locked by the admin. Users cannot modify the corresponding settings on meeting types created from this template. Copied to meeting types on assign and push.
          - `slug` string, nullable: Optional canonical URL slug. When set, this is the source of truth for the URL of meeting types created from (or synced from) this template: - Used as the base slug on `assignHost` / `assignWorkspace`. - Propagated to assigned meeting types on push. When NULL, slugs behave exactly as before: derived from the template name on assign, never touched on push. Per-user uniqueness still applies via `getAvailableMeetingTypeSlug`.
          - `resourceChannels` ResourceChannel[]
            - `name` string, nullable: Optional name to show for resource channel, fall back to user name
            - `resource` User, required (recursive)
            - `groupName` string: If resource channel is part of a group, this is the name of the group. Multiple resource channels can be part of the same group, in which case the name is the same. Multiple resources with the same group name are grouped and only the group name is shown as a resource. Any resource within the group can be selected according to the items `groupIndex`. If resource channel is the only group member, this has no effect.
            - `groupMemberIndex` number: If resource channel is part of a group, this is the zero-based index to determine order that resource channel will be chosen within the group. Group members with index 0 will be chosen first, then 1, then 2, etc. If `groupName` is null, this has no effect.
            - `availability` Availability
              - …
            - `meetingType` MeetingType
              - …
            - `meetingTypeTemplate` MeetingTypeTemplate (recursive)
            - `id` string, required
            - `resourceId` string, required
            - `availabilityId` string
            - `meetingTypeId` string
            - `meetingTypeTemplateId` string
        - `availabilities` Availability[]: Availabilities that user owns
          - `timezone` string, required
          - `id` string, required
          - `name` string, required
          - `schedule` WeeklySchedule, required
            - `monday` DaySchedule, required
              - …
            - `tuesday` DaySchedule, required
              - …
            - `wednesday` DaySchedule, required
              - …
            - `thursday` DaySchedule, required
              - …
            - `friday` DaySchedule, required
              - …
            - `saturday` DaySchedule, required
              - …
            - `sunday` DaySchedule, required
              - …
          - `useExplicitAvailability` boolean, required: When enabled, calendar events with the specified title are offered as bookable slots IN ADDITION to the weekly schedule (they behave like positive exceptions, see `UserAvailabilityService`). The weekly grid stays editable. Not to be confused with `scheduleSource`, which decides where the grid itself comes from.
          - `scheduleSource` object, required: Where the weekly schedule comes from. `manual` is the user typing it; `outlook` means it is read from the mailbox's Outlook working hours and the editor refuses direct schedule/timezone edits until switched back. Never read during slot calculation: to the availability engine an Outlook-sourced schedule is just a schedule.
          - `scheduleSourceConnectionId` string, nullable
          - `scheduleSyncedAt` string, date-time, nullable: Last time Outlook was consulted for this schedule (success or not).
          - `scheduleSyncError` object, nullable: Why the last consultation of Outlook could not update the schedule.
          - `explicitAvailabilityEventTitle` string, nullable: The calendar event title to look for when useExplicitAvailability is enabled. Events with this title will be treated as available booking slots.
          - `userId` string, required
          - `availabilityExceptions` AvailabilityException[]
            - `ownerId` string, required
            - `ownerType` 'company' | 'user', required
            - `available` boolean, required
            - `start` string, required: Start time stored as clock time: "YYYY-MM-DDTHH:mm" (e.g., "2026-01-20T10:00"). Interpreted in availability.timezone when querying. BACKWARDS COMPATIBILITY (migration in progress since Dec 2025): Legacy data has Z suffix (e.g., "2026-01-20T10:00:00.000Z") and is interpreted as UTC. When all legacy exceptions expire or are edited, the Z-suffix handling can be removed.
            - `end` string, required: End time stored as clock time: "YYYY-MM-DDTHH:mm" (e.g., "2026-01-20T17:00"). Interpreted in availability.timezone when querying. BACKWARDS COMPATIBILITY (migration in progress since Dec 2025): Legacy data has Z suffix (e.g., "2026-01-20T17:00:00.000Z") and is interpreted as UTC. When all legacy exceptions expire or are edited, the Z-suffix handling can be removed.
            - `title` string: Optional title
            - `note` string: Optional note / description
            - `id` number, required
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `availabilityId` string, required
          - `createdAt` string, date-time, required
          - `updatedAt` string, date-time, required
          - `deletedAt` string, date-time, nullable
          - `meetingType` MeetingType
            - `meetingInfo` MeetingInfo, required
              - …
            - `meetingOptions` MeetingOptions, required
              - …
            - `timeslotInterval` number, required
            - `spots` number, required
            - `enabled` boolean, required
            - `isHidden` boolean, required: Marks meeting types that should be hidden from user-facing UIs. Used for system-generated meeting types (e.g., calgent auto-bookings). When true: - Not shown in meeting type lists (filtered in all queries) - Cannot be directly edited by users via UI - May be managed by automated processes
            - `slug` string, required
            - `bookingMinimum` number, required: Time in minutes. Default 30 mins
            - `roundRobin` string[], required: Used to store user latest booked host ids Last element - lastest booked host There shouldn't be duplicates
            - `schedule` WeeklySchedule, required
              - …
            - `hideAddressField` boolean, required: Hide address field for LOCAL_ATTENDEE channel type
            - `form` Form
              - …
            - `emailQualityGuard` boolean, required: Email Quality Guard - Blocks disposable emails and verifies email addresses exist Only applies when formFirst is enabled (Lead Capture Mode)
            - `enableWaitlist` boolean, required
            - `connectGuestJoinMinutesBeforeStart` number, nullable: Guest may enter this many minutes before a scheduled Connect call.
            - `connectGuestJoinMinutesAfterEnd` number, nullable: Guest may enter until this many minutes after a scheduled Connect call.
            - `runAsWebinar` boolean, required: Run bookings of this meeting type as a Connect webinar (roles, view-only attendees, moderated Q&A, attendance reporting) instead of an ordinary meeting. Only applies when `meetingInfo.channel` is CONNECT. Default false, so every existing meeting type keeps producing ordinary Connect meetings.
            - `webinarDefaults` object, nullable: Default webinar policy stamped onto each booking's Connect room. Null means the product defaults. Only read when `runAsWebinar` is on.
            - `notetakerRecording` 'on' | 'off', nullable: Whether the notetaker joins bookings of this meeting type. Null means the host's own default decides — which is not the same as `off`, and is why this is not a boolean. Stored as `varchar` rather than a Postgres enum for the same reason the notetaker's other vocabularies are: adding a word must never need a migration that locks a table.
            - `availability` Availability (recursive)
            - `tags` string[], nullable: User-defined tags for organizing meeting types in the dashboard. Free-form strings, OR semantics for filtering. Mirrors the contact tags pattern.
            - `appointments` Appointment[]
              - …
            - `qualifiers` Qualifier[], nullable
              - …
            - `reminders` Reminder[]
              - …
            - `formMeetingTypes` Form[]
              - …
            - `locations` string[], required
            - `bookingFrequencies` BookingFrequency[]: @todo this doesn't need to be OneToMany and maybe not even a relation (unless different frequencies per user for a queue are needed)
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `timeFormat` '12' | '24', required
            - `provisionalBookings` ProvisionalBookingPayload[]
              - …
            - `accountOwnerMatchingEnabled` boolean, required: Account-owner matching: route a round-robin booking to the user who already owns the booker's account. Owner is resolved via past meetergo bookings + connected CRMs (Pipedrive, HubSpot, Salesforce). Requires `featureAccess.accountOwnerMatching` (Premium).
            - `accountOwnerMatchingScope` object, required: Where to draw the eligible-owner pool from. Picks one scope per meeting type (no inheritance). - `meetingType`: uses `accountOwnerMatchingUserIds` below - `workspace`: every active member of the meeting type's workspace - `company`: every active member of the meeting type's company
            - `accountOwnerMatchingUserIds` string[], required: Eligible owners when scope is `meetingType`. Ignored otherwise.
            - `accountOwnerMatchingAllowNonQueueHosts` boolean, required: If false (default), matched owners must also be queue members for the round-robin to route to them. If true, a matched pool user can host even when not in the queue.
            - `accountOwnerMatchingFallback` object, required: Behaviour when no owner matches (or matched owner has no slots). - `normalRoundRobin`: fall back to standard host availability - `showEmpty`: show no slots
            - `salesforceHostPoolEnabled` boolean, required: Draw the bookable hosts for this meeting type from Salesforce instead of from the statically configured queue: the booking page supplies an account key, and the pool is the account's team as {@link salesforceHostPool} describes it. The resolved pool is applied as a queue subset, so round-robin, fairness and host validation all keep working on it unchanged. Every failure — no connection, no match, timeout, nobody left after filtering — falls back to the full queue. Wrong advisor is recoverable in the CRM, a lost lead is not. Requires `featureAccess.salesforceRouting`.
            - `salesforceHostPool` SalesforceHostPoolSetting
              - …
            - `internalNotes` string, nullable: Internal note for organizers. Never shown to customers. Used for project keys, partner info, briefing notes, etc.
            - `replyTo` string, nullable: Override the Reply-To address for notification emails sent for this meeting type. When set, takes precedence over the company `useNoReplyAddress` flag and the default host email fallback. Useful for sales/support teams that want replies routed to a shared inbox instead of the individual host.
            - `enableWithdrawalButton` boolean, required: Show the EU/German withdrawal button ("Widerrufsbutton") on the booking confirmation page. Required from 19 June 2026 (§ 356a BGB) for B2C distance contracts with a right of withdrawal. Off by default. Hosts must opt in per meeting type. Disable for leisure-services tied to a fixed date (concerts, events) which are exempt under § 312g Abs. 2 Nr. 9 BGB.
            - `id` string, required
            - `activationSessionId` string, nullable, required
            - `activationResourceKey` string, nullable, required
            - `crmOptions` MeetingCRMOptions, required
              - …
            - `ics` IcsInfo, required
              - …
            - `daysIntoFuture` DaysIntoFutureSetting, required
              - …
            - `enableCalendarInviteNotification` boolean, required: New notification setting - use calendar invites instead of email notifications. When this option is enabled, booking confirmation notification and cancellation notification are handled by calendar connection. A google/outlook connection is required. It will be used to create calendar invite with notification. All meeting types pre-migration use email notifications. Introduced in order to mitigate duplicate invites, cancellation handling in calendar and overall improve both host and attendee experience.
            - `useSchedule` boolean, required: Override individual host's availability If true, availability selected for that meeting type will be used as common availability for all hosts. Move to meetingOptions?
            - `useCompanyLogo` boolean, required: Move to meetingOptions?
            - `useTeamLogo` boolean, required: Show team logo instead of company logo (for queue/round-robin meeting types)
            - `useCustomInvite` boolean, required: Move to meetingOptions?
            - `disableActionButtons` boolean, required: Move to meetingOptions?
            - `showConfetti` boolean, required: Whether to show confetti animation on the confirmation page
            - `useCustomForm` boolean, required
            - `formId` string
            - `inCompanyPage` boolean, required
            - `formFirst` boolean, required: Move to meetingOptions?
            - `communicationSettings` CommunicationSettings
              - …
            - `communicationSettingsId` number, nullable, required
            - `availabilityId` string
            - `companyId` string, required: Not null
            - `isFavorite` boolean: Computed at query time per user — not persisted on the entity. Populated by the list endpoint via a join with meeting_type_favorite.
            - `queue` Queue
              - …
            - `queueId` string: Not real value as queue is owner
            - `oldQueues` Queue[]
              - …
            - `reminderIds` string[], required
            - `workspace` Workspace
              - …
            - `workspaceId` string
            - `isGlobalQueue` boolean, required: If true, meeting type is round robin/collective, which means it has a queue. However, it doesn't belong to a workspace, so it is available to the whole company. May not be needed but using explicit value for clarity for now.
            - `user` User (recursive)
            - `userId` string, nullable
            - `createdBy` User (recursive)
            - `createdById` string, required
            - `compact` boolean, required
            - `disableTeamsEmail` boolean, required: If enabled, booking confirmation email won't be sent for Microsoft Teams meeting
            - `calendarGuestListVisible` boolean, required: Whether guests on the synced calendar event can see each other. Off by default so strangers on a group booking never see each other's address; on for teams that invite colleagues and known contacts who expect a normal shared invite. Only Google honours it; Outlook has no such flag.
            - `languageSetting` LanguageSetting, required
              - …
            - `customTextSelectChannel` CustomTextSelectChannelSetting, required
              - …
            - `timezonePreset` TimezonePresetSetting, required
              - …
            - `cssSetting` CssSetting, required
              - …
            - `meetingPaymentInfo` MeetingPaymentInfo, required
              - …
            - `template` MeetingType (recursive)
            - `templateId` string, nullable
            - `lockedFields` string[], required: Field keys locked by the template admin. Copied from the template during assignment and push. Users cannot modify fields in locked sections.
            - `metaSetting` MetaSetting, required
              - …
            - `metadata` object, nullable: Custom key-value metadata for API integrations. Similar to Stripe's metadata field. Included in webhook payloads.
            - `importSource` string: Defined if imported from another provider. Currently mainly statistical purpose.
            - `videoSetting` VideoSetting, required
              - …
            - `cancellationPolicy` CancellationPolicySetting, required
              - …
            - `reschedulingPolicy` ReschedulingPolicySetting, required
              - …
            - `recurringSetting` RecurringSetting, required
              - …
            - `enableEmailBookingLimit` boolean, required
            - `maxBookingsPerEmail` number, nullable
            - `emailBookingLimitPeriod` object
            - `emailBookingLimitPeriodCount` number, nullable: How many `emailBookingLimitPeriod` units the limit spans, so a window like "6 months" is expressible. Null or 1 counts from the calendar boundary; above 1 counts back from now. See `email-booking-limit-window.ts`.
            - `resourceChannels` ResourceChannel[]
              - …
          - `meetingTypeTemplate` MeetingTypeTemplate
            - `meetingInfo` MeetingInfo, required
              - …
            - `meetingOptions` MeetingOptions, required
              - …
            - `timeslotInterval` number, required
            - `spots` number, required
            - `bookingMinimum` number, required: Time in minutes. Default 30 mins
            - `schedule` WeeklySchedule, required
              - …
            - `hideAddressField` boolean, required: Hide address field for LOCAL_ATTENDEE channel type
            - `form` Form
              - …
            - `availability` Availability (recursive)
            - `reminders` Reminder[]
              - …
            - `locations` string[], required
            - `bookingFrequencies` HostBookingFrequency[]: @todo this doesn't need to be OneToMany and maybe not even a relation (unless different frequencies per user for a queue are needed)
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `connectGuestJoinMinutesBeforeStart` number, nullable: Guest may enter this many minutes before a scheduled Connect call.
            - `connectGuestJoinMinutesAfterEnd` number, nullable: Guest may enter until this many minutes after a scheduled Connect call.
            - `timeFormat` '12' | '24', required
            - `useHostAvailability` boolean, required
            - `meetingTypes` MeetingType[]
              - …
            - `id` string, required
            - `crmOptions` MeetingCRMOptions, required
              - …
            - `ics` IcsInfo, required
              - …
            - `daysIntoFuture` DaysIntoFutureSetting, required
              - …
            - `enableCalendarInviteNotification` boolean, required: New notification setting - use calendar invites instead of email notifications. When this option is enabled, booking confirmation notification and cancellation notification are handled by calendar connection. A google/outlook connection is required. It will be used to create calendar invite with notification. All meeting types pre-migration use email notifications. Introduced in order to mitigate duplicate invites, cancellation handling in calendar and overall improve both host and attendee experience.
            - `useSchedule` boolean, required: Override individual host's availability If true, availability selected for that meeting type will be used as common availability for all hosts. Move to meetingOptions?
            - `useCompanyLogo` boolean, required: Move to meetingOptions?
            - `useTeamLogo` boolean, required: Show team logo instead of company logo (for queue/round-robin meeting types)
            - `useCustomInvite` boolean, required: Move to meetingOptions?
            - `disableActionButtons` boolean, required: Move to meetingOptions?
            - `showConfetti` boolean, required: Whether to show confetti animation on the confirmation page
            - `enableWithdrawalButton` boolean, required: Show the EU/German withdrawal button on the booking confirmation page (§ 356a BGB, from 19.06.2026)
            - `useCustomForm` boolean, required
            - `formId` string
            - `inCompanyPage` boolean, required
            - `formFirst` boolean, required: Move to meetingOptions?
            - `communicationSettings` CommunicationSettings
              - …
            - `communicationSettingsId` number, nullable, required
            - `availabilityId` string
            - `companyId` string, required: Not null
            - `reminderIds` string[], required
            - `createdBy` User (recursive)
            - `createdById` string, required
            - `compact` boolean, required
            - `disableTeamsEmail` boolean, required: If enabled, booking confirmation email won't be sent for Microsoft Teams meeting
            - `calendarGuestListVisible` boolean, required: Whether guests on the synced calendar event can see each other. Off by default so strangers on a group booking never see each other's address; on for teams that invite colleagues and known contacts who expect a normal shared invite. Only Google honours it; Outlook has no such flag.
            - `enableWaitlist` boolean, required
            - `runAsWebinar` boolean, required: Run bookings of this meeting type as a Connect webinar. Stamped onto members' meeting types like every other shared setting — a setting that lived only on MeetingType would silently drop when a template is applied.
            - `webinarDefaults` WebinarSettings, required
              - …
            - `notetakerRecording` 'on' | 'off', nullable, required: Whether the notetaker joins bookings of members' meeting types. Null lets each host's own default decide. Stamped like every other shared setting.
            - `languageSetting` LanguageSetting, required
              - …
            - `customTextSelectChannel` CustomTextSelectChannelSetting, required
              - …
            - `timezonePreset` TimezonePresetSetting, required
              - …
            - `cssSetting` CssSetting, required
              - …
            - `meetingPaymentInfo` MeetingPaymentInfo, required
              - …
            - `videoSetting` VideoSetting, required
              - …
            - `cancellationPolicy` CancellationPolicySetting, required
              - …
            - `reschedulingPolicy` ReschedulingPolicySetting, required
              - …
            - `recurringSetting` RecurringSetting, required
              - …
            - `templateType` 'individual' | 'team', required
            - `queueType` 'simpleRoundRobin' | 'advancedRoundRobin' | 'equalDistribution', nullable
            - `assignedToCount` number: number of meetings assigned to this template
            - `lockedFields` string[], required: Field keys locked by the admin. Users cannot modify the corresponding settings on meeting types created from this template. Copied to meeting types on assign and push.
            - `slug` string, nullable: Optional canonical URL slug. When set, this is the source of truth for the URL of meeting types created from (or synced from) this template: - Used as the base slug on `assignHost` / `assignWorkspace`. - Propagated to assigned meeting types on push. When NULL, slugs behave exactly as before: derived from the template name on assign, never touched on push. Per-user uniqueness still applies via `getAvailableMeetingTypeSlug`.
            - `resourceChannels` ResourceChannel[]
              - …
          - `resourceChannels` ResourceChannel[], required
            - `name` string, nullable: Optional name to show for resource channel, fall back to user name
            - `resource` User, required (recursive)
            - `groupName` string: If resource channel is part of a group, this is the name of the group. Multiple resource channels can be part of the same group, in which case the name is the same. Multiple resources with the same group name are grouped and only the group name is shown as a resource. Any resource within the group can be selected according to the items `groupIndex`. If resource channel is the only group member, this has no effect.
            - `groupMemberIndex` number: If resource channel is part of a group, this is the zero-based index to determine order that resource channel will be chosen within the group. Group members with index 0 will be chosen first, then 1, then 2, etc. If `groupName` is null, this has no effect.
            - `availability` Availability (recursive)
            - `meetingType` MeetingType
              - …
            - `meetingTypeTemplate` MeetingTypeTemplate
              - …
            - `id` string, required
            - `resourceId` string, required
            - `availabilityId` string
            - `meetingTypeId` string
            - `meetingTypeTemplateId` string
        - `appointments` Appointment[]
          - `start` string, required
          - `end` string, required
          - `isCancelled` boolean, required: Determines whether appointment was cancelled
          - `spots` string, nullable: Per-appointment capacity for group bookings. Copied from meetingType.spots at creation time. Can be reduced to limit new bookees without cancelling existing attendees. When null, falls back to meetingType.spots. TODO: Ideally spots should be part of MeetingInfo (alongside groupBooking) so it gets copied naturally via the meetingInfo spread. Kept as top-level for now to avoid refactoring the existing meetingType.spots column + V4 API DTOs.
          - `rescheduledAt` string, date-time: Timestamp when appointment was rescheduled. Null if appointment is not rescheduled.
          - `rescheduledBy` string, nullable: Email address of the attendee, host, or operator who last rescheduled it.
          - `rescheduleCount` number, required: Number of times this appointment has been rescheduled. Used as the ICS SEQUENCE number so calendar clients recognize updates.
          - `bookedByUserId` string, nullable
          - `emailDeliveryIssues` object[], nullable: Mails the provider could not deliver for this appointment, one entry per address and kind. Written from AhaSend bounce webhooks so a typo in the attendee's address is visible before the appointment is missed.
          - `location` string
          - `scheduledReminders` ScheduledReminder[]
            - `reminder` Reminder
              - …
            - `appointment` Appointment (recursive)
            - `provisionalBooking` ProvisionalBookingPayload
              - …
            - `scheduledEmailIds` string[], required
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `id` number, required
            - `type` object, required: Whether reminder is scheduled in email state machine or sms state machine (sms state machine is a general one but called sms here for simplicity). At the time of migration all scheduled reminders are email.
            - `reminderId` number, nullable
            - `appointmentId` string, nullable, required
            - `provisionalBookingId` string, nullable, required
            - `estimatedSmsSegments` number, nullable, required: Estimate for the exact personalized SMS queued by this row.
          - `createdAt` string, date-time, required
          - `updatedAt` string, date-time, required
          - `deletedAt` string, nullable
          - `couponCode` string: Coupon code applied to this booking's payment (null if no coupon used)
          - `couponDiscountAmount` number: Discount amount applied via coupon (in the payment currency)
          - `note` string
          - `transcription` string, nullable: Full meeting transcription in markdown format
          - `summary` string, nullable: AI-generated summary of the meeting in markdown format
          - `actualStartAt` string, date-time, nullable: When the conversation actually ran, as whatever was in the call witnessed it: the first captured word for a recording, the first person in the room for a Connect call. `start` and `end` say when the meeting was booked for; these say what happened. Null until something that was there reports, and permanently null for a call nothing attended.
          - `actualEndAt` string, date-time, nullable
          - `attendanceCheckedAt` string, date-time, nullable: When something that could have witnessed the call finished looking. What tells "nobody came" apart from "nothing was watching": a past booking with this set and no `actualStartAt` is one a notetaker or the Connect room waited for and nobody joined, which the dashboard offers as a no-show to confirm. Never marks the no-show itself — that reaches the contact and their automations, and stays a person's decision.
          - `miraBrief` string, nullable: Mira's pre-meeting brief: who is coming, what was discussed last time, what is open. Written once on first request and kept, so opening the booking twice does not pay for the model twice.
          - `miraBriefAt` string, date-time, nullable
          - `occurrenceNumber` number, nullable: Position of this appointment within the recurring series (1-based). Null for non-recurring appointments.
          - `isBundleBooking` boolean, required
          - `id` string, required
          - `secret` string, required
          - `ics` string, required
          - `meetingInfo` MeetingInfo, required
            - `name` string, required: Name of the meeting type
            - `description` string, required: Description of the meeting type
            - `nameLocalised` LocalisedContent
              - …
            - `descriptionLocalised` LocalisedContent
              - …
            - `enableRedirect` boolean, required: Enable redirect after booking completion
            - `redirect` string, required: Redirect URL to navigate after booking
            - `passEventDetailsToRedirect` boolean, required: Pass event details (invitee name, email, form answers) as query params to redirect URL
            - `duration` number, required: Primary duration in minutes
            - `channel` 'local' | 'local-attendee' | 'google' | 'zoom' | 'phone' | 'phone-incoming' | 'whatsapp' | 'connect' | 'webex' | 'skypeForConsumer' | 'skypeForBusiness' | 'teamsForBusiness' | 'teamsForBusiness2' | 'teamsForExchange' | 'teams2ForExchange' | 'custom' | 'resource' | 'whereby' | 'kmeet' | 'zava' | 'jitsi' | 'nextcloudTalk' | 'openTalk' | 'alfaview', required: Meeting channel/location type
            - `customChannelName` string, required: Custom name for the meeting channel
            - `customChannelLink` string, required: Custom link for the meeting channel
            - `connectChannelName` string, required: Custom display name for the connect channel
            - `groupBooking` boolean, required: Enable group booking (multiple attendees per slot)
            - `showAvailableSlots` boolean, required: Show available spots count on the booking page
            - `enrichInvitee` boolean, required: Enable invitee data enrichment
            - `autoCreateSpace` boolean, required: Auto-create a Space when someone books
            - `spacePresetId` object, nullable: Space preset id used for the auto-created Space
            - `spaceAutoInvite` boolean, required: Publish the auto-created Space and invite the booker
            - `confirmationButton` ConfirmationButton, required
              - …
            - `bufferBefore` number, required: Buffer time before meeting in minutes
            - `bufferAfter` number, required: Buffer time after meeting in minutes
            - `bufferMustStayWithinWorkingHours` boolean, required: Only offer slots where buffer fits within working hours (no spillover into breaks or after-hours)
            - `rollingSlots` boolean, required: Restart the slot grid after each appointment instead of keeping a fixed grid per working block
            - `color` object, nullable: Color for the meeting type in RGB format, e.g. "rgb(0, 153, 255)"
          - `hostingInfo` HostingInfo, required
            - `mainHost` object, nullable
            - `meetingLink` string, nullable
            - `googleEventId` string, nullable
            - `googleConnectionSub` string, nullable
            - `calendarConnectionId` string, nullable
            - `zoomMeetingId` string, nullable
            - `connectChannelId` string, nullable
            - `wherebyMeetingId` string, nullable
            - `alfaviewMeetingId` string, nullable
            - `alfaviewModeratorLink` string, nullable
          - `cancel` BookingActionInfo, required
            - `actionAt` string, date-time, required
            - `actionBy` object, required
            - `actionSource` 'attendee' | 'company' | 'host', required
            - `reason` string, required
          - `crmOptions` MeetingCRMOptions, required
            - `hubspotContactSync` boolean, required
            - `hubspotContactStage` string, required
            - `hubspotCompanySync` boolean, required
            - `hubspotCompanyStage` string, required
            - `hubspotCompanyMatchingField` string, required
            - `hubspotDealSync` boolean, required
            - `hubspotDealStage` string, required
            - `hubspotPipelineId` string, required
            - `pipedriveDealSync` boolean, required
            - `pipedriveContactSync` boolean, required
            - `pipedrivePipelineId` number, nullable, required
            - `pipedriveStageId` number, nullable, required
            - `pipedriveUseExistingDeal` boolean, required
            - `closeDealSync` boolean, required
            - `closeContactSync` boolean, required
            - `closePipelineId` object, nullable, required
            - `closeStatusId` object, nullable, required
            - `closeUseExistingDeal` boolean, required
            - `centralStationContactSync` boolean, required
            - `centralStationDealSync` boolean, required
            - `centralStationPipelineId` number, nullable, required
            - `centralStationStageId` number, nullable, required
            - `linkandoSyncEnabled` boolean, required
            - `linkandoPlaybookId` number, nullable, required
            - `linkandoTemplateId` number, nullable, required
            - `evalancheEnabled` boolean, required
            - `klicktippEnabled` boolean, required
            - `getResponseEnabled` boolean, required
            - `getResponseCampaignId` string, nullable, required
            - `brevoEnabled` boolean, required
            - `sevdeskEnabled` boolean, required
            - `sevdeskCategoryId` number, nullable, required
            - `lexwareEnabled` boolean, required
            - `professionalWorksEnabled` boolean, required
            - `abAgentaEnabled` boolean, required
            - `smartWeContactSync` boolean, required
            - `smartWeCompanySync` boolean, required
            - `smartWeDealSync` boolean, required
            - `smartWeDealStage` string, required
            - `salesforceSyncEnabled` boolean, required
            - `salesforceCaseSync` boolean, required
            - `salesforceLeadSync` boolean, required
            - `salesforceOpportunitySync` boolean, required
            - `salesforceLeadStatus` string, required
            - `salesforceCaseStatus` string, required
            - `salesforceOpportunityStage` string, required
            - `salesforceDefaultObjectType` string, required
            - `internalCrmPipelineId` object, nullable, required
            - `internalCrmStageId` object, nullable, required
            - `crmOptInFieldName` object, nullable, required
          - `bookedBy` User (recursive)
          - `utmSource` string, nullable
          - `utmMedium` string, nullable
          - `utmCampaign` string, nullable
          - `utmContent` string, nullable
          - `utmTerm` string, nullable
          - `referrerUrl` string, nullable
          - `sourceChannel` object, nullable
          - `formRecipientId` string, nullable: `FormRecipient.id` when a personal form link routed this booking. Without it nothing connects an appointment to the person a printed link was minted for, so a returning recipient could only be offered a second calendar. With it they are shown the booking they already hold. Plain uuid, no FK: an appointment is a real commitment and must survive the deletion of the form that routed it.
          - `attendees` Attendee[]: `cascade: true` exists to INSERT attendees together with a new appointment. Never `save()` an EXISTING appointment with a loaded `attendees` array: the cascade diffs against a stale snapshot and orphan-nullifies attendees linked concurrently (group-booking joins). Link/unlink attendee rows individually instead.
            - `email` string, nullable: Attendee email address. Present for all standard bookings. Null only for phone-only bookings (`BookingDto.phoneOnlyBooking`) where `phone` is the identifier instead.
            - `fullname` string
            - `firstname` string
            - `lastname` string
            - `bringalongEmails` string[], required
            - `notes` object, required
            - `billingDetails` AttendeeBillingDetails
              - …
            - `receiveReminders` boolean, required
            - `phone` string
            - `language` string, required
            - `timezone` string, required
            - `noShow` boolean, required
            - `noShowAt` string, date-time, nullable
            - `dataPolicyAccepted` boolean, required
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `id` string, required
            - `secret` string, required
            - `appointment` Appointment (recursive)
            - `attendeeEnrichment` AttendeeEnrichment
              - …
            - `doubleOptIn` string, date-time: Timestamp when user confirmed double opt in email. Null if user didn't confirm. Only relevant if ^.doubleOptIn is enabled. Otherwise this can be ignored
          - `attendeeIds` string[], required
          - `roundRobinEntries` RoundRobinEntry[]
            - `createdAt` string, date-time, required
            - `id` number, required
            - `userId` string, nullable
            - `group` QueueUserGroup
              - …
            - `groupId` string, nullable
            - `appointmentId` string, nullable
            - `queue` Queue
              - …
            - `queueId` string
            - `selectionReason` string: Reason why host was selected. - `queue`: Host was the next host in queue - `nextAvailableHost`: Next queue host was not available due to calendar event so host was chosen - Past entries are null so that information is not known
          - `meetingType` MeetingType
            - `meetingInfo` MeetingInfo, required
              - …
            - `meetingOptions` MeetingOptions, required
              - …
            - `timeslotInterval` number, required
            - `spots` number, required
            - `enabled` boolean, required
            - `isHidden` boolean, required: Marks meeting types that should be hidden from user-facing UIs. Used for system-generated meeting types (e.g., calgent auto-bookings). When true: - Not shown in meeting type lists (filtered in all queries) - Cannot be directly edited by users via UI - May be managed by automated processes
            - `slug` string, required
            - `bookingMinimum` number, required: Time in minutes. Default 30 mins
            - `roundRobin` string[], required: Used to store user latest booked host ids Last element - lastest booked host There shouldn't be duplicates
            - `schedule` WeeklySchedule, required
              - …
            - `hideAddressField` boolean, required: Hide address field for LOCAL_ATTENDEE channel type
            - `form` Form
              - …
            - `emailQualityGuard` boolean, required: Email Quality Guard - Blocks disposable emails and verifies email addresses exist Only applies when formFirst is enabled (Lead Capture Mode)
            - `enableWaitlist` boolean, required
            - `connectGuestJoinMinutesBeforeStart` number, nullable: Guest may enter this many minutes before a scheduled Connect call.
            - `connectGuestJoinMinutesAfterEnd` number, nullable: Guest may enter until this many minutes after a scheduled Connect call.
            - `runAsWebinar` boolean, required: Run bookings of this meeting type as a Connect webinar (roles, view-only attendees, moderated Q&A, attendance reporting) instead of an ordinary meeting. Only applies when `meetingInfo.channel` is CONNECT. Default false, so every existing meeting type keeps producing ordinary Connect meetings.
            - `webinarDefaults` object, nullable: Default webinar policy stamped onto each booking's Connect room. Null means the product defaults. Only read when `runAsWebinar` is on.
            - `notetakerRecording` 'on' | 'off', nullable: Whether the notetaker joins bookings of this meeting type. Null means the host's own default decides — which is not the same as `off`, and is why this is not a boolean. Stored as `varchar` rather than a Postgres enum for the same reason the notetaker's other vocabularies are: adding a word must never need a migration that locks a table.
            - `availability` Availability
              - …
            - `tags` string[], nullable: User-defined tags for organizing meeting types in the dashboard. Free-form strings, OR semantics for filtering. Mirrors the contact tags pattern.
            - `appointments` Appointment[]
            - `qualifiers` Qualifier[], nullable
              - …
            - `reminders` Reminder[]
              - …
            - `formMeetingTypes` Form[]
              - …
            - `locations` string[], required
            - `bookingFrequencies` BookingFrequency[]: @todo this doesn't need to be OneToMany and maybe not even a relation (unless different frequencies per user for a queue are needed)
              - …
            - `createdAt` string, date-time, required
            - `updatedAt` string, date-time, required
            - `deletedAt` string, nullable
            - `timeFormat` '12' | '24', required
            - `provisionalBookings` ProvisionalBookingPayload[]
              - …
            - `accountOwnerMatchingEnabled` boolean, required: Account-owner matching: route a round-robin booking to the user who already owns the booker's account. Owner is resolved via past meetergo bookings + connected CRMs (Pipedrive, HubSpot, Salesforce). Requires `featureAccess.accountOwnerMatching` (Premium).
            - `accountOwnerMatchingScope` object, required: Where to draw the eligible-owner pool from. Picks one scope per meeting type (no inheritance). - `meetingType`: uses `accountOwnerMatchingUserIds` below - `workspace`: every active member of the meeting type's workspace - `company`: every active member of the meeting type's company
            - `accountOwnerMatchingUserIds` string[], required: Eligible owners when scope is `meetingType`. Ignored otherwise.
            - `accountOwnerMatchingAllowNonQueueHosts` boolean, required: If false (default), matched owners must also be queue members for the round-robin to route to them. If true, a matched pool user can host even when not in the queue.
            - `accountOwnerMatchingFallback` object, required: Behaviour when no owner matches (or matched owner has no slots). - `normalRoundRobin`: fall back to standard host availability - `showEmpty`: show no slots
            - `salesforceHostPoolEnabled` boolean, required: Draw the bookable hosts for this meeting type from Salesforce instead of from the statically configured queue: the booking page supplies an account key, and the pool is the account's team as {@link salesforceHostPool} describes it. The resolved pool is applied as a queue subset, so round-robin, fairness and host validation all keep working on it unchanged. Every failure — no connection, no match, timeout, nobody left after filtering — falls back to the full queue. Wrong advisor is recoverable in the CRM, a lost lead is not. Requires `featureAccess.salesforceRouting`.
            - `salesforceHostPool` SalesforceHostPoolSetting
              - …
- … truncated; see the full OpenAPI document linked below

## Other responses

- `400`: The request did not validate. `message` lists the constraints that failed.
- `401`: Missing, malformed, or expired credentials.
- `403`: Authenticated, but not allowed: the plan does not include this feature, or the token is scoped away from it.
- `404`: No such resource in your account. Resources belonging to another account are reported the same way.
- `429`: Rate limited. Back off and retry.

---

[API](https://skmtc.dev/meetergo/apis/meetergo-platform-api.md) · [All operations](https://skmtc.dev/meetergo/apis/meetergo-platform-api/llms.txt) · [OpenAPI document](https://skmtc.dev/meetergo/apis/meetergo-platform-api/revisions/b5d66cd8be67?raw)
