Image Moderation

Stream's image moderation engine detects harmful or inappropriate images. By default it runs asynchronously: images are processed in the background so they don't add latency, and actions are applied as soon as the analysis is complete. To get the verdict in the same request instead, moderate images synchronously.

Supported Categories

These are the categories shown in the dashboard image policy editor. Set an action and a confidence threshold per category, and narrow a category to some of its subcategories with subclassifications.

The subcategories depend on the image classifier enabled for your app, so your app uses one of the two lists below. The dashboard always shows the list that applies to your app.

Subcategories (list A)

Category Subcategory What it detects
Explicit CSAM Child sexual abuse material, including computer-generated images. Always act on it immediately and report it where the law requires.
Sexual Activity Explicit sexual acts between people.
Graphic Nudity Explicit, non-artistic nudity: fully exposed genitals, buttocks, or breasts in a sexual context.
Adult Toys Items designed for sexual stimulation.
Non-Explicit Nudity of Intimate parts and Kissing Partial Nudity Partially or barely covered breasts or buttocks.
Suggestive Poses & Erotica Erotic content or sexually suggestive poses without explicit nudity.
Revealing Attire Highly revealing clothing, such as lingerie or swimwear, in a sexual pose or context.
Bare Male Chest A bare male torso, or one visible through sheer fabric. Excludes opaque tank tops and babies.
Nudity in Art or Education Nudity in a clearly artistic (painting, sculpture) or educational (anatomy diagram) context.
Swimwear or Underwear Female Swimwear or Underwear Women in swimwear or visible underwear. Excludes sports bras.
Male Swimwear or Underwear Men in swimwear or visible underwear with nothing on top.
Violence Extreme Violence & Gore Graphic violence: severe injuries, mutilation, corpses, or large amounts of blood.
Physical Violence Non-graphic fights or assaults. Excludes regulated combat sports and video games.
Weapons Weapons used or shown in a threatening context. Excludes games, sports, art, and law enforcement.
Visually Disturbing Self Harm Self-harm or suicide, or content that glorifies it.
Visually Disturbing Shocking but non-gory content, such as accidents or emaciated bodies.
Body Waste Human or animal waste.
Drugs & Tobacco Drugs & Controlled Substances Illegal drugs, drug paraphernalia such as syringes or bongs, or drug use.
Tobacco & Vaping Smoking or vaping, or prominently shown tobacco and vaping products.
Alcohol Alcohol Drinking alcohol or prominently shown open containers. Excludes sealed bottles and background presence.
Rude Gestures Offensive Gestures Hand gestures widely seen as offensive or obscene, such as the middle finger.
Gambling Gambling Casino games, slot machines, or sports betting.
Hate Symbols Hate Speech & Symbols Symbols, text, or imagery tied to hate or terrorist groups. Excludes religious or cultural symbols outside a hateful context.
PII Sensitive Personal Information Clearly legible sensitive information, such as ID cards or credit cards.
QR Code QR Code A QR code, which can send users to an external URL.

Subcategories (list B)

Category Subcategories
Explicit Exposed Male Genitalia, Exposed Female Genitalia, Exposed Buttocks or Anus, Exposed Female Nipple, Explicit Sexual Activity, Sex Toys
Non-Explicit Nudity of Intimate parts and Kissing Bare Back, Exposed Male Nipple, Partially Exposed Buttocks, Partially Exposed Female Breast, Implied Nudity, Obstructed Female Nipple, Obstructed Male Genitalia, Kissing on the Lips
Swimwear or Underwear Female Swimwear or Underwear, Male Swimwear or Underwear
Violence Weapons, Weapon Violence, Physical Violence, Self-Harm, Blood & Gore, Explosions and Blasts
Visually Disturbing Emaciated Bodies, Corpses, Air Crash
Drugs & Tobacco Pills, Smoking
Alcohol Drinking, Alcoholic Beverages
Rude Gestures Middle Finger
Hate Symbols Nazi Party, White Supremacy, Extremist
Gambling, PII, QR Code No subcategories

Images have no severity levels: each category is matched by its confidence score. queryModerationRules returns your app's subcategories as ai_image_subclassifications.

To moderate text inside images, such as a phone number in a screenshot, add ocr_rules. Extracted text is classified with the AI Text labels.

