{"record":{"id":"2487ca1d67e9fd1f","repo":"calcom/cal.diy","slug":"authenticated-user-is-not-owner-of-event-type-doe","errorCode":null,"errorMessage":"authenticated user is not owner of event type, does not have memberships in common with owner of the event type, nor does belong to event type's team or org.","messagePattern":"authenticated user is not owner of event type, does not have memberships in common with owner of the event type, nor does belong to event type's team or org\\.","errorType":"http","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts","lineNumber":113,"sourceCode":"  async reserveSlot(input: ReserveSlotInput_2024_09_04, authUserId?: number) {\n    if (input.reservationDuration && !authUserId) {\n      throw new UnauthorizedException(\n        \"reservationDuration can only be used for authenticated requests - use access token, api key or OAuth credentials\"\n      );\n    }\n\n    const eventType = await this.eventTypeRepository.getEventTypeWithHosts(input.eventTypeId);\n    if (!eventType) {\n      throw new NotFoundException(`Event Type with ID=${input.eventTypeId} not found`);\n    }\n\n    if (input.reservationDuration && authUserId) {\n      const canSpecifyCustomReservationDuration = await this.canSpecifyCustomReservationDuration(\n        authUserId,\n        eventType\n      );\n      if (!canSpecifyCustomReservationDuration) {\n        throw new ForbiddenException(\n          \"authenticated user is not owner of event type, does not have memberships in common with owner of the event type, nor does belong to event type's team or org.\"\n        );\n      }\n    }\n\n    const startDate = DateTime.fromISO(input.slotStart, { zone: \"utc\" });\n    if (!startDate.isValid) {\n      throw new BadRequestException(\"Invalid start date\");\n    }\n\n    if (input.slotDuration) {\n      this.validateSlotDuration(eventType, input.slotDuration);\n    }\n\n    const endDate = startDate.plus({ minutes: input.slotDuration ?? eventType.length });\n    if (!endDate.isValid) {\n      throw new BadRequestException(\"Invalid end date\");\n    }","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts#L95-L131","documentation":"A NestJS ForbiddenException (HTTP 403) from SlotsService_2024_09_04.reserveSlot. The caller is authenticated (authUserId present) and requested a custom reservationDuration, but canSpecifyCustomReservationDuration returned false. That helper grants permission only if the user owns the event type, shares a membership with the owner (individual event), or has an accepted team/org membership (team event).","triggerScenarios":"An authenticated user who is neither the event type owner, nor a membership peer of the owner, nor a member of the event type's team/org, sends a reserve request with reservationDuration set.","commonSituations":"Using an end-user's OAuth token instead of the event-type owner's API key; an org admin expecting team permissions but their membership is not yet accepted; reservationDuration set globally on all requests regardless of caller.","solutions":["Only send reservationDuration when the caller is the event type owner or a teammate.","Use the owner's API key / OAuth credentials for privileged booking flows.","For end-user-driven flows, omit reservationDuration and use the default hold.","If a team admin should have access, ensure their team membership is accepted in the DB."],"exampleFix":"// before — using attendee token\nfetch('/v2/slots/reserve', { headers:{ Authorization:`Bearer ${attendeeToken}` }, body: JSON.stringify({ eventTypeId, slotStart, reservationDuration: 10 }) });\n\n// after — owner credentials for custom hold, or drop the field\nfetch('/v2/slots/reserve', { headers:{ 'cal-api-key': ownerApiKey }, body: JSON.stringify({ eventTypeId, slotStart, reservationDuration: 10 }) });","handlingStrategy":"validation","validationCode":"// Mirror the server's ownership check before adding reservationDuration.\nfunction canUseCustomReservationDuration(authUser, eventType) {\n  if (eventType.userId) return authUser.id === eventType.userId; // owner or peer membership (approx)\n  if (eventType.teamId) return authUser.teamMemberships?.some(m => m.teamId === eventType.teamId && m.accepted);\n  return false;\n}\nconst body = { eventTypeId, slotStart };\nif (canUseCustomReservationDuration(authUser, eventType)) body.reservationDuration = duration;","typeGuard":"function isEventTypeOwner(authUserId: number, eventType: { userId?: number | null }): boolean {\n  return eventType.userId != null && authUserId === eventType.userId;\n}","tryCatchPattern":"try {\n  await cal.slots.reserve({ eventTypeId, slotStart, reservationDuration });\n} catch (e) {\n  if (e instanceof HttpError && e.statusCode === 403) {\n    // drop the privileged field and retry with default hold\n    return cal.slots.reserve({ eventTypeId, slotStart });\n  }\n  throw e;\n}","preventionTips":["Use the event-type owner's credentials for flows that set reservationDuration.","Gate reservationDuration behind a local ownership/membership check.","For end-user tokens, omit reservationDuration.","Ensure team memberships are accepted to grant team-event permission."],"tags":["calcom-api","slots","forbidden","authorization","reservation","membership"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}