RocketChat/Rocket.Chat · error · Error

not-authorized

Error message

not-authorized

What it means

Thrown by fetchIntegration (apps/meteor/server/api/lib/integrations.ts) when the integration exists but hasIntegrationsPermission(userId, integration) returns false. That check passes only if the caller created the integration themselves or holds the incoming/outgoing integration management permissions (including channel-scoped permission for the channels the integration is configured on). It is a plain Error with the message 'not-authorized', so the REST layer returns it as an untyped failure rather than a Meteor permission error code.

Source

Thrown at apps/meteor/server/api/lib/integrations.ts:37

export const findOneIntegration = async ({
	userId,
	integrationId,
	createdBy,
}: {
	userId: string;
	integrationId: string;
	createdBy?: IUser['_id'];
}): Promise<IIntegration> => {
	const integration = await Integrations.findOneByIdAndCreatedByIfExists({
		_id: integrationId,
		createdBy,
	});
	if (!integration) {
		throw new Error('The integration does not exists.');
	}
	if (!(await hasIntegrationsPermission(userId, integration))) {
		throw new Error('not-authorized');
	}
	return integration;
};

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Perform the operation with the integration creator's account or an admin token
  2. Grant the caller's role manage-incoming-integrations / manage-outgoing-integrations (and the channel-scoped add-* permissions where relevant) in Administration > Permissions
  3. Verify which user ID the auth token maps to — the permission check uses that user, not the body's userId

Example fix

// before: bot token without integration perms
POST /api/v1/integrations.remove { "integrationId": "nS8ML..." } // -> not-authorized

// after: grant role the permission, or use owner/admin credentials
POST /api/v1/integrations.remove (Authorization: owner token) { "integrationId": "nS8ML..." }
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await client.post('/api/v1/integrations.update', { integrationId, ...changes });
} catch (e: any) {
  const msg = e?.response?.data?.error ?? '';
  if (msg === 'not-authorized' || e?.response?.status === 403) {
    throw new ForbiddenError('caller is not the integration owner and lacks manage-*-integrations permissions');
  }
  throw e;
}

Prevention

When it happens

Trigger: A non-owner user without manage-incoming-integrations / manage-outgoing-integrations permissions calls integrations.update, integrations.remove, etc. on an integration someone else created; or the caller has the global permission but lacks the channel-level permission for a channel the integration targets.

Common situations: A custom-bot admin role that was never granted the integration permissions; token belonging to a bot user rather than the integration owner; permission sets changed after the integration was created; enterprise workspace restricting integration management to a small group.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/4d9d4e30fe9ec28e. Report an issue: GitHub.