---
source_url: https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/messages/moderation
title: Report offensive messages
updated_at: 2026-06-16T12:48:54.076Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Report offensive messages

Users can report offensive messages directly from your app. Reported messages publish to `PUBNUB_INTERNAL_MODERATION_{channel_id}` and emit [Report events](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/custom-events#chat-events).

Add custom logic using [emitted events](#listen-to-report-events) to handle reported messages (e.g., [delete them](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/messages/delete)).

:::note Message Persistence
Enable Message Persistence in the [Admin Portal](https://admin.pubnub.com/).
:::

## Flag/Report messages

`report()` flags a message for admin review. Reports publish to `PUBNUB_INTERNAL_MODERATION_{channel_id}` (e.g., reporting on `support` sends to `PUBNUB_INTERNAL_MODERATION_support`).

### Method signature

This method takes the following parameters:

```swift
message.report(
    reason: String
) async throws -> Timetoken
```

#### Input

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| reason | String | Yes |  | Reason for reporting/flagging a given message. |

#### Output

| Parameter | Description |
| --- | --- |
| `Timetoken` | The `Timetoken` value of the reported message. |

### Sample code

:::tip Sample code
The code samples in Swift Chat SDK focus on asynchronous code execution.
You can also write synchronous code as the parameters are shared between the async and sync methods but we don't provide usage examples of such.
:::

Report the last message on the `support` channel as offensive.

```swift
// Assumes a "ChatImpl" reference named "chat"
Task {
  if let channel = try await chat.getChannel(channelId: "support") {
    if let message = try await channel.getHistory(count: 1).messages.first {
      let timetoken = try await message.report(reason: "Offensive Content")
      debugPrint("Reported message successfully: \(timetoken)")
    } else {
      debugPrint("No messages found in the \"support\" channel")
    }
  } else {
    debugPrint("Channel not found")
  }
}
```

## Get historical reported messages

`getMessageReportsHistory()` fetches reported message events for a channel with optional time and count filters.

### Method signature

This method takes the following parameters:

```swift
channel.getMessageReportsHistory(
    startTimetoken: Timetoken? = nil,
    endTimetoken: Timetoken? = nil,
    count: Int = 25
) async throws -> (events: [EventWrapper<EventContent>], isMore: Bool)
```

#### Input

| Parameter | Description |
| --- | --- |
| `startTimetoken`Type: `Timetoken`Default: n/a | The start timetoken for fetching the history of reported messages, which allows specifying the point in time where the history retrieval should begin. |
| `endTimetoken`Type: `Timetoken`Default: n/a | The end time token for fetching the history of reported messages, which allows specifying the point in time where the history retrieval should end. |
| `count`Type: `Int`Default: `25` | The number of reported message events to fetch from the history. |

#### Output

| Parameter | Description |
| --- | --- |
| `(events: [EventWrapper<EventContent>], isMore: Bool)` | Returned tuple containing these fields: `events` and `isMore`. |

### Sample code

:::tip Sample code
The code samples in Swift Chat SDK focus on asynchronous code execution.
You can also write synchronous code as the parameters are shared between the async and sync methods but we don't provide usage examples of such.
:::

Fetch historical messages reported on the `support` channel between the `1725100800000` (July 1, 2024, 00:00:00 UTC) and `1726780799000` (July 21, 2024, 23:59:59 UTC) timetokens.

```swift
// Define timetokens for the message history period
let startTimetoken: Timetoken = 1725100800000 // July 1, 2024, 00:00:00 UTC
let endTimetoken: Timetoken = 1726780799000 // July 21, 2024, 23:59:59 UTC

// Assumes a "ChatImpl" reference named "chat"
Task {
  if let channel = try await chat.getChannel(channelId: "support") {
    let historyResponse = try await channel.getMessageReportsHistory(
      startTimetoken: startTimetoken,
      endTimetoken: endTimetoken,
      count: 25
    )
    historyResponse.events.forEach { eventWrapper in
      print("Payload: \(eventWrapper.event.payload)")
    }
  }
}
```

## Listen to Report events

`onMessageReported()` monitors report events for moderation dashboards via a closure. You can also use `channel.stream.reports()` for an `AsyncStream`-based approach.

:::note Deprecation
`streamMessageReports()` is deprecated. Use `onMessageReported()` (closure-based) or `channel.stream.reports()` (AsyncStream-based) instead.
:::

:::tip Events documentation
To read more about the events of type `Report`, refer to the [Chat events](https://www.pubnub.com/docs/chat/swift-chat-sdk/build/features/custom-events#events-for-reported-messages) documentation.
:::

### Method signature

```swift
channel.onMessageReported(
    callback: @escaping (Report) -> Void
) -> AutoCloseable
```

The `Report` struct carries the report payload:

```swift
public struct Report {
    public let reason: String
    public let text: String?
    public let messageTimetoken: Timetoken?
    public let reportedMessageChannelId: String?
    public let reportedUserId: String?
    public let autoModerationId: String?
}
```

##### Input

| Parameter | Description |
| --- | --- |
| `callback` *Type: `(Report) -> Void`Default: n/a | Closure called with a `Report` whenever a message is reported on the channel. |

##### Output

| Parameter | Description |
| --- | --- |
| `AutoCloseable` | An object you must retain. When released or closed, the listener stops. |

### Sample code

:::tip Sample code
The code samples in Swift Chat SDK focus on asynchronous code execution.
You can also write synchronous code as the parameters are shared between the async and sync methods but we don't provide usage examples of such.
:::

Print a notification for an offensive message reported on the `support` channel.

```swift
// Assumes a "ChatImpl" reference named "chat"
Task {
  if let channel = try await chat.getChannel(channelId: "support") {
    for await event in channel.streamMessageReports() {
      // Access the report details from the event's payload
      let reportPayload = event.event.payload
      let reportReason = reportPayload.reason
      // Print the notification
      if reportReason.lowercased() == "offensive" {
        print("Notification: An offensive message was reported on the 'support' channel by user \(event.event.userId). Reason: \(reportReason)")
      }
    }
  } else {
    debugPrint("Channel not found")
  }
}
```