---
source_url: https://www.pubnub.com/docs/sdks/kotlin/api-reference/storage-and-playback
title: Message Persistence API for Kotlin SDK
updated_at: 2026-06-19T11:37:45.036Z
sdk_name: PubNub Kotlin SDK
sdk_version: 13.4.0
---

> 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


# Message Persistence API for Kotlin SDK

PubNub Kotlin SDK, use the latest version: 13.4.0

Install:

```bash
Add PubNub dependency to your build@13.4.0
```

:::warning Breaking changes in v9.0.0
PubNub Kotlin SDK version 9.0.0 unifies the codebases for Kotlin and [Java](https://www.pubnub.com/docs/sdks/java) SDKs, introduces a new way of instantiating the PubNub client, and changes asynchronous API callbacks and emitted [status events](https://www.pubnub.com/docs/sdks/kotlin/status-events). These changes can impact applications built with previous versions (< `9.0.0` ) of the Kotlin SDK.
For more details about what has changed, refer to [Java/Kotlin SDK migration guide](https://www.pubnub.com/docs/sdks/kotlin/migration-guides/kotlin-v9-migration-guide).
:::

Message Persistence gives you real-time access to the history of messages published to PubNub. Each message is timestamped to the nearest 10 nanoseconds and stored across multiple availability zones in several geographic locations. You can encrypt stored messages with AES-256 so they are not readable on PubNub’s network. For details, see [Message Persistence](https://www.pubnub.com/docs/general/storage).

You control how long messages are stored through your account’s retention policy. Options include: 1 day, 7 days, 30 days, 3 months, 6 months, 1 year, or Unlimited.

You can retrieve the following:

* Messages
* Message reactions
* Files (using the File Sharing API)

:::tip Request execution
Most PubNub Kotlin SDK method invocations return an Endpoint object, which allows you to decide whether to perform the operation synchronously or asynchronously.
You must invoke the `.sync()` or `.async()` method on the Endpoint to execute the request, or the operation **will not** be performed.
```kotlin
val channel = pubnub.channel("channelName")
channel.publish("This SDK rules!").async { result ->
    result.onFailure { exception ->
        // Handle error
    }.onSuccess { value ->
        // Handle successful method result
    }
}
```
:::

## Batch history

:::warning Requires Message Persistence
Enable Message Persistence for your key in the [Admin Portal](https://admin.pubnub.com/). See how to [enable add-on features](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-).
:::

Fetch historical messages from multiple channels. Use `includeMessageActions` or `includeActions` to include message actions.

You can control how messages are returned and in what order.

* If you specify only the `start` parameter (without `end`), you receive messages older than the `start` timetoken.
* If you specify only the `end` parameter (without `start`), you receive messages from that `end` timetoken and newer.
* If you specify both `start` and `end`, you retrieve messages between those timetokens (inclusive of the `end` value).

You can receive up to 100 messages for a single channel. For multiple channels (up to 500), you can receive up to 25 messages per channel. If more messages match the time range, make iterative calls and adjust the `start` timetoken to page through the results.

### Method(s)

Use the following method(s) in the Kotlin SDK:

```kotlin
pubnub.fetchMessages(
    channels: List<String>,
    page: PNBoundedPage,
    includeMeta: Boolean,
    includeMessageAction: Boolean,
    includeMessageType: Boolean,
    includeCustomMessageType: Boolean,
).async { result -> }
```

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| channels | List<String> | Yes |  | Channels to return history messages from. |
| page | PNBoundedPage | Optional |  | The paging object used for pagination. Set `limit` to specify the number of historical messages to return per channel. If `includeMessageActions` is `false`, then `100` is the default (and maximum) value. Otherwise it's `25`. Set `start` to delimit the start of time slice (exclusive) to pull messages from. Set `end` to delimit the end of time slice (inclusive) to pull messages from. |
| includeMeta | Boolean | Optional | `false` | Whether to include the message metadata in the response. |
| includeMessageActions | Boolean | Optional | `false` | Whether to retrieve history messages with message actions. If `true`, limited to one channel only. |
| includeMessageType | Boolean | Optional | `true` | Whether to pass `true` to include message type. Default is `true`. |
| includeCustomMessageType | Boolean | Optional | `false` | Whether to retrieve messages with the custom message type. See [Retrieving Messages](https://www.pubnub.com/docs/general/storage#retrieve-messages). |

### Sample code

:::tip Reference code
This example is a self-contained code snippet ready to be run. It includes necessary imports and executes methods with console logging. Use it as a reference when working with other examples in this document.
:::

```kotlin
import com.pubnub.api.PubNub
import com.pubnub.api.UserId
import com.pubnub.api.models.consumer.PNBoundedPage
import com.pubnub.api.models.consumer.history.PNFetchMessagesResult
import com.pubnub.api.v2.PNConfiguration
import kotlin.math.min

/**
 * This example demonstrates how to fetch message history using the PubNub Kotlin SDK.
 */
fun main() {
    println("PubNub History Example")
    println("======================")

    // Configure PubNub
    val userId = UserId("history-demo-user")
    val config = PNConfiguration.builder(userId, "demo").apply {
        publishKey = "demo"
    }.build()

    // Create PubNub instance
    val pubnub = PubNub.create(config)

    // Channel to fetch history from
    val channelName = "demo-channel"

    // Publish 5 messages
    pubnub.channel(channelName).publish("Message 01").sync()
    pubnub.channel(channelName).publish("Message 02").sync()
    pubnub.channel(channelName).publish("Message 03").sync()
    pubnub.channel(channelName).publish("Message 04").sync()
    pubnub.channel(channelName).publish("Message 05").sync()
    pubnub.channel(channelName).publish("Message 06").sync()

    // 1. Basic history fetch - get the last 10 messages
    fetchBasicHistory(pubnub, channelName)

    // 2. Advanced history fetch with options
    fetchHistoryWithOptions(pubnub, channelName)

    // Clean up resources
    pubnub.deleteMessages(channels = listOf(channelName)).sync()
    pubnub.destroy()
}

/**
 * Basic fetch message history example
 */
fun fetchBasicHistory(pubnub: PubNub, channelName: String) {
    println("\n# Fetching basic message history from channel: $channelName")

    pubnub.fetchMessages(
        channels = listOf(channelName),
        page = PNBoundedPage(limit = 10)
    ).async { result ->
        result.onSuccess { response ->
            println("SUCCESS: Retrieved message history")
            displayMessages(response, channelName)
        }.onFailure { exception ->
            println("ERROR: Failed to fetch history")
            println("Error details: ${exception.message}")
        }
    }

    // Wait for the operation to complete
    Thread.sleep(2000)
}

/**
 * Fetch message history with additional options
 */
fun fetchHistoryWithOptions(pubnub: PubNub, channelName: String) {
    println("\n# Fetching message history with options from channel: $channelName")

    // Get the last 25 messages, including metadata and message actions
    pubnub.fetchMessages(
        channels = listOf(channelName),
        page = PNBoundedPage(limit = 25),
        includeMessageActions = true,
        includeMessageType = true,
        includeMeta = true
    ).async { result ->
        result.onSuccess { response ->
            println("SUCCESS: Retrieved message history with options")
            displayMessages(response, channelName)
        }.onFailure { exception ->
            println("ERROR: Failed to fetch history with options")
            println("Error details: ${exception.message}")
        }
    }

    // Wait for the operation to complete
    Thread.sleep(2000)
}

/**
 * Helper function to display message history results
 */
fun displayMessages(response: PNFetchMessagesResult, channelName: String) {
    val channelMessages = response.channels[channelName]

    if (channelMessages.isNullOrEmpty()) {
        println("No messages found for channel: $channelName")
        return
    }

    val messageCount = channelMessages.size
    println("Found $messageCount messages for channel: $channelName")

    // Display up to 5 most recent messages (or all if less than 5)
    val displayCount = min(5, messageCount)
    println("Showing the $displayCount most recent messages:")

    channelMessages.take(displayCount).forEach { message ->
        println("---------------------------------------")
        println("Timetoken: ${message.timetoken}")
        println("Message: ${message.message}")

        // If available, display metadata
        if (message.meta != null) {
            println("Metadata: ${message.meta}")
        }

        // If available, display message actions
        if (!message.actions.isNullOrEmpty()) {
            println("Message Actions: ${message.actions}")
        }
    }
    println("---------------------------------------")
}
```

### Returns

The `fetchMessages()` operation returns a map of channels and `List<PNFetchMessagesResult>` and `PNBoundedPage` object.

| Method | Description |
| --- | --- |
| `channels`Type: `HashMap<String, List<PNFetchMessageItem>>` | Map of channels and their respective lists of `PNFetchMessageItem`. See [PNFetchMessageItem](#pnfetchmessageitem) for more details. |
| `page`Type: `PNBoundedPage` | If exists indicates that more data is available. It can be passed directly to another call of `fetchMessages` to retrieve this remaining data. |

#### PNFetchMessageItem

| Method | Description |
| --- | --- |
| `message`Type: `JsonElement` | Message |
| `timetoken`Type: `Long` | `Timetoken` of the message. Always returned by default. |
| `meta`Type: `JsonElement?` | Metadata of the message. Is `null` if not requested, otherwise an empty string if requested but no associated metadata. |
| `actions`Type: `Map<String, HashMap<String, List<Action>>>?` | The message actions associated with the message. Is `null` if not requested. The key of the map is the action type. The value is another map, which key is the actual value of the message action, and the key being a list of actions, ie. a list of UUIDs which have posted such a message action. See [Action](#action) for more details. |
| `customMessageType`Type: `String` | The custom message type. |

#### Action

| Method | Description |
| --- | --- |
| `uuid`Type: `String` | The UUID of the publisher. |
| `actionTimetoken`Type: `String` | The publish timetoken of the action. |

### Other examples

#### Paging history responses

```kotlin
import com.pubnub.api.PubNub
import com.pubnub.api.UserId
import com.pubnub.api.models.consumer.PNBoundedPage
import com.pubnub.api.models.consumer.history.PNFetchMessageItem
import com.pubnub.api.models.consumer.history.PNFetchMessagesResult
import com.pubnub.api.v2.PNConfiguration

fun main() {
    val timetokenOfLastMessage = 17474884096044353
    getAllMessages(listOf("kotlin-ch1", "kotlin-ch2"), start = timetokenOfLastMessage + 1) { result ->
        result.channels.forEach { (channel, messages) ->
            println("Channel $channel")
            messages.forEach { item ->
                println(item)
            }
        }
    }
}

private fun getAllMessages(
    channels: List<String>,
    start: Long,
    callback: (result: PNFetchMessagesResult) -> Unit
) {
    val userId = UserId("history-demo-user")
    val config = PNConfiguration.builder(userId, "demo").apply {
        publishKey = "demo"
    }.build()

    // Create PubNub instance
    val pubnub = PubNub.create(config)

    pubnub.fetchMessages(
        channels = channels,
        page = PNBoundedPage(
            limit = 25,
            start = start
        ),
    ).async { result ->
        result.onSuccess { value ->
            if (value.channels.isNotEmpty()) {
                callback.invoke(value)

                var timetoken: Long = start
                value.channels.values.forEach { it: List<PNFetchMessageItem> ->
                    it.forEach { message: PNFetchMessageItem ->
                        if (message.timetoken!! < timetoken) {
                            timetoken = message.timetoken!!
                        }
                    }
                }
                getAllMessages(channels, timetoken, callback)
            }
        }
    }
}
```

## Delete messages from history

:::warning Requires Message Persistence
Enable Message Persistence for your key in the [Admin Portal](https://admin.pubnub.com/). See how to [enable add-on features](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-).
:::

Remove messages from the history of a specific channel.

:::note Required setting
Enable `Delete-From-History` for your key in the Admin Portal and initialize the SDK with a secret key.
:::

### Method(s)

To `deleteMessages()` you can use the following method(s) in the Kotlin SDK.

```kotlin
pubnub.deleteMessages(
    channels: List<String>,
    start: Long,
    end: Long
).async { result -> }
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: `List<String>`Default: n/a | Channels to delete messages from. |
| `start`Type: `Long`Default: n/a | Timetoken delimiting the start (inclusive) of the time slice. |
| `end`Type: `Long`Default: n/a | Timetoken delimiting the end (exclusive) of the time slice. |

### Sample code

```kotlin
pubnub.deleteMessages(
    channels = listOf("channel_1", "channel_2"),
    start = 1460693607379L,
    end = 1460893617271L
).async { result ->
    // The deleteMessages() method does not return actionable data.
    // Be sure to check the status on the outcome of the operation

    if (result.isSuccess) {
        println("Successfully deleted messages.")
    } else {
        println("Error deleting messages: ${result.exceptionOrNull()?.message}")
    }
}
```

### Other examples

#### Delete specific message from history

To delete a specific message, pass the `publish timetoken` (received from a successful publish) in the `End` parameter and timetoken `+/- 1` in the `Start` parameter. For example, if `15526611838554310` is the publish timetoken, pass `15526611838554309` in Start and `15526611838554310` in End parameters respectively as shown in the following code snippet.

```kotlin
pubnub.deleteMessages(
    channels = listOf("channel_1"),
    start = 15526611838554309L,
    end = 15526611838554310L
).async { result ->
    // The deleteMessages() method does not return actionable data.
    // Be sure to check the status on the outcome of the operation

    if (result.isSuccess) {
        println("Successfully deleted messages.")
    } else {
        println("Error deleting messages: ${result.exceptionOrNull()?.message}")
    }
}
```

## Message counts

:::warning Requires Message Persistence
Enable Message Persistence for your key in the [Admin Portal](https://admin.pubnub.com/). See how to [enable add-on features](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-).
:::

Return the number of messages published since the given time. The count is the number of messages with a timetoken greater than or equal to `channelsTimetoken`.

:::note Unlimited message retention
Only messages from the last 30 days are counted.
:::

### Method(s)

Use the following method(s) in the Kotlin SDK:

```kotlin
pubnub.messageCounts(
    channels: List<String>,
    channelsTimetoken: List<Long>
).async { result -> }
```

| Parameter | Description |
| --- | --- |
| `channels` *Type: `List<String>`Default: n/a | Channels to fetch the message count. |
| `channelsTimetoken` *Type: `List<Long>`Default: n/a | Array in the same order as channels; a single timetoken applies to all channels; otherwise, lengths must match exactly. |

### Sample code

```kotlin
val lastHourTimetoken = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)

pubnub.messageCounts(
    channels = listOf("news"),
    channelsTimetoken = listOf(lastHourTimetoken * 10_000L)
).async { result ->
    result.onFailure { exception ->
        // Handle error
    }.onSuccess { value ->
        // Handle successful method result
    }
}
```

### Returns

| Method | Description |
| --- | --- |
| `channels`Type: `Map<String, Long>` | A map with values of Long for each channel. Channels without messages have a count of 0. Channels with 10,000 messages or more have a count of 10000. |

### Other examples

```kotlin
val lastHourTimetoken = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)
val lastDayTimetoken = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)

pubnub.messageCounts(
    channels = listOf("news", "info"),
    channelsTimetoken = listOf(lastHourTimetoken, lastDayTimetoken).map { it * 10_000L }
).async { result ->
    result.onFailure { exception ->
        // Handle error
    }.onSuccess { pmMessageCountResult ->
        pmMessageCountResult.channels.forEach { (channel, count) ->
            println("$count new messages on $channel")
        }
    }
}
```

## History (deprecated)

:::note Requires Message Persistence
This method requires that Message Persistence is enabled for your key in the [Admin Portal](https://admin.pubnub.com/). Read the [support page](https://support.pubnub.com/hc/en-us/articles/360051974791-How-do-I-enable-add-on-features-for-my-keys-) on enabling add-on features on your keys.
:::

:::warning Deprecated
This method is deprecated and will be removed in a future version. Please use the `fetchHistory()` method instead.
:::

### Method(s)

Use the following method(s) in the Kotlin SDK:

```kotlin
pubnub.history(
    channel: String,
    reverse: Boolean,
    includeTimetoken: Boolean,
    includeMeta: Boolean,
    start: Long,
    end: Long,
    count: Int
).async { result ->  }
```

| Parameter | Description |
| --- | --- |
| `channel` *Type: `String`Default: n/a | Channel to return history messages from. |
| `reverse`Type: `Boolean`Default: `false` | Traverse from oldest to newest when set to `true`. |
| `includeTimetoken`Type: `Boolean`Default: `false` | Whether to include message timetokens in the response. |
| `includeMeta`Type: `Boolean`Default: `false` | Include the meta object (if provided at publish time) in the response. |
| `start`Type: `Long`Default: n/a | Timetoken delimiting the start (exclusive) of the time slice. |
| `end`Type: `Long`Default: n/a | Timetoken delimiting the end (inclusive) of the time slice. |
| `count`Type: `Int`Default: `100` | Number of historical messages to return. |

:::tip Using the reverse parameter:
Messages are always returned sorted in ascending time direction from history regardless of `reverse`. The `reverse` direction matters when you have more than 100 (or `count`, if it's set) messages in the time interval, in which case `reverse` determines the end of the time interval from which it should start retrieving the messages.
:::

### Sample code

Retrieve the last 100 messages on a channel:

```kotlin
pubnub.history(
    channel = "history_channel", // where to fetch history from
    count = 100 // how many items to fetch
).async { result -> }
```

### Returns

The `history()` operation returns a `PNHistoryResult` which contains the following operations:

| Method | Description |
| --- | --- |
| `messages`Type: `List<PNHistoryItemResult>` | List of messages of type `PNHistoryItemResult`. See [PNHistoryItemResult](#pnhistoryitemresult) for more details. |
| `startTimetoken`Type: `Long` | Start `timetoken`. |
| `endTimetoken`Type: `Long` | End `timetoken`. |

#### PNHistoryItemResult

| Method | Description |
| --- | --- |
| `timetoken`Type: `Long?` | `Timetoken` of the message. Is `null` if not requested. |
| `entry`Type: `JsonElement` | Message |
| `meta`Type: `JsonElement?` | Metadata of the message. Is `null` if not requested, otherwise an empty string if requested but no associated metadata. |

### Other examples

#### Use history() to retrieve the three oldest messages by retrieving from the time line in reverse

```kotlin
pubnub.history(
    channel = "my_channel", // where to fetch history from
    count = 3, // how many items to fetch
    reverse = true // should go in reverse?
).async { result ->
    result.onSuccess { res ->
        res.messages.forEach { message ->
            message.entry // custom JSON structure for message
        }
    }
}
```

#### Use history() to retrieve messages newer than a given timetoken by paging from oldest message to newest message starting at a single point in time (exclusive)

```kotlin
pubnub.history(
    channel = "my_channel", // where to fetch history from
    start = 13847168620721752L, // first timestamp
    reverse = true // should go in reverse?
).async { result ->
    result.onSuccess { res ->
        res.messages.forEach { message ->
            message.entry // custom JSON structure for message
        }
    }.onFailure { e ->
        // handle error
        e.message
        e.statusCode
        e.pubnubError
    }
}
```

#### Use history() to retrieve messages until a given timetoken by paging from newest message to oldest message until a specific end point in time (inclusive)

```kotlin
pubnub.history(
    channel = "my_channel", // where to fetch history from
    count = 100, // how many items to fetch
    start = -1, // first timestamp
    end = 13847168819178600L, // last timestamp
    reverse = true // should go in reverse?
).async { result ->
    result.onSuccess { res ->
        res.messages // ["Pub3","Pub4","Pub5"]
        res.startTimetoken // 13406746780720711
        res.endTimetoken // 13406746845892666
    }.onFailure { e ->
        // handle error
        e.message
        e.statusCode
        e.pubnubError
    }
}
```

#### History paging example

```kotlin
import com.pubnub.api.PubNub
import com.pubnub.api.UserId
import com.pubnub.api.models.consumer.history.PNHistoryResult

fun main() {
    getAllMessages("my_channel", start = 0L, count = 10) { result ->
        result.messages.forEach {
            println(it.entry)
        }
    }
}

/**
 * Fetches channel history in a recursive manner, in chunks of specified size, starting from the most recent,
 * with every subset (with predefined size) sorted by the timestamp the messages were published.
 *
 * @param channel  The channel where to fetch history from
 * @param start    The timetoken which the fetching starts from
 * @param count    Chunk size
 * @param callback Callback which fires when a chunk is fetched
 */
private fun getAllMessages(
    channel: String,
    start: Long,
    count: Int,
    callback: (result: PNHistoryResult) -> Unit
) {
    val configBuilder = com.pubnub.api.v2.PNConfiguration.builder(UserId("myUserId"), "demo").apply {
        publishKey = "demo"
    }
    val pubnub = PubNub.create(configBuilder.build())

    pubnub.history(
        channel = channel,
        start = start,
        count = count,
        includeTimetoken = true
    ).async { result ->
        result.onSuccess { value ->
            if (value.messages.isNotEmpty()) {
                callback.invoke(value)
                getAllMessages(channel, value.messages.first().timetoken!!, count, callback)
            }
        }
    }
}
```

#### Include timetoken in history response

```kotlin
package com.pubnub.docs.messagePersistence

import com.pubnub.docs.SnippetBase

class HistoryDeprecated : SnippetBase() {
    private fun retrieveLast100MessagesOnChannel() {
        // https://www.pubnub.com/docs/sdks/kotlin/api-reference/storage-and-playback#other-examples-2

        val pubnub = createPubNub()

        // snippet.retrieveLast100MessagesOnChannel
        pubnub.history(
            channel = "history_channel", // where to fetch history from
            count = 100 // how many items to fetch
        ).async { result -> }
        // snippet.end
    }

    private fun historyThreeOldestMessages() {
        // https://www.pubnub.com/docs/sdks/kotlin/api-reference/storage-and-playback#use-history-to-retrieve-the-three-oldest-messages-by-retrieving-from-the-time-line-in-reverse
        val pubnub = createPubNub()

        // snippet.historyThreeOldestMessages
        pubnub.history(
            channel = "my_channel", // where to fetch history from
            count = 3, // how many items to fetch
            reverse = true // should go in reverse?
        ).async { result ->
            result.onSuccess { res ->
                res.messages.forEach { message ->
                    message.entry // custom JSON structure for message
                }
            }
        }
        // snippet.end
    }

    private fun historyRetrieveMessagesNewerThanGivenTimetoken() {
        // https://www.pubnub.com/docs/sdks/kotlin/api-reference/storage-and-playback#use-history-to-retrieve-messages-newer-than-a-given-timetoken-by-paging-from-oldest-message-to-newest-message-starting-at-a-single-point-in-time-exclusive

        val pubnub = createPubNub()

        // snippet.historyRetrieveMessagesNewerThanGivenTimetoken
        pubnub.history(
            channel = "my_channel", // where to fetch history from
            start = 13847168620721752L, // first timestamp
            reverse = true // should go in reverse?
        ).async { result ->
            result.onSuccess { res ->
                res.messages.forEach { message ->
                    message.entry // custom JSON structure for message
                }
            }.onFailure { e ->
                // handle error
                e.message
                e.statusCode
                e.pubnubError
            }
        }
        // snippet.end
    }

    private fun historyRetrieveMessagesUntilGivenTimetoken() {
        // https://www.pubnub.com/docs/sdks/kotlin/api-reference/storage-and-playback#use-history-to-retrieve-messages-until-a-given-timetoken-by-paging-from-newest-message-to-oldest-message-until-a-specific-end-point-in-time-inclusive

        val pubnub = createPubNub()

        // snippet.historyRetrieveMessagesUntilGivenTimetoken
        pubnub.history(
            channel = "my_channel", // where to fetch history from
            count = 100, // how many items to fetch
            start = -1, // first timestamp
            end = 13847168819178600L, // last timestamp
            reverse = true // should go in reverse?
        ).async { result ->
            result.onSuccess { res ->
                res.messages // ["Pub3","Pub4","Pub5"]
                res.startTimetoken // 13406746780720711
                res.endTimetoken // 13406746845892666
            }.onFailure { e ->
                // handle error
                e.message
                e.statusCode
                e.pubnubError
            }
        }
        // snippet.end
    }

    private fun includeTimetoken() {
        // https://www.pubnub.com/docs/sdks/kotlin/api-reference/storage-and-playback#include-timetoken-in-history-response

        val pubnub = createPubNub()

        // snippet.includeTimetoken
        pubnub.history(
            channel = "history_channel", // where to fetch history from
            count = 10, // how many items to fetch
            includeTimetoken = true // include timetoken with each entry
        ).async { result ->
            result.onSuccess { value ->
                value!!.messages.forEach {
                    it.entry // custom JSON structure for message
                    it.timetoken // requested message timetoken
                }
            }
        }
        // snippet.end
    }
}
```

## Terms in this document

* **Message** - A unit of data transmitted between clients or between a client and a server in PubNub, containing information such as text, binary data, or structured data formats like JSON. Messages are sent over channels and can be tracked for delivery and read status.
* **PubNub** - PubNub is a real-time messaging platform that provides APIs and SDKs for building scalable applications. It handles the complex infrastructure of real-time communication, including: Message delivery and persistence, Presence detection, Access control, Push notifications, File sharing, Serverless processing with Functions and Events & Actions, Analytics and monitoring with BizOps Workspace, AI-powered insights with Illuminate.
* **Timetoken** - A unique identifier for each message that represents the number of 100-nanosecond intervals since January 1, 1970, for example, 16200000000000000.