Rules
Moderation rules allow you to define automated actions that trigger when certain conditions are met during content moderation. Rules can be scoped to specific configs or applied app-wide. They support conditions based on harm labels, severity levels, and other criteria, with configurable actions and cooldown periods.
Upsert Rule
Create or update a moderation rule. This endpoint uses POST /api/v2/moderation/moderation_rule and allows you to define the rule name, conditions, actions, and other configuration in a single request.
client.moderation.upsert_moderation_rule(GetStream::Generated::Models::UpsertModerationRuleRequest.new(
name: "ban_on_severe_harassment",
config_keys: ["chat:messaging"],
action: GetStream::Generated::Models::RuleBuilderAction.new(
type: "ban",
ban: GetStream::Generated::Models::BanActionRequest.new(timeout: 1440, reason: "Severe harassment")
),
conditions: [GetStream::Generated::Models::RuleBuilderCondition.new(label: "HARASSMENT", severity: "critical")],
enabled: true,
description: "Auto-ban users for critical harassment"
))Request Parameters
| key | required | type | description |
|---|---|---|---|
| name | true | string | Unique name for the rule. |
| config_keys | false | array | List of config keys this rule applies to. If empty, applies to all configs. |
| action | false | object | The action to take when conditions are met. |
| conditions | false | array | List of conditions that trigger the rule (label, severity, etc.). |
| groups | false | array | Condition groups for complex logic. |
| enabled | false | boolean | Whether the rule is active. |
| description | false | string | Human-readable description of the rule. |
| cooldown_period | false | string | Cooldown period between rule triggers (e.g., "1h", "24h"). |
| team | false | string | Team identifier for multi-tenancy. |
Response
| key | type | description |
|---|---|---|
| rule | object | The created or updated moderation rule. |
Get Rule
Retrieve a single moderation rule by its ID. This endpoint uses GET /api/v2/moderation/moderation_rule/{id} and returns the full rule object including its conditions, actions, and metadata.
client.moderation.get_moderation_rule("rule_id")Request Parameters
| key | required | type | description |
|---|---|---|---|
| id | true | string | The unique identifier of the moderation rule. |
Response
| key | type | description |
|---|---|---|
| rule | object | The moderation rule. |
Delete Rule
Delete a moderation rule by its ID. This endpoint uses DELETE /api/v2/moderation/moderation_rule/{id} and permanently removes the rule from the system.
client.moderation.delete_moderation_rule("rule_id")Request Parameters
| key | required | type | description |
|---|---|---|---|
| id | true | string | The unique identifier of the moderation rule to delete. |
Response
| key | type | description |
|---|---|---|
| duration | string | Request duration. |
Query Rules
Query moderation rules with optional filters, sorting, and pagination. This endpoint uses POST /api/v2/moderation/moderation_rules and supports standard query patterns for listing and searching rules.
client.moderation.query_moderation_rules(GetStream::Generated::Models::QueryModerationRulesRequest.new(
filter: { "enabled" => true },
sort: [GetStream::Generated::Models::SortParam.new(field: "created_at", direction: -1)],
limit: 10
))Request Parameters
| key | required | type | description |
|---|---|---|---|
| filter | false | object | Filter conditions. |
| sort | false | array | Sort parameters. |
| limit | false | number | Maximum rules to return. |
| next | false | string | Cursor for pagination. |
Response
| key | type | description |
|---|---|---|
| rules | array | List of moderation rules. |
| next | string | Next cursor for pagination. |
Rule Actions Reference
The following table lists all available action types that can be assigned to a moderation rule. Each action defines what happens when the rule conditions are met.
| action type | description |
|---|---|
| flag | Flag the content for manual review. |
| remove | Automatically remove the content. |
| bounce | Bounce the message back to the sender (chat only). |
| ban | Ban the content creator. |
| shadow_block | Shadow-block the content. |
| webhook_only | Send moderation_rule.triggered without a further enforcement action. See Rule-Triggered Webhooks. |
| custom | Trigger a custom webhook. |
Global vs Config-Specific Rules
Rules can be scoped to apply either globally across your entire application or only to specific moderation configurations.
Global Rules
Global rules apply to all the content in your application, as long as it reaches the moderation system.
{
"name": "Global Spam Detection",
"description": "Applies to all configurations",
"team": "moderation",
"config_keys": [], // Empty array = global rule
"id": "global-spam-detection",
"rule_type": "user",
"enabled": true,
"cooldown_period": "24h",
"conditions": [
// ... conditions
],
"logic": "AND",
"action": {
// ... action definition
}
}Config-Specific Rules
Config-specific rules only apply to the moderation configurations you specify. List the configuration keys in the config_keys array.
E.g., if you have a rule that only applies to chat, you can set the config_keys to ["chat:messaging", "chat:support"], then counters will only be counted for messages sent in the channel types mentioned.
{
"name": "Chat-Only Rule",
"description": "Only applies to chat configurations",
"team": "moderation",
"config_keys": ["chat:messaging", "chat:support"], // Specific configs
"id": "chat-only-rule",
"rule_type": "user",
"enabled": true,
"cooldown_period": "12h",
"conditions": [
// ... conditions
],
"logic": "AND",
"action": {
// ... action definition
}
}Use Cases:
- Global Rules: Account-level violations, severe content policies, cross-platform spam detection
- Config-Specific Rules: Channel-specific rules, different content standards per product area
Basic Rule Structure
Every rule has three main parts:
- Conditions: What behavior to watch for
- Threshold: How many violations before taking action
- Action: What to do when the threshold is reached
Example: Complete Rule Structure
{
"name": "Spam Detection",
"description": "Detects and bans users for spam behavior",
"team": "moderation",
"config_keys": ["chat:messaging", "chat:support"],
"id": "spam-detection",
"rule_type": "user",
"enabled": true,
"cooldown_period": "24h",
"conditions": [
{
"type": "text_rule",
"text_rule_params": {
"threshold": 5,
"time_window": "1h",
"llm_harm_labels": {
"SCAM": "Fraudulent content, phishing attempts, or deceptive practices",
"PLATFORM_BYPASS": "Content that attempts to circumvent platform moderation systems"
}
}
},
{
"type": "content_count_rule",
"content_count_rule_params": {
"threshold": 50,
"time_window": "1h"
}
}
],
"logic": "OR",
"action": {
"type": "ban_user",
"ban_options": {
"duration": 3600,
"reason": "Spam behavior detected",
"shadow_ban": false,
"ip_ban": false
}
}
}This rule:
- Watches for spam and advertising content
- Triggers when a user posts 5+ spam messages within 1 hour or 50+ messages within 1 hour
- Bans the user for 1 hour when triggered
Key Differences
| Aspect | User-Type Rules | Content-Type Rules |
|---|---|---|
| Evaluation Timing | Track over time, trigger when threshold reached | Evaluate immediately per content piece |
| Threshold | Required (e.g., 3 violations in 24h) | Not applicable (immediate evaluation) |
| Time Window | Required (e.g., "24h", "7d") | Not applicable |
| Use Case | Pattern detection, repeated violations | Immediate content filtering |
| Actions | User actions (ban_user, flag user) | Content actions (flag content, block_content) |
Action Selection Guidelines
- User-Type Rules: Use user actions (ban_user, flag user) when you want to take action against the user account based on their behavior pattern
- Content-Type Rules: Use content actions (flag content, block_content) when you want to take action against specific content pieces
- Call-Type Rules: See Call Moderation for call-specific actions and escalation
- Mixed Rules: You can use any action type, but consider whether you want to affect the user or just the content
Condition types
These are the conditions you can put on a rule. Create them in the dashboard rule builder, or send the same JSON on Upsert Rule.
Reaction velocity
user_reaction_count is a user rule. It fires when one user sends too many reactions in a window (app-wide, not per channel). A reaction has no text to score, so message rules never see this.
count picks the unit:
reactions(default): every reaction eventreacted_messages: each message they reacted to counts once. Stacking several reaction types on one post is still 1.
{
"type": "user_reaction_count",
"user_reaction_count_params": {
"threshold": 10,
"time_window": "1m",
"count": "reacted_messages"
}
}| Field | Required | Description |
|---|---|---|
threshold |
yes | Inclusive. Must be greater than 0. |
time_window |
yes | One of 15s, 30s, 1m, 5m, 30m, 1h, 24h, 7d, 30d. |
count |
no | reactions (default) or reacted_messages. |
Use a user action (ban, flag the user, or a webhook). This only runs when a reaction is moderated.
Identical images
user_identical_image_count is a user rule. It counts how many times the same user posts the same image. Identity comes from the image bytes Stream already downloads for moderation, not the CDN URL, so a re-upload still matches.
{
"type": "user_identical_image_count",
"user_identical_image_count_params": {
"threshold": 3,
"time_window": "5m",
"match": "exact"
}
}| Field | Required | Description |
|---|---|---|
threshold |
yes | Inclusive. Must be at least 1. |
time_window |
yes | One of 15s, 30s, 1m, 5m, 30m, 1h. |
match |
no | exact (default): same file bytes. similar: near-duplicate after resize or re-encode. |
similarity_distance |
no | Only with match: "similar". 1 to 20, default 10. Lower is stricter. |
Images that cannot be fetched are skipped (fail-open).
Flood identical and similar
rule_type: "flood" is for repeated copy. Conditions are flood_identical (exact text after normalization, or media when there is no text) and flood_similar (near-duplicate text). Both default to per user. Only content actions apply (flag, remove, shadow_block). There is no cooldown: once the threshold is hit, every further match is actioned.
{
"type": "flood_identical",
"flood_identical_params": {
"threshold": 5,
"time_window": "30s",
"track_across_users": true,
"min_text_length": 20,
"allowlist": ["gg"]
}
}| Field | Required | Description |
|---|---|---|
threshold |
yes | 2 to 100, inclusive. |
time_window |
yes | One of 15s, 30s, 1m, 2m, 3m, 5m, 10m, 15m, 30m, 1h. |
allowlist |
no | Phrases that do not count, after the same normalization. |
track_across_users |
no | Identical only. Default false is per user. true counts that same text from anyone (coordinated copy-paste). It is total occurrences, not distinct users. |
min_text_length |
no | Identical only. Skip shorter text (3 to 500). Omitted or 0 uses 3. Attachment-only messages still count. |
flood_similar uses the same threshold, time_window, and allowlist, plus similarity_distance (1 to 16). It has no across-users option.
This is separate from policy flood_config, which stays per user and has no track_across_users or min_text_length.
Custom properties (Chat and Feeds)
content_custom_property matches a key on the moderated entity. Chat already forwarded message.custom. Feeds now forwards activity and comment custom the same way, so a rule on audience_rating works on a post or a comment.
{
"type": "content_custom_property",
"content_custom_property_params": {
"property_key": "audience_rating",
"operator": "eq",
"expected_value": "kids"
}
}Operators: exists, not_exists, eq, ne, gt, gte, lt, lte, contains, not_contains.
To act on how often a user sends matching custom values, use content_custom_property_count with threshold and time_window.
Time Windows
Specify how long to track user behavior (only applicable to user-type rules):
"15s","30s": short bursts"30m": 30 minutes"1h": 1 hour"24h": 24 hours"7d": 7 days"30d": 30 days
Cooldown Periods
The Rule Builder supports cooldown periods to prevent immediate re-triggering of rules after an action has been taken. This is particularly useful when users are banned and then unbanned by administrators.
When a rule with a cooldown period is triggered and an action is taken (like banning a user), the system records this action with an expiration time. During the cooldown period, the same rule will not trigger again for that user, even if they continue to violate the conditions.
Configuration
Add a cooldown_period field to your rule configuration:
{
"name": "Spam Detection with Cooldown",
"description": "Spam detection rule with 24h cooldown",
"team": "moderation",
"config_keys": [],
"id": "spam-detection",
"rule_type": "user",
"enabled": true,
"cooldown_period": "24h",
"conditions": [
// ... conditions
],
"action": {
"type": "ban_user",
"ban_options": {
"duration": 3600,
"reason": "Spam behavior detected",
"shadow_ban": false,
"ip_ban": false
}
}
}Example Scenario
- User violates rule: User posts 5 spam messages in 1 hour
- Rule triggers: User gets banned for 1 hour
- Admin unbans user: Administrator manually unbans the user
- User posts again: User immediately posts more spam messages
- Cooldown active: Rule does not trigger again due to 24-hour cooldown
- After cooldown: User can trigger the rule again after 24 hours
Use Cases
- Post-Ban Protection: Prevent immediate re-banning after manual unbans
- Graduated Response: Give users time to reflect before facing consequences again
- Administrative Flexibility: Allow admins to override rules without immediate re-triggering
Best Practices
Start Simple
Begin with basic rules and gradually add complexity as you understand your community's needs.
Set Reasonable Thresholds
- Too low: May catch legitimate users
- Too high: May miss problematic behavior
- Start conservative and adjust based on results
Use Appropriate Time Windows
- Short windows (1-6 hours): Catch immediate abuse
- Medium windows (24-48 hours): Catch persistent violators
- Long windows (7-30 days): Catch chronic offenders
Configure Cooldown Periods
- Short cooldowns (1-6 hours): For minor violations where users should get another chance quickly
- Medium cooldowns (24-48 hours): For moderate violations where users need time to reflect
- Long cooldowns (7-30 days): For serious violations where users need significant time before facing consequences again
Test Your Rules
Use the test mode to verify your rules work as expected before enabling them in production.
Monitor Performance
Watch for rules that trigger too frequently or not enough, and adjust accordingly.