Call Moderation

Handle moderation events in video call applications. Stream's moderation system automatically emits events when content violates configured policies. Configure policies via the Stream Dashboard or moderation API.

Best Practices

  • Display warnings clearly - Show moderation warnings prominently to users
  • Handle blur gracefully - Apply blur filters without disrupting call flow
  • Inform about termination - Explain why a call ended due to violations
  • Log moderation events - Track violations for review and policy adjustment

Stream API moderation events:

  • call.moderation_warning - Warning issued to user
  • call.moderation_blur - Blur effect should be applied to camera
  • call.ended - Call terminated (with reason: PolicyViolationModeration for violations)

Video Moderation Configuration

The Flutter SDK handles call.moderation_blur events through VideoModerationConfig, set via CallPreferences when creating a call:

Flag / CallbackDescription
muteAudioMutes the local user's microphone and prevents re-enabling it for the configured duration.
muteVideoMutes the local user's camera and prevents re-enabling it for the configured duration.
applyBlurApplies a full-frame native blur on the camera track visible to all participants. Requires the stream_video_filters package.
durationHow long the moderation action lasts. null means it persists until call.clearModerationBlur() is called.
onWarningCallback invoked when a call.moderation_warning event is received.
onApplyCallback invoked when the moderation blur action fires (after built-in actions).
onClearCallback invoked when the moderation blur action is cleared (after built-in clear actions).

Convenience constructors cover the most common presets:

ConstructorEquivalent to
VideoModerationConfig.disabled()Default. No automatic action. The event is still emitted on callEvents.
VideoModerationConfig.mute()muteAudio: true, muteVideo: true
VideoModerationConfig.blur()applyBlur: true

Setting the Configuration

Pass videoModerationConfig in DefaultCallPreferences when creating a call:

final call = streamVideo.makeCall(
  callType: StreamCallType.defaultType(),
  id: 'my-call-id',
  preferences: DefaultCallPreferences(
    videoModerationConfig: VideoModerationConfig.mute(
      duration: Duration(seconds: 5),
    ),
  ),
);

Combining Behaviors

Flags are composable. For example, mute audio and video while also running a custom callback:

final call = streamVideo.makeCall(
  callType: StreamCallType.defaultType(),
  id: 'my-call-id',
  preferences: DefaultCallPreferences(
    videoModerationConfig: VideoModerationConfig(
      muteAudio: true,
      muteVideo: true,
      duration: Duration(seconds: 10),
      onWarning: (message) {
        // Show a warning snackbar
      },
      onApply: () {
        // Show a moderation banner in the UI
      },
      onClear: () {
        // Hide the moderation banner
      },
    ),
  ),
);

Or combine mute with native blur:

VideoModerationConfig(
  muteAudio: true,
  muteVideo: true,
  applyBlur: true,
  duration: Duration(seconds: 10),
)

Moderation Blur

To use applyBlur: true (or VideoModerationConfig.blur()), you need the stream_video_filters package. This applies a full-frame native blur on the outgoing camera track before encoding, so the blur is visible to all participants.

Behavior

  • With stream_video_filters package - Full-screen blur applied to outgoing video, auto-removed after the configured duration
  • Without the package - The blur handlers will not be registered and the blur effect will not be applied

Usage

final call = streamVideo.makeCall(
  callType: StreamCallType.defaultType(),
  id: 'my-call-id',
  preferences: DefaultCallPreferences(
    videoModerationConfig: VideoModerationConfig.blur(
      duration: Duration(seconds: 5),
    ),
  ),
);

Custom Moderation Handling

For full control over what happens when a moderation blur event is received, pass onApply and onClear callbacks directly in the config:

final call = streamVideo.makeCall(
  callType: StreamCallType.defaultType(),
  id: 'my-call-id',
  preferences: DefaultCallPreferences(
    videoModerationConfig: VideoModerationConfig(
      duration: Duration(seconds: 5),
      onWarning: () {
        // Called when a moderation warning is received
      },
      onApply: () {
        // Called when moderation blur fires
      },
      onClear: () {
        // Called when moderation blur is cleared
      },
    ),
  ),
);

The callbacks run after any built-in actions, so you can add them alongside muteAudio, muteVideo, or applyBlur for additional side effects.

Listening to Call Events

All moderation events are also emitted on call.callEvents, regardless of the VideoModerationConfig setting. For moderation warnings you can use either the onWarning callback in the config or listen to StreamCallModerationWarningEvent on callEvents directly.

Moderation Warning

Display a notification when the user receives a moderation warning:

call.callEvents.on<StreamCallModerationWarningEvent>((event) {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('Moderation Warning'),
      content: Text(event.message),
      actions: [
        TextButton(
          onPressed: () => Navigator.of(context).pop(),
          child: Text('OK'),
        ),
      ],
    ),
  );
});

Moderation Blur

Listen to blur events directly when using VideoModerationConfig.disabled() or when you need additional side effects alongside the config:

call.callEvents.on<StreamCallModerationBlurEvent>((event) {
  // Handle the blur event manually
});

Handling Moderation Policy Violation

Multiple policy violations may terminate the call. The StreamCallEndedEvent carries a reason field with the value PolicyViolationModeration when the call was ended due to moderation:

call.callEvents.on<StreamCallEndedEvent>((event) {
  if (event.reason == 'PolicyViolationModeration') {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Call Terminated'),
        content: Text(
          'The video call was terminated due to multiple policy violations.',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: Text('OK'),
          ),
        ],
      ),
    );
  }
});

Checking Moderation State

You can check whether the local user is currently under moderation via the call state:

final isModerated = call.state.value.isVideoModerated;

To programmatically clear the moderation action (for example, from an admin UI):

call.clearModerationBlur();