Supported Formats

Image moderation accepts the following formats:

  • JPEG (.jpg, .jpeg)
  • PNG (.png)
  • GIF (.gif)*. For animated GIFs, only the first frame is moderated.
  • WebP (.webp)*
  • SVG (.svg)*
  • HEIC/HEIF (.heic, .heif)**

* Converted to JPEG or PNG for moderation only. The stored file is not changed.

** Converted to JPEG when uploaded to Stream's CDN, so the stored file is a JPEG and is moderated as one. HEIC/HEIF images hosted outside Stream's CDN are not moderated.

The following MIME types are accepted:

  • image/jpeg, image/jpg
  • image/png
  • image/gif
  • image/webp
  • image/svg+xml

The format is detected from the file contents, falling back to the Content-Type header. Images in other formats are not moderated. This includes BMP, which can be uploaded to Stream's CDN but is not converted, as well as TIFF and AVIF. To keep unsupported uploads out entirely in Stream Chat, configure the app's image upload allowlist or blocklist from the dashboard. This is a Stream Chat upload setting, not part of the Moderation API; for standalone moderation, filter file types in your own upload flow.

Confidence Scores

For each category, set a confidence threshold between 1 and 100. An action triggers only when the engine's confidence for that category is at or above the threshold.

  • A higher threshold (80-90) only matches highly confident detections. It catches less and produces fewer false positives.
  • A lower threshold (40-50) catches more, including borderline images, and produces more false positives.

For strict enforcement, such as explicit content on a platform for teens, lower the threshold so more is detected. For categories where false positives hurt more than misses, raise it. Start with the default (50) and adjust from what you see in the review queue.

Configuration

Set up image moderation with ai_image_config on a policy. min_confidence is required on every image rule. The scale is 0-100 and the default is 50. A higher value flags less: 90 only matches highly confident detections.

await client.moderation.upsertConfig({
  key: "chat:messaging",
  ai_image_config: {
    rules: [
      { label: "Explicit", action: "remove", min_confidence: 70 },
      { label: "Violence", action: "flag", min_confidence: 50 },
    ],
    ocr_rules: [{ label: "PII", action: "flag" }],
  },
});

Image rule actions are flag, shadow, remove, bounce, bounce_flag, and bounce_remove. subclassifications narrows a label to its L2 children. queryModerationRules returns ai_image_label_definitions and ai_image_subclassifications for the exact strings.

Send images

Send image URLs in moderation_payload.images (up to 30) with the Check API. Stream Chat and Feeds send attached images for you.

const response = await client.moderation.check({
  entity_type: "listing",
  entity_id: "listing-123",
  entity_creator_id: "user-42",
  config_key: "marketplace",
  moderation_payload: {
    images: ["https://cdn.example.com/listings/123/photo-1.jpg"],
  },
});

To send image bytes instead of URLs, use Analyze.

Images on your own CDN

Image moderation works with any CDN, as long as Stream's servers can download the image with a plain GET. Signed URLs work if they are still valid when Stream fetches them. Private S3 objects use a stream+s3:// URL. client.moderation.checkS3Access() checks that the configured IAM role can read them; pass s3_url to test one object.

To moderate a large set of existing images, client.moderation.bulkImageModeration({ csv_file }) takes a CSV of image URLs and returns a task_id. Completion arrives as export.bulk_image_moderation.success.

How It Works

By default, image moderation is asynchronous:

  1. The image is accepted immediately to maintain low latency.
  2. Analysis begins in the background, and the Check response has status: "pending".
  3. The engine evaluates the image against all configured categories.
  4. If a confidence threshold is met, the configured action is applied. Stream Chat and Feeds update the message or post.
  5. Flagged images appear in the review queue, and a review_queue_item.new webhook is sent.

Synchronous moderation

To hold an image until it's checked, turn on Process images synchronously in the dashboard image policy, or set async: false on ai_image_config. Check then returns the image verdict in its response, and Stream Chat applies the action before the message is delivered.

await client.moderation.upsertConfig({
  key: "marketplace",
  ai_image_config: {
    async: false,
    rules: [{ label: "Explicit", action: "remove", min_confidence: 50 }],
  },
});

Synchronous moderation adds the analysis time to the request. An image that was already moderated on upload reuses that verdict.