Multitenant Moderation

Many applications with chat functionality serve multiple customers or organizations. For example, platforms like Slack or SaaS applications like InVision need to ensure that messages remain private between different customer organizations. Stream Chat addresses this through multi-tenant mode, which organizes users into separate teams that operate independently. For more details on multitenancy setup, see our Multitenancy documentation.

This guide explains how you can provide moderation experience for your customers (tenants) by using team-level moderation policies. This will allow you to build a multitenant dashboard for your customers to manage their moderation policies and also allow them to review flagged content for their team. Basically we are providing you the APIs that Stream users to build the dashboard for their customers and you can use these APIs to build the dashboard for your customers however you want.

Limitations and Prerequisites

  • Multi-tenancy is only supported on chat
  • Multi-tenant moderation policies do not support Blocklists and Semantic Phrase lists (Coming Soon ⏰)

Create Moderation Policy

You can create moderation configs at the team level by setting the team field in the UpsertConfig API. Following moderation configuration will be used for all the messages sent to messaging channel type within the team team-a.

Only team admins and moderators can manage team-level policies

Server Side Integration

The server-side integration provides full control over moderation configs and is typically used for:

  • Initial setup of team moderation policies
  • Bulk updates to moderation rules
  • Administrative operations
const client = new StreamChat("api_key", "api_secret");
await client.moderation.upsertConfig({
  key: "chat:messaging",
  team: "team-a",
  ai_text_config: {
    rules: [{ label: "SEXUAL_HARASSMENT", action: "flag" }],
  },
  ai_image_config: {
    rules: [{ label: "Non-Explicit Nudity", action: "flag" }],
  },
});

Client Side Integration

Client side access to API allows you to give control to your customers (tenants) to manage their own moderation policies. This is particularly useful when:

  • Building a self-service moderation dashboard
  • Allowing teams to customize their moderation settings
  • Providing real-time moderation management
const client = new StreamChat("api_key");
await client.connectUser({ id: "user-from-team-a" }, token);

// Get all the moderation configs for the team that the user belongs to
const { configs, next } = await client.moderation.upsertConfig({
  key: "chat:messaging",
  team: "team-a",
  ai_text_config: {
    rules: [{ label: "SEXUAL_HARASSMENT", action: "flag" }],
  },
  ai_image_config: {
    rules: [{ label: "Non-Explicit Nudity", action: "flag" }],
  },
});

Please check AI Text Supported Harm Categories and AI Image Supported Harm Categories for the list of supported harm categories.

Moderation Policy Scopes

The scope of moderation policies is determined by the combination of the config key and team field. Here’s a detailed breakdown:

Config KeyTeam FieldScopeUse Case
chatApplies to all channels across all teamsGlobal moderation policies
chatteam-aApplies to all channels in team-aTeam-wide policies
chat:messagingApplies to messaging channels across all teamsChannel-type specific policies
chat:messagingteam-aApplies to messaging channels in team-aTeam-specific channel type policies
chat:messaging:<channel_id>Applies to channel with id channel_id irrespective of teamChannel-specific policies
chat:messaging:<channel_id>team-aApplies to channel with id channel_id in team-aTeam-specific channel policies

Query Moderation Policies

You can query moderation configs at the team level to:

  • Audit existing moderation policies
  • Review team-specific settings
  • Monitor policy changes

Using Server side integration

The server-side query provides comprehensive access to all moderation configs:

const client = new StreamChat("api_key", "api_secret");

// Get all the moderation configs for all the teams
const { configs, next } = await client.moderation.queryConfigs({}, [], {
  limit: 2,
});

// Get all the moderation configs for the team `team-a`
const { configs, next } = await client.moderation.queryConfigs(
  {
    team: "team-a",
  },
  [{ field: "created_at", direction: -1 }],
  { limit: 2 },
);

Using Client side integration

Client-side queries are restricted to the team context of the authenticated user:

const client = new StreamChat("api_key");
await client.connectUser({ id: "little-lake-2" }, token);

// Get all the moderation configs for the team that the user belongs to
const { configs, next } = await client.moderation.queryConfigs({}, [], {
  limit: 2,
});

Query Review Queue

The review queue is a crucial component of the moderation system that allows teams to:

  • Review flagged content
  • Take action on moderated messages
  • Track moderation history
  • Maintain audit trails

You can query the review queue at the team level to using QueryReviewQueue API.

Server Side Integration

Server-side access provides complete control over the review queue:

const client = new StreamChat("api_key", "api_secret");

// Get all the review queue for all the teams
const { reviews, next } = await client.moderation.queryReviewQueue({}, [], {
  limit: 2,
});

// Get all the review queue for the team `team-a`
const { reviews, next } = await client.moderation.queryReviewQueue(
  { teams: "team-a" },
  [{ field: "created_at", direction: -1 }],
  { limit: 2 },
);

// Get all the review queue for the team `team-a` and `team-b`
const { reviews, next } = await client.moderation.queryReviewQueue(
  { teams: { $in: ["team-a", "team-b"] } },
  [{ field: "created_at", direction: -1 }],
  { limit: 2 },
);

Client Side Integration

The client-side review queue access enables building custom moderation dashboards:

const client = new StreamChat("api_key");
await client.connectUser({ id: "admin-from-team-a" }, token);

// Get all the review queue for all the teams that the user belongs to.
const { reviews, next } = await client.moderation.queryReviewQueue({}, [], {
  limit: 2,
});

// Get all the review queue for the team `team-a`
// This will return all the review queue for the team `team-a`, only if the user belongs to `team-a`
const { reviews, next } = await client.moderation.queryReviewQueue(
  {
    team: "team-a",
  },
  [{ field: "created_at", direction: -1 }],
  { limit: 2 },
);
Consider implementing pagination when displaying review queue items to improve performance and user experience. The `next` parameter can be used to fetch subsequent pages of results.
© Getstream.io, Inc. All Rights Reserved.