feat: Add endpoint to add attendees to existing bookings (#24414)

* feat: add endpoint to add attendees to existing bookings

- Created POST /v2/bookings/:bookingUid/attendees endpoint
- Added AddAttendeesInput_2024_08_13 for input validation
- Added AddAttendeesOutput_2024_08_13 for response format
- Created BookingAttendeesService_2024_08_13 for business logic
- Created BookingAttendeesController_2024_08_13 for API endpoint
- Added validation to check for duplicate attendee emails
- Integrated with existing booking and event type repositories
- Added validateAndTransformAddAttendeesInput method to InputBookingsService
- Fixed pre-existing ESLint no-prototype-builtins warnings
- Left placeholder for custom booking field validation logic

Co-Authored-By: somay@cal.com <somaychauhan98@gmail.com>

* refactor: move booking attendee operations to dedicated repository

* refactor: move repository files into dedicated repositories directory

* feat: validate guests field availability before adding attendees to booking

* feat: migrate addAttendees API to use existing addGuests handler

* refactor: remove unused validateAndTransformAddAttendeesInput method from InputBookingsService

* refactor: rename attendees to guests in booking API endpoints and types

* refactor: rename booking-attendees to booking-guests for consistency

* WIP: add e2e tests for add booking guests endpoint

* faet: improve guest booking tests

* refactor: extract getHtml method in email templates

* feat: add email toggle support for guest invites based on OAuth client settings

* refactor: addGuests handler

* feat: add SMS notifications when adding guests to existing bookings

* refactor: rename add-attendees to add-guests for consistent terminology

* refactor: added repository pattern in addGuests.handler

* test: add attendee scheduled email spy to booking guests tests

* fix: use event type team ID instead of user org ID for booking permission check

* Update BookingEmailSmsHandler.ts

* Remove comments

* refactor: rename booking guests to booking attendees

* refactor: rename guest-related methods to use attendees terminology for consistency

* update api docs

* refactor: restructure addGuests handler to top

* refactor: update guest email format to use object structure in booking tests

* docs: clarify API version header requirement for booking attendees endpoint

* docs: add email notification details to booking attendees API documentation

* refactor: rename booking attendees to guests for consistency

* refactor: rename attendees to guests in booking API endpoints

* feat: add email validation for guest invites

* feat: improve error handling for guest booking failures

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2025-10-28 14:16:07 +05:30
1 parent c0530cc91c
commit b620581d7a
34 files changed
+1302 -103

No files matched your search

+199
View File
@@ -8998,6 +8998,64 @@
"tags": ["Bookings"]
}
},
"/v2/bookings/{bookingUid}/guests": {
"post": {
"operationId": "BookingGuestsController_2024_08_13_addGuests",
"summary": "Add guests to an existing booking",
"description": "Add one or more guests to an existing booking.\n \n **Email Notifications:**\n When guests are added, the following notifications are sent (unless disabled by event type settings):\n \n - **Organizer & Team Members:** Receive an \"Add Guests\" notification email informing them that new guests have been added to the booking.\n \n - **New Guests:** Receive a \"Scheduled Event\" email with full booking details and calendar invite. If they have a phone number, they also receive an SMS notification.\n \n - **Existing Guests:** Receive an \"Add Guests\" notification email informing them that additional guests have been added to the booking.\n \n <Note>The cal-api-version header is required for this endpoint. Without it, the request will fail with a 404 error.</Note>\n ",
"parameters": [
{
"name": "cal-api-version",
"in": "header",
"description": "Must be set to 2024-08-13. This header is required as this endpoint does not exist in older API versions.",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "bookingUid",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "Authorization",
"in": "header",
"description": "value must be `Bearer <token>` where `<token>` is api key prefixed with cal_ or managed user access token",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AddGuestsInput_2024_08_13"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AddGuestsOutput_2024_08_13"
}
}
}
}
},
"tags": ["Bookings / Guests"]
}
},
"/v2/calendars/{calendar}/event/{eventUid}": {
"get": {
"operationId": "CalUnifiedCalendarsController_getCalendarEventDetails",
@@ -27285,6 +27343,147 @@
},
"required": ["status", "data"]
},
"Guest": {
"type": "object",
"properties": {
"email": {
"type": "string",
"description": "The email of the guest.",
"example": "john.doe@example.com"
},
"name": {
"type": "string",
"description": "The name of the guest.",
"example": "John Doe"
},
"timeZone": {
"type": "string",
"description": "The time zone of the guest.",
"example": "America/New_York"
},
"phoneNumber": {
"type": "string",
"description": "The phone number of the guest in international format.",
"example": "+919876543210"
},
"language": {
"type": "string",
"enum": [
"ar",
"ca",
"de",
"es",
"eu",
"he",
"id",
"ja",
"lv",
"pl",
"ro",
"sr",
"th",
"vi",
"az",
"cs",
"el",
"es-419",
"fi",
"hr",
"it",
"km",
"nl",
"pt",
"ru",
"sv",
"tr",
"zh-CN",
"bg",
"da",
"en",
"et",
"fr",
"hu",
"iw",
"ko",
"no",
"pt-BR",
"sk",
"ta",
"uk",
"zh-TW",
"bn"
],
"description": "The preferred language of the guest. Used for booking confirmation.",
"example": "it",
"default": "en"
}
},
"required": ["email"]
},
"AddGuestsInput_2024_08_13": {
"type": "object",
"properties": {
"guests": {
"description": "Array of guests to add to the booking",
"example": [
{
"email": "john.doe@example.com",
"name": "John Doe",
"timeZone": "America/New_York"
},
{
"email": "jane.smith@example.com",
"name": "Jane Smith"
}
],
"type": "array",
"items": {
"$ref": "#/components/schemas/Guest"
}
}
},
"required": ["guests"]
},
"AddGuestsOutput_2024_08_13": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "success",
"enum": ["success", "error"]
},
"data": {
"oneOf": [
{
"$ref": "#/components/schemas/BookingOutput_2024_08_13"
},
{
"$ref": "#/components/schemas/RecurringBookingOutput_2024_08_13"
},
{
"type": "array",
"items": {
"$ref": "#/components/schemas/RecurringBookingOutput_2024_08_13"
}
},
{
"$ref": "#/components/schemas/GetSeatedBookingOutput_2024_08_13"
},
{
"$ref": "#/components/schemas/GetRecurringSeatedBookingOutput_2024_08_13"
},
{
"type": "array",
"items": {
"$ref": "#/components/schemas/GetRecurringSeatedBookingOutput_2024_08_13"
}
}
],
"description": "Booking data, which can be either a BookingOutput object, a RecurringBookingOutput object, or an array of RecurringBookingOutput objects"
}
},
"required": ["status", "data"]
},
"CreateTeamMembershipInput": {
"type": "object",
"properties": {