feat: api v2 GET booking attendees endpoint (#27664)

* feat: add booking attendees endpoint to API v2

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* feat: add rate limiting to booking attendees endpoint

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* refactor: simplify attendees output to id, bookingId, name, email, timeZone

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* test: add E2E tests for booking attendees endpoint

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* chore: update bookings repository

* fixup: add pbac guards and update service logic

* chore: update openapi spec

* test: add rate limiting E2E test for booking attendees endpoint

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: tests

* fix: return 404 instead of 403 for non-existent booking in BookingPbacGuard

The BookingPbacGuard was returning 403 (Forbidden) for non-existent bookings
because doesUserIdHaveAccessToBooking returns false when a booking doesn't
exist, which the guard treated as an access denial.

Added an explicit booking existence check in the guard before the access
check, so non-existent bookings now correctly return 404 (Not Found) as
documented in the PR description.

Updated the E2E test to expect 404 for non-existent booking UIDs.

Issue identified by cubic.

Co-Authored-By: unknown <>

* fixup

* fix: return 404 instead of 403 for non-existent booking in attendees endpoint

BookingPbacGuard now checks booking existence before the access check,
returning 404 (Not Found) instead of 403 (Forbidden) for non-existent
booking UIDs. Updated the E2E test assertion and description to match.

Issue identified by cubic (confidence 9/10).

Co-Authored-By: unknown <>

* chore: implement PR feedback

* chore: update tests

* fixup

* chore: update endpoint decsription

* feat: endpoint to retrieve specific attendee

* chore: update e2e tests

* chore: implement cubic feedback

* fix: update test to expect 403 for non-existent booking UID (BookingPbacGuard behavior)

Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>

* fix: merge conflicts

* feat: endpoint to get attendees

* chore: update findByUidIncludeEventTypeAttendeesAndUser method

* chore: implement PR feedback

* fix: e2e tests

* chore: update e2e tests

* fixup fixup

* fix: remove phoneNumber assertion since it's optional and not provided in test

* chore: implement PR feedback

* fix: keep the same output shape for get attendees and get attendee endpoint

* chore: update openapi spec

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
This commit is contained in:
authored and GitHub committed 2026-03-06 09:50:29 +02:00
1 parent 658e65be4d
commit 5a7e783a0a
11 files changed
+906 -29

No files matched your search

+217
View File
@@ -10235,6 +10235,52 @@
}
},
"/v2/bookings/{bookingUid}/attendees": {
"get": {
"operationId": "BookingAttendeesController_2024_08_13_getBookingAttendees",
"summary": "Get all attendees for a booking",
"description": "Retrieve all attendees for a specific booking by its UID.\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"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetBookingAttendeesOutput_2024_08_13"
}
}
}
}
},
"tags": ["Bookings / Attendees"]
},
"post": {
"operationId": "BookingAttendeesController_2024_08_13_addAttendee",
"summary": "Add an attendee to a booking",
@@ -10292,6 +10338,62 @@
"tags": ["Bookings / Attendees"]
}
},
"/v2/bookings/{bookingUid}/attendees/{attendeeId}": {
"get": {
"operationId": "BookingAttendeesController_2024_08_13_getBookingAttendee",
"summary": "Get a specific attendee for a booking",
"description": "Retrieve a specific attendee by their ID for a booking identified by its UID.\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": "attendeeId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
},
{
"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"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetBookingAttendeeOutput_2024_08_13"
}
}
}
}
},
"tags": ["Bookings / Attendees"]
}
},
"/v2/bookings/{bookingUid}/guests": {
"post": {
"operationId": "BookingGuestsController_2024_08_13_addGuests",
@@ -31580,6 +31682,121 @@
},
"required": ["status", "data"]
},
"BookingAttendeeWithId_2024_08_13": {
"type": "object",
"properties": {
"name": {
"type": "string",
"example": "John Doe"
},
"email": {
"type": "string",
"example": "john@example.com"
},
"displayEmail": {
"type": "string",
"example": "john@example.com",
"description": "Clean email for display purposes"
},
"timeZone": {
"type": "string",
"example": "America/New_York"
},
"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"
],
"example": "en"
},
"absent": {
"type": "boolean",
"example": false
},
"phoneNumber": {
"type": "string",
"example": "+1234567890"
},
"id": {
"type": "number",
"example": 251
}
},
"required": ["name", "email", "displayEmail", "timeZone", "absent", "id"]
},
"GetBookingAttendeesOutput_2024_08_13": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "success",
"enum": ["success", "error"]
},
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BookingAttendeeWithId_2024_08_13"
}
}
},
"required": ["status", "data"]
},
"GetBookingAttendeeOutput_2024_08_13": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "success",
"enum": ["success", "error"]
},
"data": {
"$ref": "#/components/schemas/BookingAttendeeWithId_2024_08_13"
}
},
"required": ["status", "data"]
},
"AddAttendeeInput_2024_08_13": {
"type": "object",
"properties": {