calcom/cal.diy · error · ForbiddenException
You do not have permission to reassign this booking
Error message
You do not have permission to reassign this booking
What it means
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.
Source
Thrown at apps/api/v2/src/platform/bookings/2024-08-13/services/bookings.service.ts:1011
async reassignBooking(bookingUid: string, reassignedByUser: ApiAuthGuardUser) {
const booking = await this.bookingsRepository.getByUidWithEventType(bookingUid);
if (!booking) {
throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`);
}
if (!booking.eventType) {
throw new BadRequestException(
`Event type with id=${booking.eventTypeId} was not found in the database`
);
}
const isAllowed = await this.eventTypeAccessService.userIsEventTypeAdminOrOwner(
reassignedByUser,
booking.eventType
);
if (!isAllowed) {
throw new ForbiddenException(BOOKING_REASSIGN_PERMISSION_ERROR);
}
const platformClientParams = booking.eventTypeId
? await this.platformBookingsService.getOAuthClientParams(booking.eventTypeId)
: undefined;
const emailsEnabled = platformClientParams ? platformClientParams.arePlatformEmailsEnabled : true;
const profile = this.usersService.getUserMainProfile(reassignedByUser);
try {
await roundRobinReassignment({
bookingId: booking.id,
orgId: profile?.organizationId || null,
emailsEnabled,
platformClientParams,
reassignedById: reassignedByUser.id,
actionSource: "API_V2",View on GitHub (pinned to 176037d0af)
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.
Example fix
// before
await apiClient.post(`/v2/bookings/${uid}/reassign`); // 403 for non-owners
// after
const me = await apiClient.get('/v2/me').then(r => r.data);
const eventType = await apiClient.get(`/v2/event-types/${booking.eventTypeId}`).then(r => r.data);
const isOwnerOrAdmin = eventType.ownerId === me.id || (eventType.team?.members ?? []).some(m => m.id === me.id && m.isAdmin);
if (!isOwnerOrAdmin) throw new Error('Authenticated user cannot reassign this booking');
await apiClient.post(`/v2/bookings/${uid}/reassign`); Defensive patterns
Strategy: validation
Validate before calling
// Confirm the caller is owner/admin of the event type before reassign
const me = await apiClient.get('/v2/me').then(r => r.data);
const et = await apiClient.get(`/v2/event-types/${booking.eventTypeId}`).then(r => r.data);
const canReassign = et.ownerId === me.id || (et.team?.members ?? []).some(m => m.id === me.id && (m.role === 'ADMIN' || m.role === 'OWNER'));
if (!canReassign) throw new Error('Caller is not event-type owner/admin'); Type guard
function isEventTypeManager(userId: number, et: { ownerId?: number | null; team?: { members?: Array<{ id: number; role?: string }> } | null }): boolean {
return et.ownerId === userId || (et.team?.members ?? []).some(m => m.id === userId && (m.role === 'ADMIN' || m.role === 'OWNER'));
} Try / catch
try {
await apiClient.post(`/v2/bookings/${uid}/reassign`);
} catch (err) {
if (err.response?.status === 403 && /permission to reassign/i.test(err.response?.data?.message ?? '')) {
throw new InsufficientPrivilegesError('Use an event-type owner/admin credential');
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Access denied. Either the team with ID=${teamId} does not ow
- You are not authorized to book this event type. You must be
- BookingPbacGuard - user with id=${user.id} does not have acc
- checkBookingRequiresAuthentication - user is not authorized
- Event type with id=${booking.eventTypeId} was not found in t
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/a39a17d98f3e0e20.
Report an issue: GitHub.