{"record":{"id":"a39a17d98f3e0e20","repo":"calcom/cal.diy","slug":"you-do-not-have-permission-to-reassign-this-bookin","errorCode":null,"errorMessage":"You do not have permission to reassign this booking","messagePattern":"You do not have permission to reassign this booking","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"apps/api/v2/src/platform/bookings/2024-08-13/services/bookings.service.ts","lineNumber":1011,"sourceCode":"  async reassignBooking(bookingUid: string, reassignedByUser: ApiAuthGuardUser) {\n    const booking = await this.bookingsRepository.getByUidWithEventType(bookingUid);\n    if (!booking) {\n      throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`);\n    }\n\n    if (!booking.eventType) {\n      throw new BadRequestException(\n        `Event type with id=${booking.eventTypeId} was not found in the database`\n      );\n    }\n\n    const isAllowed = await this.eventTypeAccessService.userIsEventTypeAdminOrOwner(\n      reassignedByUser,\n      booking.eventType\n    );\n\n    if (!isAllowed) {\n      throw new ForbiddenException(BOOKING_REASSIGN_PERMISSION_ERROR);\n    }\n\n    const platformClientParams = booking.eventTypeId\n      ? await this.platformBookingsService.getOAuthClientParams(booking.eventTypeId)\n      : undefined;\n\n    const emailsEnabled = platformClientParams ? platformClientParams.arePlatformEmailsEnabled : true;\n\n    const profile = this.usersService.getUserMainProfile(reassignedByUser);\n\n    try {\n      await roundRobinReassignment({\n        bookingId: booking.id,\n        orgId: profile?.organizationId || null,\n        emailsEnabled,\n        platformClientParams,\n        reassignedById: reassignedByUser.id,\n        actionSource: \"API_V2\",","sourceCodeStart":993,"sourceCodeEnd":1029,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/bookings/2024-08-13/services/bookings.service.ts#L993-L1029","documentation":"Thrown by BookingsService.reassignBooking when EventTypeAccessService.userIsEventTypeAdminOrOwner(reassignedByUser, booking.eventType) returns false. It is a NestJS ForbiddenException (HTTP 403) carrying the BOOKING_REASSIGN_PERMISSION_ERROR constant message. Reassignment is restricted to users who admin or own the event type that the booking belongs to.","triggerScenarios":"Calling the reassign endpoint authenticated as a user who is neither the owner nor an admin/member of the team that owns the event type; using an OAuth client whose linked user lacks event-type ownership; cross-org access where the caller belongs to a different organization.","commonSituations":"Service-account / OAuth tokens scoped to a non-owner user; team memberships not yet propagated; attempting reassign as a plain member of a team event type that requires admin rights; wrong api key reused across environments.","solutions":["Authenticate the request with a user (or OAuth credential) that is the owner or an admin of the event type.","Confirm team membership / org assignment of the authenticated user covers the event type.","If using an OAuth client, ensure the authorized user is the event-type owner.","Fall back to an account admin performing the reassign, then rotate access if misuse is suspected."],"exampleFix":"// before\nawait apiClient.post(`/v2/bookings/${uid}/reassign`); // 403 for non-owners\n\n// after\nconst me = await apiClient.get('/v2/me').then(r => r.data);\nconst eventType = await apiClient.get(`/v2/event-types/${booking.eventTypeId}`).then(r => r.data);\nconst isOwnerOrAdmin = eventType.ownerId === me.id || (eventType.team?.members ?? []).some(m => m.id === me.id && m.isAdmin);\nif (!isOwnerOrAdmin) throw new Error('Authenticated user cannot reassign this booking');\nawait apiClient.post(`/v2/bookings/${uid}/reassign`);","handlingStrategy":"validation","validationCode":"// Confirm the caller is owner/admin of the event type before reassign\nconst me = await apiClient.get('/v2/me').then(r => r.data);\nconst et = await apiClient.get(`/v2/event-types/${booking.eventTypeId}`).then(r => r.data);\nconst canReassign = et.ownerId === me.id || (et.team?.members ?? []).some(m => m.id === me.id && (m.role === 'ADMIN' || m.role === 'OWNER'));\nif (!canReassign) throw new Error('Caller is not event-type owner/admin');","typeGuard":"function isEventTypeManager(userId: number, et: { ownerId?: number | null; team?: { members?: Array<{ id: number; role?: string }> } | null }): boolean {\n  return et.ownerId === userId || (et.team?.members ?? []).some(m => m.id === userId && (m.role === 'ADMIN' || m.role === 'OWNER'));\n}","tryCatchPattern":"try {\n  await apiClient.post(`/v2/bookings/${uid}/reassign`);\n} catch (err) {\n  if (err.response?.status === 403 && /permission to reassign/i.test(err.response?.data?.message ?? '')) {\n    throw new InsufficientPrivilegesError('Use an event-type owner/admin credential');\n  }\n  throw err;\n}","preventionTips":["Scope OAuth clients / API keys to event-type owners.","Cache the user's team roles and check before reassign flows.","Surface a clear permission error in the UI so users request the right access."],"tags":["authorization","permissions","booking","api-v2","reassign","forbidden"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}