{"record":{"id":"b870190bbbe67724","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed","errorCode":"error-not-allowed","errorMessage":"error-not-allowed","messagePattern":"error-not-allowed","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/ee/server/api/v1/omnichannel/transcript.ts","lineNumber":24,"sourceCode":"import { requestPdfTranscript } from '../../../lib/omnichannel/requestPdfTranscript';\n\nAPI.v1.addRoute(\n\t'omnichannel/:rid/request-transcript',\n\t{ authRequired: true, permissionsRequired: ['request-pdf-transcript'], license: ['livechat-enterprise'] },\n\t{\n\t\tasync post() {\n\t\t\tconst room = await LivechatRooms.findOneById<Pick<IOmnichannelRoom, '_id' | 'open' | 'v' | 't' | 'pdfTranscriptFileId'>>(\n\t\t\t\tthis.urlParams.rid,\n\t\t\t\t{\n\t\t\t\t\tprojection: { _id: 1, open: 1, v: 1, t: 1, pdfTranscriptFileId: 1 },\n\t\t\t\t},\n\t\t\t);\n\t\t\tif (!room) {\n\t\t\t\tthrow new Error('error-invalid-room');\n\t\t\t}\n\n\t\t\tif (!(await canAccessRoomAsync(room, { _id: this.userId }))) {\n\t\t\t\tthrow new Error('error-not-allowed');\n\t\t\t}\n\n\t\t\t// Flow is as follows:\n\t\t\t// 1. On Test Mode, call Transcript.workOnPdf directly\n\t\t\t// 2. On Normal Mode, call QueueWorker.queueWork to queue the work\n\t\t\t// 3. OmnichannelTranscript.workOnPdf will be called by the worker to generate the transcript\n\t\t\t// 4. We be happy :)\n\t\t\tawait requestPdfTranscript(room, this.userId);\n\n\t\t\treturn API.v1.success();\n\t\t},\n\t},\n);\n","sourceCodeStart":6,"sourceCodeEnd":38,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/api/v1/omnichannel/transcript.ts#L6-L38","documentation":"Even with the request-pdf-transcript permission, the caller must pass canAccessRoomAsync(room, user) for the freshly loaded room; otherwise error-not-allowed. Feature permission and per-room access are checked separately: the first gates the route, the second gates this specific conversation.","triggerScenarios":"An admin holding request-pdf-transcript who cannot access the target conversation (not the serving agent, not a member, and no omnichannel access rights that satisfy canAccessRoom for that room) calls POST /api/v1/omnichannel/<rid>/request-transcript.","commonSituations":"Back-office roles granted the transcript permission without livechat room access; requesting transcripts for other departments' conversations; service accounts with minimal roles.","solutions":["Make the request as a user who can access the room (e.g. the serving agent or a manager whose access covers that conversation).","Extend the caller's roles so canAccessRoom passes for that omnichannel room (manager role, department membership, or the access rights your workspace uses).","Sanity-check with rooms.info as the same user — if they cannot read the room, the transcript call will fail too."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await api.post(`/v1/omnichannel/${encodeURIComponent(rid)}/request-transcript`);\n} catch (e) {\n  if (e?.response?.data?.errorType === 'error-not-allowed') {\n    // the caller cannot access this room: retry as the serving agent\n    // or extend the user's roles so canAccessRoom passes\n  } else throw e;\n}","preventionTips":["Feature permission (request-pdf-transcript) does not grant room access; check both.","Trigger transcript generation as a user who can read the room (rooms.info as that user is a good probe).","For automated flows, use service identities whose roles cover the target conversations."],"tags":["omnichannel","transcript","pdf","authorization","access-control"],"backgroundTag":"permission-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}