---
source_url: https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/custom-events
title: Create custom events
updated_at: 2026-06-18T11:25:26.900Z
---

> 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


# Create custom events

Learn how Chat SDK handles events before creating your own [custom chat events](#create-and-send-events).

## Event handling

### PubNub events

With a standard PubNub SDK, building a chat app requires [additional steps](https://www.pubnub.com/docs/general/basics/receive-messages):

* Subscribe to channels to receive messages
* Add event listeners to handle messages, signals, and events

Chat SDK handles this automatically. All listener methods return a function to stop receiving events and unsubscribe from the channel.

| Entity | Method | Events handled |
| --- | --- | --- |
| `Channel` | [onUpdated() or onDeleted()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/updates#get-channel-updates) | Start/stop getting all `objects` events of type `channel`. |
| `User` | [onUpdated() or onDeleted()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/updates#get-user-updates) | Start/stop getting all `objects` events of type `uuid`. |
| `Message` | [onUpdated()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/updates#get-message-updates) | Start/stop getting all `messageAction` events (for message and message reactions changes) of type `added` or `removed`. |
| `Membership` | [onUpdated() or onDeleted()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/membership#get-updates) | Start/stop getting all `objects` events of type `membership`. |
| `Channel` | [onMessageReceived()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/watch) | Start/stop getting all `message` events of type `text`. |
| `Channel` | [onTypingChanged()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/typing-indicator#get-typing-events) | Start/stop getting all `signal` events of type `typing`. |
| `Channel` | [onPresenceChanged()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/presence#get-presence-updates) | Start/stop getting all `presence` events of type `action` (monitoring when users join, leave, or disconnect from channels). |
| `Channel` | [onMessageReported()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/moderation#listen-to-report-events) | Start/stop getting `report` events for moderation. |
| `Channel` | [onReadReceiptReceived()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/read-receipts#get-read-receipts) | Start/stop getting individual `receipt` events. |
| `Channel` | [onCustomEvent()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/custom-events#receive-current-events) | Start/stop getting custom events. |
| `User` | [onMentioned()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/mentions#show-notifications-for-mentions) | Start/stop getting `mention` events. |
| `User` | [onInvited()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/invite#listen-to-invite-events) | Start/stop getting `invite` events. |
| `User` | [onRestrictionChanged()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#listen-to-moderation-events) | Start/stop getting `moderation` events. |

Chat SDK wraps server responses into entities like `Channel`, `Message`, and `User` with methods and properties for building your app's UI.

### Chat events

Events are separate [entities](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/learn/chat-entities/event) that carry data payloads and can trigger business logic (for example, the Typing Indicator starts or stops based on typing events).

Chat SDK automatically emits these event types when a user:

* Reports a message (`report` event type)
* Starts/Stops typing a message on a channel ([typing](#events-for-typing-indicator) event type)
* Mentions someone else in the message ([mention](#events-for-mentions) event type)
* Reads a message published on a channel ([receipt](#events-for-read-receipts) event type)
* Invites another user to join a channel ([invite](#events-for-channel-initations) event type)
* Mutes a user, bans them, or removes these restrictions ([moderation](#events-for-user-moderation) event type)

All event types use the PubNub Pub/Sub API with one of these methods:

* [publish()](https://www.pubnub.com/docs/sdks/kotlin/api-reference/publish-and-subscribe#publish) - for events requiring history (always [enabled](https://www.pubnub.com/docs/sdks/kotlin/api-reference/publish-and-subscribe#methods) in Chat SDK)
* [signal()](https://www.pubnub.com/docs/sdks/kotlin/api-reference/publish-and-subscribe#signal) - for short-lived events without history (for example, typing indicators)

Listen to events with entity-level methods on `Channel` and `User` objects (for example, `channel.onTypingChanged()`, `user.onMentioned()`, `channel.onMessageReported()`). For historical events, use `getEventsHistory()`.

:::warning Deprecated methods
`listenForEvents()` is deprecated. Use entity-level methods instead:
* Typing → `channel.onTypingChanged()`
* Reports → `channel.onMessageReported()`
* Read receipts → `channel.onReadReceiptReceived()`
* Mentions → `user.onMentioned()`
* Invites → `user.onInvited()`
* Moderation → `user.onRestrictionChanged()`
* Custom events → `channel.onCustomEvent()`
:::

:::note History limitations
`getEventsHistory()` cannot filter by event type. It returns all events emitted via `publish()` on the channel within the specified timeframe.
:::

Each event type has a fixed payload structure documented below.

#### Events for reported messages

* **Type:** `report`
* **PubNub method:** PubNub method used to send events you listen for. `publish()` (with history) is used for all events related to message reporting.
* **Target:** [PUBNUB_INTERNAL_MODERATION_{channel_id}](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/moderation)
* **Trigger:** [report()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/moderation#flagreport-messages) method on the `Message` object
* **Listener:** [onMessageReported()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/moderation#listen-to-report-events) (current) and `getMessageReportsHistory` (historical)
* **Sample use case:** [Message moderation](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/moderation#listen-to-report-events). You might want to create a UI for an operational dashboard to monitor and manage all reported messages.
* **Payload:**

```kotlin
@SerialName("report")
    class Report(
        // content of the flagged message
        val text: String? = null,
        // reason for flagging the message
        val reason: String,
        // timetoken of the flagged message
        val reportedMessageTimetoken: Long? = null,
        // channel where message was flagged
        val reportedMessageChannelId: String? = null,
        // author of the flagged message
        val reportedUserId: String?,
    ) : EventContent()
```

#### Events for typing indicator

* **Type:** `typing`
* **PubNub method:** PubNub method used to send events you listen for. `signal()` (without history) is used for all events related to typing.
* **Target:** The same channel where messages are published.
* **Trigger:** [startTyping() and stopTyping()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/typing-indicator) methods on the `Channel` object
* **Listener:** `onTypingChanged()` on the `Channel` object
* **Sample use case:** [Typing indicator](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/typing-indicator#get-typing-events). You might want to show graphically on the channel that another channel member is typing or has stopped typing a message.
* **Payload:**

```kotlin
@SerialName("typing")
    class Typing(
        // value showing whether someone is typing or not
        val value: Boolean
    ) : EventContent()
```

#### Events for mentions

* **Type:** `mention`
* **PubNub method:** PubNub method used to send events you listen for. `publish()` (with history) is used for all events related to mentions.
* **Target:** Unlike in other event types, a target for mention events is equal to a user ID. This ID is treated as a user-specific channel and is used to send system notifications about changes concerning a [User object](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/learn/chat-entities/user), such as creating, updating, or deleting that user. The channel name is equal to the ID (`id`) of the user and you can retrieve it by calling the `currentUser` method on the `Chat` object.
* **Trigger:** [sendText()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/send-receive) method on the `Channel` object
* **Listener:** `onMentioned()` (current) on the `User` object, or `getEventsHistory()` (historical) on the `Chat` object
* **Sample use case:** [User mentions](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/mentions#show-notifications-for-mentions). You might want to receive notifications for all events emitted when you are mentioned in a parent or thread channel.
* **Payload:**

```kotlin
@SerialName("mention")
    class Mention(
        // timetoken of the message where someone is mentioned
        val messageTimetoken: Long,
        // channel on which the message with mention was sent
        val channel: String,
        // parent channel on which the message with mention was sent
        val parentChannel: String? = null
    ) : EventContent()
```

#### Events for read receipts

* **Type:** `receipt`
* **PubNub method:** PubNub method used to send events you listen for. `signal()` (with history persisted as the last read message on the `Membership` object) is used for all events related to message read receipts.
* **Target:** The same channel where messages are published.
* **Trigger:** [markAllMessagesAsRead()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/unread#mark-messages-as-read-all-channels) method on the `Chat` object, the [setLastReadMessageTimetoken()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/unread#mark-messages-as-read-one-channel) method on the `Membership` object, and the [setLastReadMessage()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/unread#mark-messages-as-read-one-channel) method on the `Membership` object
* **Listener:** `onReadReceiptReceived()` (current) on the `Channel` object, or `fetchReadReceipts()` to get all current read statuses
* **Sample use case:** [Read receipts](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/read-receipts). You might want to indicate on a channel - through avatars or some other indicator - that a message was read by another user/other users.
* **Payload:**

```kotlin
@SerialName("receipt")
    class Receipt(
        // timetoken of the read message
        val messageTimetoken: Long
    ) : EventContent()
```

#### Events for channel initations

* **Type:** `invite`
* **PubNub method:** PubNub method used to send events you listen for. `publish()` (with history) is used for all events related to channel invitations.
* **Target:** An event is sent to the ID of the invited user (user channel with the name same as the user ID).
* **Trigger:** [invite()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/invite#invite-one-user) and [inviteMultiple](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/invite#invite-multiple-users) methods on the `Channel` object
* **Listener:** `onInvited()` (current) on the `User` object, or `getEventsHistory()` (historical) on the `Chat` object
* **Sample use case:** [Channel invitations](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/channels/invite). You might want to notify users that they were invited to join a channel.
* **Payload:**

```kotlin
@SerialName("invite")
    class Invite(
        // type of a channel to which a user was invited (direct or group)
        val channelType: ChannelType,
        // ID of the channel to which a user was invited
        val channelId: String
    ) : EventContent()
```

#### Events for user moderation

* **Type:** `moderation`
* **PubNub method:** PubNub method used to send events you listen for. `publish()` (with history) is used for all events related to user restrictions.
* **Target:** An event is sent to the `PUBNUB_INTERNAL_MODERATION.[user_id]` channel, where `user_id` is the ID of the moderated user.
* **Trigger:** [setRestrictions()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#mute-or-ban-users-as-an-administrator) methods on the `Channel`, `Chat`, and `User` objects
* **Listener:** `onRestrictionChanged()` (current) on the `User` object, or `getEventsHistory()` (historical) on the `Chat` object
* **Sample use case:** [User moderation](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/users/moderation#mute-or-ban-users-as-an-administrator). You might want to notify users when they were muted, banned, or when you remove these restrictions from them.
* **Payload:**

```kotlin
@SerialName("moderation")
    class Moderation(
        // ID of the channel on which the user's moderation restrictions were set or lifted
        val channelId: String,
        // type of restriction: whether a user was muted, banned, or at least one of these restrictions was removed ("lifted")
        val restriction: RestrictionType,
        // reason for muting or banning the user
        val reason: String? = null
    ) : EventContent()
```

## Custom events

The `custom` event type carries custom payloads for additional business logic. Methods:

* `channel.emitCustomEvent()` - create and send custom events on a channel
* `channel.onCustomEvent()` - listen for incoming custom events on a channel
* `getEventsHistory()` - retrieve historical events

:::warning Deprecated methods
`chat.emitEvent()` and `chat.listenForEvents()` are deprecated. Use `channel.emitCustomEvent()` and `channel.onCustomEvent()` instead.
:::

### Create and send events

`emitCustomEvent()` sends custom events on a channel with your custom payload.

#### Method signature

This method takes the following parameters:

```kotlin
channel.emitCustomEvent(
    payload: Map<String, Any?>,
    messageType: String? = null,
    storeInHistory: Boolean = true,
): PNFuture<PNPublishResult>
```

##### Input

| Parameter | Description |
| --- | --- |
| `payload` *Type: `Map<String, Any?>`Default: n/a | Arbitrary key-value payload to publish. |
| `messageType`Type: `String`Default: `null` | Optional custom message type used for filtering. |
| `storeInHistory`Type: `Boolean`Default: `true` | If `true`, the event is stored in Message Persistence (if enabled). |

##### Output

| Type | Description |
| --- | --- |
| `PNFuture<PNPublishResult>` | Result of the PubNub Publish or Signal call. |

#### Sample code

You want to monitor a high-priority channel with a keyword spotter that identifies dissatisfaction words like "annoyed," "frustrated," or "angry." Suppose a message sent by any of the customers present on this channel contains any of these words. In that case, you want to resend it (with relevant metadata) to a separate technical channel (`CUSTOMER-SATISFACTION-CREW`) that's monitored by the team responsible for customer satisfaction.

```kotlin
val channel: Channel
// ...

channel.emitCustomEvent(
    payload = mapOf(
        "chatID" to "chat1234",
        "timestamp" to "2022-04-30T10:30:00Z",
        "customerID" to "customer5678",
        "triggerWord" to "frustrated"
    )
).async { result ->
    result.onSuccess { pnPublishResult ->
        println("Event emitted with timetoken: ${pnPublishResult.timetoken}")
    }.onFailure { ex ->
        println("Failed to emit event: ${ex.message}")
    }
}
```

### Receive current events

`onCustomEvent()` watches a channel for new custom events and handles them via a callback.

#### Method signature

This method takes the following parameters:

```kotlin
channel.onCustomEvent(
    messageType: String? = null,
    callback: (event: CustomEvent<Map<String, Any?>>) -> Unit,
): AutoCloseable
```

##### Input

| Parameter | Description |
| --- | --- |
| `messageType`Type: `String`Default: `null` | Optional custom message type filter. If `null`, all custom events are accepted. |
| `callback` *Type: `(event: CustomEvent<Map<String, Any?>>) -> Unit`Default: n/a | Function invoked for each matching custom event. |

The `CustomEvent` object contains:

| Property | Description |
| --- | --- |
| `timetoken`Type: `Long` | When the event was published. |
| `userId`Type: `String` | ID of the user who emitted the event. |
| `type`Type: `String?` | The custom message type. |
| `payload`Type: `Map<String, Any?>` | The custom event payload data. |

##### Output

| Type | Description |
| --- | --- |
| `AutoCloseable` | Interface that lets you stop receiving custom events by invoking the `close()` method. |

#### Sample code

Monitor a channel for frustrated customer events. When such an event occurs, the `handleFrustratedEvent` function responds with a message acknowledging the customer's frustration and offering assistance.

```kotlin
val channel: Channel
// ...

val subscription = channel.onCustomEvent { event ->
    if (event.payload["triggerWord"] == "frustrated") {
        val customerID = event.payload["customerID"]
        println("Frustrated customer detected: $customerID at ${event.timetoken}")
    }
}

// stop listening:
// subscription.close()
```

### Get historical events

`getEventsHistory()` retrieves historical events from a channel, similar to [getHistory()](https://www.pubnub.com/docs/chat/kotlin-chat-sdk/build/features/messages/history) for messages. Results cannot be filtered by type and include all events emitted via `publish()` in the specified timeframe.

#### Method signature

This method takes the following parameters:

```kotlin
chat.getEventsHistory(
    channelId: String,
    startTimetoken: Long? = null,
    endTimetoken: Long? = null,
    count: Int = 100

): PNFuture<GetEventsHistoryResult>
```

#### Input

| Parameter | Description |
| --- | --- |
| `channelId` *Type: `String`Default: n/a | Channel from which you want to pull historical messages. |
| `startTimetoken`Type: `Long`Default: n/a | Timetoken delimiting the start of a time slice (exclusive) to pull events from. For details, refer to [Message Persistence](https://www.pubnub.com/docs/sdks/kotlin/api-reference/storage-and-playback). |
| `endTimetoken`Type: `Long`Default: n/a | Timetoken delimiting the end of a time slice (inclusive) to pull events from. For details, refer to [Message Persistence](https://www.pubnub.com/docs/sdks/kotlin/api-reference/storage-and-playback). |
| `count`Type: `Int`Default: `100` | Number of historical events to return for the channel in a single call. You can pull a maximum number of 100 events in a single call. |

#### Output

| Parameter | Description |
| --- | --- |
| `PNFuture<GetEventsHistoryResult>`Type: `object` | Returned object containing two fields: `events` and `isMore`. |
| `> events`Type: `Set<Event<EventContent>>` | Array listing the requested number of historical events objects. |
| `> isMore`Type: `Boolean` | Info whether there are more historical events to pull. |

#### Sample code

Fetch the last `10` historical events from the `CUSTOMER-SATISFACTION-CREW` channel.

```kotlin
chat.getEventsHistory(
        channelId = "CUSTOMER-SATISFACTION-CREW",
        count = 10
).async { result -> 
    // ... 
}
```