{"record":{"id":"d7baea5e81dc66ba","repo":"RocketChat/Rocket.Chat","slug":"not-authorized-d7baea","errorCode":"not_authorized","errorMessage":"not_authorized","messagePattern":"not_authorized","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/integrations/outgoing/addOutgoingIntegration.ts","lineNumber":55,"sourceCode":"\t\t\ttriggerWords: Match.Maybe([String]),\n\t\t\tavatar: Match.Maybe(String),\n\t\t\ttoken: Match.Maybe(String),\n\t\t\timpersonateUser: Match.Maybe(Boolean),\n\t\t\tretryCount: Match.Maybe(Number),\n\t\t\tretryDelay: Match.Maybe(String),\n\t\t\tretryFailedCalls: Match.Maybe(Boolean),\n\t\t\trunOnEdits: Match.Maybe(Boolean),\n\t\t\ttargetRoom: Match.Maybe(String),\n\t\t\ttriggerWordAnywhere: Match.Maybe(Boolean),\n\t\t}),\n\t);\n\n\tif (\n\t\t!userId ||\n\t\t(!(await hasPermissionAsync(userId, 'manage-outgoing-integrations')) &&\n\t\t\t!(await hasPermissionAsync(userId, 'manage-own-outgoing-integrations')))\n\t) {\n\t\tthrow new Meteor.Error('not_authorized');\n\t}\n\n\tif (integration.script?.trim()) {\n\t\tvalidateScriptEngine(integration.scriptEngine ?? 'isolated-vm');\n\t}\n\n\tconst integrationData = await validateOutgoingIntegration(integration, userId);\n\n\tconst { insertedId } = await Integrations.insertOne(removeEmpty(integrationData));\n\n\tconst integrationStored = await Integrations.findOne({ _id: insertedId });\n\n\tif (!integrationStored) {\n\t\tthrow new Error('Error inserting integration');\n\t}\n\n\tvoid notifyOnIntegrationChanged({ ...integrationStored, _id: insertedId }, 'inserted');\n","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/integrations/outgoing/addOutgoingIntegration.ts#L37-L73","documentation":"addOutgoingIntegration (creating an outgoing webhook integration) throws bare not_authorized when there is no userId on the connection, or when the caller holds neither 'manage-outgoing-integrations' nor 'manage-own-outgoing-integrations' — either single permission is sufficient. The check runs after check() validation of the payload and before validateOutgoingIntegration.","triggerScenarios":"Logged-out client calling addOutgoingIntegration, or a logged-in user whose roles lack both integration permissions; also when the workspace has integrations enabled but the admin never granted manage-own-outgoing-integrations to regular roles.","commonSituations":"Non-admin users attempting self-service outgoing webhook creation on hardened workspaces; custom scripts invoking the DDP method without login; role changes not yet reflected because the session is stale.","solutions":["Authenticate the client before invoking the method","Grant 'manage-own-outgoing-integrations' (self-service) or 'manage-outgoing-integrations' (full admin) to the caller's role in Administration > Permissions","Have the user log out and back in (or refresh) so permission changes take effect","Use POST /v1/integrations.create with an authorized user's token instead of DDP"],"exampleFix":"// before: button always visible, call fails for unprivileged users\nawait Meteor.callAsync('addOutgoingIntegration', integration);\n\n// after: gate the UI on permission\nconst canAdd = usePermission('manage-outgoing-integrations') || usePermission('manage-own-outgoing-integrations');\n{canAdd && <AddOutgoingIntegrationButton />}","handlingStrategy":"validation","validationCode":"// client\nconst canAdd =\n  usePermission('manage-outgoing-integrations') ||\n  usePermission('manage-own-outgoing-integrations');\n// server\nconst canAdd =\n  !!userId &&\n  ((await hasPermissionAsync(userId, 'manage-outgoing-integrations')) ||\n    (await hasPermissionAsync(userId, 'manage-own-outgoing-integrations')));","typeGuard":null,"tryCatchPattern":"try {\n  await Meteor.callAsync('addOutgoingIntegration', integration);\n} catch (err) {\n  if (err instanceof Meteor.Error && err.error === 'not_authorized') {\n    // request login or the integration permission, then stop\n    return;\n  }\n  throw err;\n}","preventionTips":["Gate integration-creation UI behind the two manage permissions","For self-service webhooks, grant manage-own-outgoing-integrations to the intended role","Refresh sessions after granting permissions — stale sessions keep failing"],"tags":["integrations","outgoing-integrations","permissions","meteor-methods"],"backgroundTag":"permission-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}