{"record":{"id":"40f7972f8e00fb70","repo":"calcom/cal.diy","slug":"user-with-id-userid-does-not-own-event-type-wit","errorCode":null,"errorMessage":"User with ID=${userId} does not own event type with ID=${eventType.id}","messagePattern":"User with ID=(.+?) does not own event type with ID=(.+?)","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"apps/api/v2/src/platform/event-types/event-types_2024_04_15/services/event-types.service.ts","lineNumber":201,"sourceCode":"      userLevelSelectedCalendars: selectedCalendars,\n      allSelectedCalendars: [...eventTypeSelectedCalendars, ...selectedCalendars],\n    };\n  }\n\n  async deleteEventType(eventTypeId: number, userId: number) {\n    const existingEventType = await this.eventTypesRepository.getEventTypeById(eventTypeId);\n    if (!existingEventType) {\n      throw new NotFoundException(`Event type with ID=${eventTypeId} does not exist.`);\n    }\n\n    this.checkUserOwnsEventType(userId, existingEventType);\n\n    return this.eventTypesRepository.deleteEventType(eventTypeId);\n  }\n\n  checkUserOwnsEventType(userId: number, eventType: Pick<EventType, \"id\" | \"userId\">) {\n    if (userId !== eventType.userId) {\n      throw new ForbiddenException(`User with ID=${userId} does not own event type with ID=${eventType.id}`);\n    }\n  }\n}\n","sourceCodeStart":183,"sourceCodeEnd":205,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/event-types/event-types_2024_04_15/services/event-types.service.ts#L183-L205","documentation":"Thrown by EventTypesService_2024_04_15.checkUserOwnsEventType as ForbiddenException (HTTP 403) when the authenticated userId does not equal eventType.userId. The event type exists and was fetched, but the caller is not its owner. This is the ownership gate shared by update and delete flows in the 2024_04_15 service.","triggerScenarios":"PATCH/DELETE /v2/event-types/:eventTypeId where the event type exists but belongs to another user; an org admin (who can read via getUserEventTypeForAtom) attempting to write — the 04_15 endpoints restrict writes to the owner only; userId mismatch due to a token scoped to a different user.","commonSituations":"An org admin reading a teammate's event type successfully (GET passes) but then attempting PATCH/DELETE (which require ownership); using a service-account token to modify a user-owned resource; assuming team membership implies write access.","solutions":["Use the event type owner's own access token/API key for PATCH and DELETE.","For team/org-level management, use the 2024_06_14 endpoints which broaden authorization, or impersonate the owner via an authorized flow.","Confirm userId in the token matches eventType.userId before attempting the write.","If delegation is required, implement a server-side owner-impersonation endpoint rather than reusing a teammate's token."],"exampleFix":"// before - org admin token used to delete a teammate's event type\nawait api.delete(`/v2/event-types/${teammateEventTypeId}`);\n// after - obtain the owner's token or use a delegated admin endpoint\nawait apiAsOwner.delete(`/v2/event-types/${teammateEventTypeId}`);","handlingStrategy":"validation","validationCode":"// Confirm the token user is the owner before PATCH/DELETE on 2024_04_15 endpoints\nconst me = await api.get('/v2/me');\nconst et = await api.get(`/v2/event-types/${id}`);\nif (et.userId !== me.id) {\n  throw new Error(`token user ${me.id} is not the owner of event type ${id}`);\n}","typeGuard":"function isOwnerOf(tokenUserId: number, eventType: unknown): boolean {\n  return typeof eventType === 'object' && eventType !== null && (eventType as any).userId === tokenUserId;\n}","tryCatchPattern":"try {\n  await api.delete(`/v2/event-types/${id}`);\n} catch (e) {\n  if (e.response?.status === 403) {\n    // not the owner — switch to the owner's token or use a delegated admin endpoint\n  } else throw e;\n}","preventionTips":["Use the 2024_06_14 endpoints for broader org/team admin access.","Store the owner userId alongside each eventTypeId in your client state.","Never assume team membership implies write access on the 04_15 endpoints."],"tags":["nestjs","forbidden","event-types","authorization","ownership"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}