What actually gets written: the Booking model and its relations
The createBooking() call from the trace writes one Booking row, but that row is deliberately thin: its references and seatsReferences relations stay empty at creation time and are filled in later by separate writes once EventManager talks to the external calendar or video provider. This module reads the Booking, BookingReference, Attendee, and BookingSeat models in packages/prisma/schema.prisma to show exactly which fields createBooking() populates immediately and which related tables are populated afterward, and why the application code (createBooking, EventManager, RegularBookingService) is written to match the shape of those Prisma models rather than the reverse.
buildNewBookingData: turning request data into a Prisma.BookingCreateInput
packages/features/bookings/lib/handleNewBooking/createBooking.ts · lines 168–202Before assembling the write, getAttendeesData(evt) and getEventTypeRel(eventType.id) pre-shape two pieces of the incoming request into the exact structures Prisma's relation API expects, so the object literal below can just reference them.
uid is set as a bare scalar — this is the value the caller generated and passed in, landing directly on the Booking.uid @unique column.
startTime/endTime are normalized to UTC with dayjs.utc(...).toDate() before being written, matching the plain DateTime columns on Booking that carry no timezone of their own.
status is decided right here, not left to the schema's @default(ACCEPTED) — a ternary on eventType.isConfirmedByDefault picks BookingStatus.ACCEPTED or BookingStatus.PENDING, which later determines whether RegularBookingService immediately syncs to the calendar or queues a confirmation webhook instead.
eventType: eventTypeRel writes the pre-built { connect: { id: eventTypeId } } object rather than an eventTypeId scalar — Prisma's nested-write API models relations as connect/create instructions, not raw foreign keys, so getEventTypeRel exists purely to produce that shape.
attendees: { createMany: { data: attendeesData } } } is a nested write: Prisma creates one Attendee row per entry in attendeesData inside the same tx.booking.create call, and stamps each with this booking's bookingId — no separate insert is needed for the fan-out to happen.
function buildNewBookingData(params: CreateBookingParams) { const { uid, evt, reqBody, eventType, input, originalRescheduledBooking, rescheduledBy, creationSource, tracking, } = params; const attendeesData = getAttendeesData(evt); const eventTypeRel = getEventTypeRel(eventType.id); const newBookingData: Prisma.BookingCreateInput = { uid, userPrimaryEmail: evt.organizer.email, responses: input.responses === null || evt.seatsPerTimeSlot ? Prisma.JsonNull : input.responses, title: evt.title, startTime: dayjs.utc(evt.startTime).toDate(), endTime: dayjs.utc(evt.endTime).toDate(), description: evt.seatsPerTimeSlot ? null : evt.additionalNotes, customInputs: isPrismaObjOrUndefined(evt.customInputs), status: eventType.isConfirmedByDefault ? BookingStatus.ACCEPTED : BookingStatus.PENDING, oneTimePassword: evt.oneTimePassword, location: evt.location, eventType: eventTypeRel, smsReminderNumber: input.smsReminderNumber, metadata: reqBody.metadata, attendees: { createMany: { data: attendeesData, }, },
model BookingReference { id Int @id @default(autoincrement()) /// @zod.string.min(1) type String /// @zod.string.min(1) uid String meetingId String? thirdPartyRecurringEventId String? meetingPassword String? meetingUrl String? booking Booking? @relation(fields: [bookingId], references: [id], onDelete: Cascade) bookingId Int? externalCalendarId String? deleted Boolean? credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) credentialId Int? delegationCredential DelegationCredential? @relation(fields: [delegationCredentialId], references: [id], onDelete: SetNull) delegationCredentialId String? @@index([bookingId]) @@index([type]) @@index([uid])}
packages/prisma/schema.prisma · lines 801–824type (e.g. "google_calendar" or "daily_video"), uid, meetingId, and meetingUrl are the external provider's own identifiers for the calendar or video event; bookingId is the foreign key tying that identity back to one Booking. This is the table EventManager and later webhook code query to answer "which Google Calendar event / Daily.co room belongs to this booking" without ever re-deriving it from scratch.try { if (!isDryRun) { await deps.prismaClient.booking.update({ where: { uid: booking.uid, }, data: { location: evt.location, metadata: { ...(typeof booking.metadata === "object" && booking.metadata), ...metadata }, references: { createMany: { data: referencesToCreate, }, }, }, }); }
packages/features/bookings/lib/service/RegularBookingService.ts · lines 2444–2460createBooking()'s transaction already committed the Booking and Attendee rows — booking.uid here is the uid from that earlier write. RegularBookingService calls eventManager.create(evt, ...) in between (not shown in this excerpt), collects its referencesToCreate array, and only now issues a second deps.prismaClient.booking.update whose nested references: { createMany } writes the BookingReference rows. The booking's identity in your database and its identity in the external calendar are deliberately written in two separate steps, because the second one depends on a network call that can fail independently of the first.