Flooding

Flood detection identifies users sending large volumes of repeated or near-duplicate messages in a short window and appends flood labels and escalated actions to the moderation response. It runs per user, per moderation policy, and covers two independent detectors that can be enabled separately or together:

  • Identical — fires when a user sends the same message repeatedly. Comparison is exact after normalization (lowercase, punctuation and extra whitespace stripped).
  • Similar — fires when a user sends near-duplicate messages that vary by small edits, such as typos, punctuation, or whitespace changes. Similarity is measured with a configurable strictness.

Each detector fires once the user reaches threshold matching messages within time_window.

Configuration

Attach a flood_config block to a moderation policy via the Upsert Config API. Both detectors are opt-in and can be configured with different thresholds and actions.

$client->upsertConfig(new UpsertConfigRequest(
    key: 'chat:messaging',
    floodConfig: new FloodConfig(
        identical: new FloodIdenticalConfig(
            enabled: true,
            threshold: 5,
            timeWindow: '30m',
            action: 'flag',
        ),
        similar: new FloodSimilarConfig(
            enabled: true,
            threshold: 5,
            timeWindow: '30m',
            similarityDistance: 5,
            action: 'remove',
        ),
        allowlist: ['gg', 'well played'],
    ),
));

Fields

Field Type Required Description
identical.enabled boolean false Turn identical-message detection on for this policy.
identical.threshold integer (2 to 100) when enabled Number of matching messages that triggers a flood verdict.
identical.time_window string when enabled Rolling window over which the counter accumulates. One of 15s, 30s, 1m, 2m, 3m, 5m, 10m, 15m, 30m, 1h.
identical.action string when enabled Action applied on trigger. One of flag, remove.
similar.enabled boolean false Turn similar-message detection on for this policy.
similar.threshold integer (2 to 100) when enabled Number of near-matching messages that triggers a flood verdict.
similar.time_window string when enabled Same values as identical.time_window.
similar.similarity_distance integer (1 to 16) when enabled How close two messages must be to count as similar. Lower is stricter (fewer false positives, more misses); higher is looser.
similar.action string when enabled Same values as identical.action.
allowlist string array false Benign phrases exempted from flood detection. Up to 100 entries, up to 200 characters each.

Enabling either detector without both threshold and time_window fails validation on upsert.

Policy flood_config is always per user. To count the same text across users, or to skip short copy, use a flood_identical rule instead (track_across_users, min_text_length).

Allowlist

allowlist exempts benign phrases, such as game shout-outs or common greetings, from flood detection. Matching is exact after normalization (lowercase, punctuation and extra whitespace stripped), so gg, GG!!!, and gg with surrounding whitespace all match an entry gg, but a longer message like gg buy crypto does not.

The exemption applies only to flood detection. A phrase that both appears in the allowlist and matches a blocklist, AI text rule, or other provider is still actioned by those providers. Short entries such as gg are kept. Identical-message counting skips text shorter than 3 characters unless the message also has an image or video.

Detection Labels and Actions

When a detector fires, the moderation response reflects the verdict:

  • One of the following labels is appended to labels:
    • flood_identical — identical-message detection triggered.
    • flood_similar — similar-message detection triggered.
  • recommended_action is the stricter of the flood action and any other provider's action. A remove from another provider stays remove.
  • harm_type is set to spam if it was not already set.

identical.action and similar.action are only flag or remove.

Grouping Flood Webhooks

Once a user crosses the threshold, every later matching message in the window is flagged on its own and creates its own review queue item, so a single flood sends one review_queue_item.new webhook per flagged message.

To handle a flood once, read flood_group_id from the flood flag in the webhook payload. Its value is the entity_id of the first message flagged in that flood, and every later flagged message in the same flood carries the same value:

{
  "type": "review_queue_item.new",
  "review_queue_item": { "entity_id": "msg-4", "entity_creator_id": "u1", ... },
  "flags": [
    {
      "type": "flood",
      "entity_id": "msg-4",
      "labels": ["flood_identical"],
      "result": [
        {
          "labels": ["flood_identical"],
          "action": "flag",
          "custom_result": { "flood_group_id": "msg-3" }
        }
      ]
    }
  ]
}

A message whose flood_group_id equals its own entity_id starts a flood; any other value means it belongs to a flood you have already received.

Groups follow the detector that fired:

  • Identical — one group per user and message content.
  • Similar — one group per user. Two different near-duplicate floods from the same user within one time_window share a group.

A group lasts one time_window from its first flagged message. A flood that continues or restarts after that gets a new flood_group_id.

flood_group_id is set on flags created by policy flood_config through moderation checks. It is not included on moderation_rule.triggered webhooks from flood rules.

Across users

flood_config on a policy counts per user. To count the same text from anyone in the app, add a flood_identical rule and set track_across_users to true. That counter is total occurrences, not distinct users. flood_similar stays per user. See Flood identical and similar.