Skip to content

Rule-Triggered Webhooks

Use the Rule Builder to receive a webhook only when a chosen condition matches — for example a specific harm label. Stream sends moderation_rule.triggered for that rule. Other messages do not produce this event.

This is the scoped alternative to subscribing to every review-queue item. Pair it with webhook_only when you want a notification, or with a user/content action when you also want Stream to enforce something.

How it works

  1. Create a rule whose conditions describe the harms (or other signals) you care about.
  2. Set the rule action to webhook_only, or to a user/content action such as flag_user.
  3. Subscribe an event hook to moderation_rule.triggered with product moderation.

When a check evaluates that rule and the conditions pass, Stream sends one moderation_rule.triggered event. Rule actions run in addition to the action already configured on the policy for that label.

Content rule: notify on a harm label

A content rule with text_content evaluates the current message, activity, or comment. It fires as soon as the listed labels are present.

{
  "name": "High-profile harms",
  "description": "Notify when selected harm labels are detected",
  "rule_type": "content",
  "enabled": true,
  "config_keys": ["chat:messaging", "chat:livestream"],
  "conditions": [
    {
      "type": "text_content",
      "text_content_params": {
        "harm_labels": ["UNDERAGE_USER", "SCAM"]
      }
    }
  ],
  "action": {
    "type": "webhook_only"
  }
}
Field Description
rule_type "content" evaluates the current piece of content.
config_keys Policies this rule applies to. An empty list applies the rule app-wide.
text_content_params.harm_labels Labels that satisfy the condition. Any listed label is enough.
text_content_params.severity Optional minimum severity (LOW, MEDIUM, HIGH, CRITICAL).
action.type "webhook_only" sends the event and does not apply a further enforcement action.

Create or update the rule with POST /api/v2/moderation/moderation_rule. Enable the Rule Builder on the same config:

{
  "key": "chat:messaging",
  "rule_builder_config": {
    "enabled": true
  }
}
Node.js
await client.moderation.upsertConfig({
  key: "chat:messaging",
  rule_builder_config: { enabled: true },
});

await client.moderation.upsertModerationRule({
  name: "High-profile harms",
  description: "Notify when selected harm labels are detected",
  config_keys: ["chat:messaging", "chat:livestream"],
  enabled: true,
  rule_type: "content",
  conditions: [
    {
      type: "text_content",
      text_content_params: {
        harm_labels: ["UNDERAGE_USER", "SCAM"],
      },
    },
  ],
  action: { type: "webhook_only" },
});

Other conditions on a content rule work the same way: blocklist_match, contains_url, image harm_labels, and so on. See Rules.

User rule: act on the account

A user rule with text_rule counts matching labels for one user over a time window. Use this when the signal should follow the account rather than a single piece of content.

{
  "name": "Repeat high-severity insult",
  "rule_type": "user",
  "enabled": true,
  "config_keys": ["chat:messaging"],
  "conditions": [
    {
      "type": "text_rule",
      "text_rule_params": {
        "threshold": 1,
        "time_window": "1h",
        "harm_labels": ["INSULT"],
        "severity": "HIGH"
      }
    }
  ],
  "action": {
    "type": "flag_user"
  }
}

threshold: 1 fires on the first matching message. Increase it to require a repeated pattern. The same moderation_rule.triggered event is sent when the rule fires.

Subscribe to the event

Configure the hook in the dashboard (Moderation → Preferences → Webhook & SNS/SQS) or via event_hooks on the app:

{
  "hook_type": "webhook",
  "enabled": true,
  "product": "moderation",
  "event_types": ["moderation_rule.triggered"],
  "webhook_url": "https://example.com/moderation/rules"
}

product must be moderation (or all). If event_types omits moderation_rule.triggered, the event is dropped.

Event payload

{
  "type": "moderation_rule.triggered",
  "created_at": "2026-09-14T11:00:54.928472211Z",
  "rule": {
    "id": "5423c4d3-d4f0-4264-85a9-e176c9915477",
    "name": "High-profile harms",
    "type": "content",
    "description": "Notify when selected harm labels are detected"
  },
  "entity_id": "85f3c2a1-9c4e-4b1d-8a77-2f0e91c4d123",
  "entity_type": "stream:chat:v1:message",
  "user_id": "user-42",
  "triggered_actions": ["webhook_only"]
}
Field Description
type Always moderation_rule.triggered.
created_at When the rule fired.
rule.id Rule ID.
rule.name Rule name.
rule.type content, user, or call.
rule.description Rule description.
entity_id ID of the entity that triggered the rule. For chat this is the message ID. For Feeds v3 this is the activity or comment ID. For calls this is the call CID.
entity_type Entity type. Common values: stream:chat:v1:message, stream:feeds:v3:activity, stream:feeds:v3:comment, stream:v1:call.
user_id ID of the user who created the entity.
triggered_actions Actions the rule executed, for example ["webhook_only"] or ["flag_user"].
review_queue_item_id Review-queue item ID when one is associated with the event.
violation_number Present on call rules with action sequences.
matched_contents Present on aggregation rules (keyframes, closed captions, keyframe OCR) when the request supplied content_ids. Each entry includes type, id, published_at, and the classifications or severity for that item.

One event is sent per rule per entity (and per violation_number on call rules).

Event When it fires
moderation_rule.triggered A Rule Builder rule matched. Use this for label-scoped or condition-scoped notifications.
review_queue_item.new A review-queue item is created. The payload includes the item, flags, and the enriched entity (message, activity, comment, or call).
review_queue_item.updated An existing item receives more flags or a moderator action.
moderation_check.completed A moderation check finished, including when the recommended action is keep.

See Webhooks for the full payload of each event.