# Message history (Beta) (/en/realtime-media/rtm/build/send-and-receive-messages/message-history)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

<_PlatformTabsGroup groupMode="structured" canonicalPlatform="web" platforms="[&#x22;web&#x22;,&#x22;android&#x22;,&#x22;ios&#x22;,&#x22;macos&#x22;,&#x22;windows&#x22;,&#x22;linux-cpp&#x22;]" showTabs="true">
  <_PlatformPanel platform="web">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />

    The Signaling message history feature allows you to store messages when publishing them to a channel. For example, if a user joins a channel midway through a conversation, you can retrieve messages that were published before the user joined. You can configure the message retention period for each project, ranging from 1 day to permanent storage.

    When a message is published, it is stored using the channel name and the message timestamp. You can use this information to retrieve historical messages.

    <CalloutContainer type="info">
      <CalloutDescription>
        The historical message feature is currently available for User Channels and Message Channels, but not for Stream Channels.
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites]

    Before implementing this feature, ensure that you have:

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../index) page.
    * Enabled the message history feature by contacting [support@agora.io](mailto\:support@agora.io). You can request a storage duration of 1, 7, 30, 90, or 365 days, or opt for permanent storage.

      <CalloutContainer type="info">
        <CalloutDescription>
          Changes to storage duration only affect future messages.
        </CalloutDescription>
      </CalloutContainer>

    ## Implement message history [#implement-message-history]

    This section shows how to store and retrieve messages from history.

    ### Store messages [#store-messages]

    To store a message on the server, set `setStoreInHistory` to `true` in the `publish()` method. The following example shows the minimum code to store a message published to a message channel:

    ```javascript
    try {
      const result = await rtm.publish(
        "chatroom",
        "Hello world!",
        {
          channelType: "MESSAGE",
          customType: "STRING",
          storeInHistory: true, // Overrides the default storage option
        }
      );
    } catch (status) {
      console.log(status);
    }
    ```

    To store a message in the history of a user channel, use the same parameter:

    ```javascript
    try {
      const result = await rtm.publish(
        "Tony", // User ID
        "Hello world!",
        {
          channelType: "USER",
          customType: "STRING",
          storeInHistory: true, // Overrides the default storage option
        }
      );
    } catch (status) {
      console.log(status);
    }
    ```

    ### Retrieve messages [#retrieve-messages]

    The SDK provides the `getMessages()` method to retrieve up to 100 history messages at a time. You can filter messages by setting the `start` and `end` parameters to define a time range. To retrieve only the most recent messages, such as those sent between the last offline time and now, set only the `end` parameter.

    The following example retrieves the latest 50 messages sent before a specific time:

    ```javascript
    try {
      const result = await rtmClient.history.getMessages(
        "chat_room",
        "MESSAGE",
        {
          messageCount: 50,
          end: 1688978391800,
        }
      );
    } catch (status) {
      const { operation, reason, errorCode } = status;
      console.log(`${operation} failed. Error code: ${errorCode}, reason: ${reason}.`);
    }
    ```

    The `result` includes a `newStart` field, which indicates whether there are unread messages in the history:

    * If `newStart` is `0`, all messages have been retrieved.
    * If `newStart` is not `0`, use this value as the new `start` parameter in the next `getMessages()` call to continue retrieving messages.

    The following use-cases show how to specify the `start` and `end` parameters to retrieve messages in a specific range:

    #### Retrieve before a start timestamp [#retrieve-before-a-start-timestamp]

    | Parameter | Behavior                                                                                |
    | --------- | --------------------------------------------------------------------------------------- |
    | `start`   | Returns messages sent before the specified timestamp. The timestamp itself is excluded. |

    ```text
    Timeline:
    [Older messages] ------------ start timestamp ------------ [Newer messages]
    [             ←-- n messages]
    ```

    #### Retrieve messages up to an end timestamp [#retrieve-messages-up-to-an-end-timestamp]

    | Parameter | Behavior                                                                             |
    | --------- | ------------------------------------------------------------------------------------ |
    | `end`     | Returns the n most recent messages sent up to and including the specified timestamp. |

    ```text
    Timeline:
    [Older messages] ------------ end timestamp ------------ [Newer messages]
                                           [  ←-- n messages]
    ```

    #### Retrieve messages between two timestamps [#retrieve-messages-between-two-timestamps]

    | Parameter | Behavior                                                  |
    | --------- | --------------------------------------------------------- |
    | `start`   | Returns messages sent before this timestamp (excluded).   |
    | `end`     | Returns messages sent up to and including this timestamp. |

    ```text
    Timeline:
    [Older messages] ---- end timestamp ------------ start timestamp ---- [Newer messages]
                                   [ ←-- n messages]
    ```

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="android">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />

    The Signaling message history feature allows you to store messages when publishing them to a channel. For example, if a user joins a channel midway through a conversation, you can retrieve messages that were published before the user joined. You can configure the message retention period for each project, ranging from 1 day to permanent storage.

    When a message is published, it is stored using the channel name and the message timestamp. You can use this information to retrieve historical messages.

    <CalloutContainer type="info">
      <CalloutDescription>
        The historical message feature is currently available for User Channels and Message Channels, but not for Stream Channels.
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-1]

    Before implementing this feature, ensure that you have:

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../index) page.
    * Enabled the message history feature by contacting [support@agora.io](mailto\:support@agora.io). You can request a storage duration of 1, 7, 30, 90, or 365 days, or opt for permanent storage.

      <CalloutContainer type="info">
        <CalloutDescription>
          Changes to storage duration only affect future messages.
        </CalloutDescription>
      </CalloutContainer>

    ## Implement message history [#implement-message-history-1]

    This section shows how to store and retrieve messages from history.

    ### Store messages [#store-messages-1]

    To store a message on the server, set `setStoreInHistory` to `true` in the `publish()` method. The following example shows the minimum code to store a message published to a message channel:

    ```java
    PublishOptions options = new PublishOptions();
    options.setCustomType("custom type");
    options.setStoreInHistory(true);

    String channelName = "chatRoom";
    String message = "Hello world!";
    rtm.publish(channelName, message, options, new ResultCallback<Void>() {
        @Override
        public void onSuccess(Void responseInfo) {
            log(CALLBACK, "send message success");
        }

        @Override
        public void onFailure(ErrorInfo errorInfo) {
            log(ERROR, errorInfo.toString());
        }
    });
    ```

    To store a message in the history of a user channel, use the same parameter:

    ```java
    PublishOptions options = new PublishOptions();
    options.setChannelType(RtmChannelType.USER);
    options.setCustomType("custom type");
    options.setStoreInHistory(true);

    String userId = "Tony";
    String message = "Hello world!";
    rtm.publish(userId, message, options, new ResultCallback<Void>() {
        @Override
        public void onSuccess(Void responseInfo) {
            log(CALLBACK, "send message success");
        }

        @Override
        public void onFailure(ErrorInfo errorInfo) {
            log(ERROR, errorInfo.toString());
        }
    });
    ```

    ### Retrieve messages [#retrieve-messages-1]

    The SDK provides the `getMessages()` method to retrieve up to 100 history messages at a time. You can filter messages by setting the `start` and `end` parameters to define a time range. To retrieve only the most recent messages, such as those sent between the last offline time and now, set only the `end` parameter.

    The following example retrieves the latest 50 messages sent before a specific time:

    ```java
    GetHistoryMessagesOptions options = new GetHistoryMessagesOptions();
    options.setMessageCount(50);
    options.setEnd(1688978391800);

    String channelName = "chatRoom";
    rtm.getHistory().getMessages(channelName, RtmChannelType.MESSAGE, options, new ResultCallback<GetMessagesResult>() {
        @Override
        public void onSuccess(GetMessagesResult result) {
            log(CALLBACK, "get history messages! new start: " + result.getNewStart());
            for (HistoryMessage message : result.getMessageList()) {
                log(INFO, message.toString());
            }
        }

        @Override
        public void onFailure(ErrorInfo errorInfo) {
            log(ERROR, errorInfo.toString());
        }
    });
    ```

    The `result` includes a `newStart` field, which indicates whether there are unread messages in the history:

    * If `newStart` is `0`, all messages have been retrieved.
    * If `newStart` is not `0`, use this value as the new `start` parameter in the next `getMessages()` call to continue retrieving messages.

    The following use-cases show how to specify the `start` and `end` parameters to retrieve messages in a specific range:

    #### Retrieve before a start timestamp [#retrieve-before-a-start-timestamp-1]

    | Parameter | Behavior                                                                                |
    | --------- | --------------------------------------------------------------------------------------- |
    | `start`   | Returns messages sent before the specified timestamp. The timestamp itself is excluded. |

    ```text
    Timeline:
    [Older messages] ------------ start timestamp ------------ [Newer messages]
    [             ←-- n messages]
    ```

    #### Retrieve messages up to an end timestamp [#retrieve-messages-up-to-an-end-timestamp-1]

    | Parameter | Behavior                                                                             |
    | --------- | ------------------------------------------------------------------------------------ |
    | `end`     | Returns the n most recent messages sent up to and including the specified timestamp. |

    ```text
    Timeline:
    [Older messages] ------------ end timestamp ------------ [Newer messages]
                                           [  ←-- n messages]
    ```

    #### Retrieve messages between two timestamps [#retrieve-messages-between-two-timestamps-1]

    | Parameter | Behavior                                                  |
    | --------- | --------------------------------------------------------- |
    | `start`   | Returns messages sent before this timestamp (excluded).   |
    | `end`     | Returns messages sent up to and including this timestamp. |

    ```text
    Timeline:
    [Older messages] ---- end timestamp ------------ start timestamp ---- [Newer messages]
                                   [ ←-- n messages]
    ```

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="ios">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />

    The Signaling message history feature allows you to store messages when publishing them to a channel. For example, if a user joins a channel midway through a conversation, you can retrieve messages that were published before the user joined. You can configure the message retention period for each project, ranging from 1 day to permanent storage.

    When a message is published, it is stored using the channel name and the message timestamp. You can use this information to retrieve historical messages.

    <CalloutContainer type="info">
      <CalloutDescription>
        The historical message feature is currently available for User Channels and Message Channels, but not for Stream Channels.
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-2]

    Before implementing this feature, ensure that you have:

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../index) page.
    * Enabled the message history feature by contacting [support@agora.io](mailto\:support@agora.io). You can request a storage duration of 1, 7, 30, 90, or 365 days, or opt for permanent storage.

      <CalloutContainer type="info">
        <CalloutDescription>
          Changes to storage duration only affect future messages.
        </CalloutDescription>
      </CalloutContainer>

    ## Implement message history [#implement-message-history-2]

    This section shows how to store and retrieve messages from history.

    ### Store messages [#store-messages-2]

    To store a message on the server, set `setStoreInHistory` to `true` in the `publish()` method. The following example shows the minimum code to store a message published to a message channel:

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objc">
          Objc
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        let message = "Hello Agora!"
        let channel = "your_channel"

        let publishOption = AgoraRtmPublishOptions()
        publishOption.storeInHistory = true;

        rtmKit.publish(channelName: channel, message: message, option: publishOption, completion: { res, error in
            if error != nil {
                print("\(error?.operation) failed! error reason is \(error?.reason)")
            } else {
                print("success")
            }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objc">
        ```objc
        NSString* message = @"Hello Agora!";
        NSString* channel = @"your_channel";

        AgoraRtmPublishOptions* publish_option = [[AgoraRtmPublishOptions alloc] init];
        publish_option.storeInHistory = true;

        [_kit publish:channel message:message option:publish_option completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"publish success!!");
            } else {
                NSLog(@"publish failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    To store a message in the history of a user channel, use the same parameter:

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objc">
          Objc
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        let message = "Hello Agora!"
        let user = "Tony"

        let publishOption = AgoraRtmPublishOptions()
        publishOption.channelType = .user
        publishOption.storeInHistory = true;

        rtmKit.publish(channelName: user, message: message, option: publishOption, completion: { res, error in
            if error != nil {
                print("\(error?.operation) failed! error reason is \(error?.reason)")
            } else {
                print("success")
            }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objc">
        ```objc
        NSString* message = @"Hello Agora!";
        NSString* user = @"Tony";

        AgoraRtmPublishOptions* publish_option = [[AgoraRtmPublishOptions alloc] init];
        publish_option.channelType = AgoraRtmChannelTypeUser;
        publish_option.storeInHistory = true;

        [_kit publish:user message:message option:publish_option completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"publish success!!");
            } else {
                NSLog(@"publish failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ### Retrieve messages [#retrieve-messages-2]

    The SDK provides the `getMessages()` method to retrieve up to 100 history messages at a time. You can filter messages by setting the `start` and `end` parameters to define a time range. To retrieve only the most recent messages, such as those sent between the last offline time and now, set only the `end` parameter.

    The following example retrieves the latest 50 messages sent before a specific time:

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objc">
          Objc
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        var option = AgoraRtmGetHistoryMessagesOptions()
         option.messageCount = 50
         option.end = 1688978391800

        rtmKit.getHistory()?.getMessages(channelName: "channel_name", channelType: .message, options: option, completion: { response, error in
            if response != nil {
                print("total message count is: \(response!.messageList.count)")
                    for msg in response!.messageList {
                        print("publisher: \(msg.publisher)message content: \(msg.message.stringData!)time stamp: \(msg.timestamp)")
                    }
                    print("new start: \(response!.newStart)")
            } else {
                print("get history message failed")
            }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objc">
        ```objc
        AgoraRtmGetHistoryMessagesOptions *option = [[AgoraRtmGetHistoryMessagesOptions alloc] init];
        option.messageCount = 50;
        option.end = 1688978391800;

        [_kit getMessages:@"channel_name"
                    channelType:AgoraRtmChannelTypeMessage
                        options:option
                     completion:^(AgoraRtmGetHistoryMessagesResponse *response, NSError *error) {
            if (response != nil) {
                NSLog(@"total message count is: %lu", (unsigned long)response.messageList.count);

                for (AgoraRtmMessage *msg in response.messageList) {
                    NSLog(@"publisher: %@message content: %@time stamp: %lld", msg.publisher, msg.message.stringData, msg.timestamp);
                }

                NSLog(@"new start: %@", response.newStart);
            } else {
                NSLog(@"get history message failed: %@", error.localizedDescription);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    The `response` includes a `newStart` field, which indicates whether there are unread messages in the history:

    * If `newStart` is `0`, all messages have been retrieved.
    * If `newStart` is not `0`, use this value as the new `start` parameter in the next `getMessages()` call to continue retrieving messages.

    The following use-cases show how to specify the `start` and `end` parameters to retrieve messages in a specific range:

    #### Retrieve before a start timestamp [#retrieve-before-a-start-timestamp-2]

    | Parameter | Behavior                                                                                |
    | --------- | --------------------------------------------------------------------------------------- |
    | `start`   | Returns messages sent before the specified timestamp. The timestamp itself is excluded. |

    ```text
    Timeline:
    [Older messages] ------------ start timestamp ------------ [Newer messages]
    [             ←-- n messages]
    ```

    #### Retrieve messages up to an end timestamp [#retrieve-messages-up-to-an-end-timestamp-2]

    | Parameter | Behavior                                                                             |
    | --------- | ------------------------------------------------------------------------------------ |
    | `end`     | Returns the n most recent messages sent up to and including the specified timestamp. |

    ```text
    Timeline:
    [Older messages] ------------ end timestamp ------------ [Newer messages]
                                           [  ←-- n messages]
    ```

    #### Retrieve messages between two timestamps [#retrieve-messages-between-two-timestamps-2]

    | Parameter | Behavior                                                  |
    | --------- | --------------------------------------------------------- |
    | `start`   | Returns messages sent before this timestamp (excluded).   |
    | `end`     | Returns messages sent up to and including this timestamp. |

    ```text
    Timeline:
    [Older messages] ---- end timestamp ------------ start timestamp ---- [Newer messages]
                                   [ ←-- n messages]
    ```

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="macos">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="macos" />

    The Signaling message history feature allows you to store messages when publishing them to a channel. For example, if a user joins a channel midway through a conversation, you can retrieve messages that were published before the user joined. You can configure the message retention period for each project, ranging from 1 day to permanent storage.

    When a message is published, it is stored using the channel name and the message timestamp. You can use this information to retrieve historical messages.

    <CalloutContainer type="info">
      <CalloutDescription>
        The historical message feature is currently available for User Channels and Message Channels, but not for Stream Channels.
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-3]

    Before implementing this feature, ensure that you have:

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../index) page.
    * Enabled the message history feature by contacting [support@agora.io](mailto\:support@agora.io). You can request a storage duration of 1, 7, 30, 90, or 365 days, or opt for permanent storage.

      <CalloutContainer type="info">
        <CalloutDescription>
          Changes to storage duration only affect future messages.
        </CalloutDescription>
      </CalloutContainer>

    ## Implement message history [#implement-message-history-3]

    This section shows how to store and retrieve messages from history.

    ### Store messages [#store-messages-3]

    To store a message on the server, set `setStoreInHistory` to `true` in the `publish()` method. The following example shows the minimum code to store a message published to a message channel:

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objc">
          Objc
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        let message = "Hello Agora!"
        let channel = "your_channel"

        let publishOption = AgoraRtmPublishOptions()
        publishOption.storeInHistory = true;

        rtmKit.publish(channelName: channel, message: message, option: publishOption, completion: { res, error in
            if error != nil {
                print("\(error?.operation) failed! error reason is \(error?.reason)")
            } else {
                print("success")
            }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objc">
        ```objc
        NSString* message = @"Hello Agora!";
        NSString* channel = @"your_channel";

        AgoraRtmPublishOptions* publish_option = [[AgoraRtmPublishOptions alloc] init];
        publish_option.storeInHistory = true;

        [_kit publish:channel message:message option:publish_option completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"publish success!!");
            } else {
                NSLog(@"publish failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    To store a message in the history of a user channel, use the same parameter:

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objc">
          Objc
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        let message = "Hello Agora!"
        let user = "Tony"

        let publishOption = AgoraRtmPublishOptions()
        publishOption.channelType = .user
        publishOption.storeInHistory = true;

        rtmKit.publish(channelName: user, message: message, option: publishOption, completion: { res, error in
            if error != nil {
                print("\(error?.operation) failed! error reason is \(error?.reason)")
            } else {
                print("success")
            }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objc">
        ```objc
        NSString* message = @"Hello Agora!";
        NSString* user = @"Tony";

        AgoraRtmPublishOptions* publish_option = [[AgoraRtmPublishOptions alloc] init];
        publish_option.channelType = AgoraRtmChannelTypeUser;
        publish_option.storeInHistory = true;

        [_kit publish:user message:message option:publish_option completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
            if (errorInfo == nil) {
                NSLog(@"publish success!!");
            } else {
                NSLog(@"publish failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ### Retrieve messages [#retrieve-messages-3]

    The SDK provides the `getMessages()` method to retrieve up to 100 history messages at a time. You can filter messages by setting the `start` and `end` parameters to define a time range. To retrieve only the most recent messages, such as those sent between the last offline time and now, set only the `end` parameter.

    The following example retrieves the latest 50 messages sent before a specific time:

    <CodeBlockTabs defaultValue="Swift">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Swift">
          Swift
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Objc">
          Objc
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Swift">
        ```swift
        var option = AgoraRtmGetHistoryMessagesOptions()
         option.messageCount = 50
         option.end = 1688978391800

        rtmKit.getHistory()?.getMessages(channelName: "channel_name", channelType: .message, options: option, completion: { response, error in
            if response != nil {
                print("total message count is: \(response!.messageList.count)")
                    for msg in response!.messageList {
                        print("publisher: \(msg.publisher)message content: \(msg.message.stringData!)time stamp: \(msg.timestamp)")
                    }
                    print("new start: \(response!.newStart)")
            } else {
                print("get history message failed")
            }
        })
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Objc">
        ```objc
        AgoraRtmGetHistoryMessagesOptions *option = [[AgoraRtmGetHistoryMessagesOptions alloc] init];
        option.messageCount = 50;
        option.end = 1688978391800;

        [_kit getMessages:@"channel_name"
                    channelType:AgoraRtmChannelTypeMessage
                        options:option
                     completion:^(AgoraRtmGetHistoryMessagesResponse *response, NSError *error) {
            if (response != nil) {
                NSLog(@"total message count is: %lu", (unsigned long)response.messageList.count);

                for (AgoraRtmMessage *msg in response.messageList) {
                    NSLog(@"publisher: %@message content: %@time stamp: %lld", msg.publisher, msg.message.stringData, msg.timestamp);
                }

                NSLog(@"new start: %@", response.newStart);
            } else {
                NSLog(@"get history message failed: %@", error.localizedDescription);
            }
        }];
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    The `response` includes a `newStart` field, which indicates whether there are unread messages in the history:

    * If `newStart` is `0`, all messages have been retrieved.
    * If `newStart` is not `0`, use this value as the new `start` parameter in the next `getMessages()` call to continue retrieving messages.

    The following use-cases show how to specify the `start` and `end` parameters to retrieve messages in a specific range:

    #### Retrieve before a start timestamp [#retrieve-before-a-start-timestamp-3]

    | Parameter | Behavior                                                                                |
    | --------- | --------------------------------------------------------------------------------------- |
    | `start`   | Returns messages sent before the specified timestamp. The timestamp itself is excluded. |

    ```text
    Timeline:
    [Older messages] ------------ start timestamp ------------ [Newer messages]
    [             ←-- n messages]
    ```

    #### Retrieve messages up to an end timestamp [#retrieve-messages-up-to-an-end-timestamp-3]

    | Parameter | Behavior                                                                             |
    | --------- | ------------------------------------------------------------------------------------ |
    | `end`     | Returns the n most recent messages sent up to and including the specified timestamp. |

    ```text
    Timeline:
    [Older messages] ------------ end timestamp ------------ [Newer messages]
                                           [  ←-- n messages]
    ```

    #### Retrieve messages between two timestamps [#retrieve-messages-between-two-timestamps-3]

    | Parameter | Behavior                                                  |
    | --------- | --------------------------------------------------------- |
    | `start`   | Returns messages sent before this timestamp (excluded).   |
    | `end`     | Returns messages sent up to and including this timestamp. |

    ```text
    Timeline:
    [Older messages] ---- end timestamp ------------ start timestamp ---- [Newer messages]
                                   [ ←-- n messages]
    ```

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="windows">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="windows" />

    The Signaling message history feature allows you to store messages when publishing them to a channel. For example, if a user joins a channel midway through a conversation, you can retrieve messages that were published before the user joined. You can configure the message retention period for each project, ranging from 1 day to permanent storage.

    When a message is published, it is stored using the channel name and the message timestamp. You can use this information to retrieve historical messages.

    <CalloutContainer type="info">
      <CalloutDescription>
        The historical message feature is currently available for User Channels and Message Channels, but not for Stream Channels.
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-4]

    Before implementing this feature, ensure that you have:

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../index) page.
    * Enabled the message history feature by contacting [support@agora.io](mailto\:support@agora.io). You can request a storage duration of 1, 7, 30, 90, or 365 days, or opt for permanent storage.

      <CalloutContainer type="info">
        <CalloutDescription>
          Changes to storage duration only affect future messages.
        </CalloutDescription>
      </CalloutContainer>

    ## Implement message history [#implement-message-history-4]

    This section shows how to store and retrieve messages from history.

    ### Store messages [#store-messages-4]

    To store a message on the server, set `setStoreInHistory` to `true` in the `publish()` method. The following example shows the minimum code to store a message published to a message channel:

    ```cpp
    uint64_t requestId = 0;
    std::string channelName = "chatRoom"
    std::string message = "Hello world!";

    PublishOptions options;
    options.storeInHistory = true;
    options.channelType = RTM_CHANNEL_TYPE_MESSAGE;
    options.messageType = RTM_MESSAGE_TYPE_STRING;

    rtmClient_->publish(channelName.c_str(), message.c_str(), message.size(), options,
                       requestId);
    ```

    To store a message in the history of a user channel, use the same parameter:

    ```cpp
    uint64_t requestId = 0;
    std::string userId = "Tony"
    std::string message = "Hello world!";

    PublishOptions options;
    options.storeInHistory = true;
    options.channelType = RTM_CHANNEL_TYPE_USER;
    options.messageType = RTM_MESSAGE_TYPE_STRING;

    rtmClient_->(userId.c_str(), message.c_str(), message.size(), options,
                requestId);
    ```

    ### Retrieve messages [#retrieve-messages-4]

    The SDK provides the `getMessages()` method to retrieve up to 100 history messages at a time. You can filter messages by setting the `start` and `end` parameters to define a time range. To retrieve only the most recent messages, such as those sent between the last offline time and now, set only the `end` parameter.

    The following example retrieves the latest 50 messages sent before a specific time:

    ```cpp
    uint64_t requestId = 0;
    std::string channelName = "chatRoom";

    GetHistoryMessagesOptions options;
    options.messageCount = 50;
    options.end = 1688978391800;

    rtmClient_->getHistory()->getMessages(channelName.c_str(), RTM_CHANNEL_TYPE_MESSAGE,
                                         options, requestId);
    ```

    The `result` includes a `newStart` field, which indicates whether there are unread messages i the history:

    * If `newStart` is `0`, all messages have been retrieved.
    * If `newStart` is not `0`, use this value as the new `start` parameter in the next `getMessages()` call to continue retrieving messages.

    The following use-cases show how to specify the `start` and `end` parameters to retrieve messages in a specific range:

    #### Retrieve before a start timestamp [#retrieve-before-a-start-timestamp-4]

    | Parameter | Behavior                                                                                |
    | --------- | --------------------------------------------------------------------------------------- |
    | `start`   | Returns messages sent before the specified timestamp. The timestamp itself is excluded. |

    ```text
    Timeline:
    [Older messages] ------------ start timestamp ------------ [Newer messages]
    [             ←-- n messages]
    ```

    #### Retrieve messages up to an end timestamp [#retrieve-messages-up-to-an-end-timestamp-4]

    | Parameter | Behavior                                                                             |
    | --------- | ------------------------------------------------------------------------------------ |
    | `end`     | Returns the n most recent messages sent up to and including the specified timestamp. |

    ```text
    Timeline:
    [Older messages] ------------ end timestamp ------------ [Newer messages]
                                           [  ←-- n messages]
    ```

    #### Retrieve messages between two timestamps [#retrieve-messages-between-two-timestamps-4]

    | Parameter | Behavior                                                  |
    | --------- | --------------------------------------------------------- |
    | `start`   | Returns messages sent before this timestamp (excluded).   |
    | `end`     | Returns messages sent up to and including this timestamp. |

    ```text
    Timeline:
    [Older messages] ---- end timestamp ------------ start timestamp ---- [Newer messages]
                                   [ ←-- n messages]
    ```

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="linux-cpp">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="linux-cpp" />

    The Signaling message history feature allows you to store messages when publishing them to a channel. For example, if a user joins a channel midway through a conversation, you can retrieve messages that were published before the user joined. You can configure the message retention period for each project, ranging from 1 day to permanent storage.

    When a message is published, it is stored using the channel name and the message timestamp. You can use this information to retrieve historical messages.

    <CalloutContainer type="info">
      <CalloutDescription>
        The historical message feature is currently available for User Channels and Message Channels, but not for Stream Channels.
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-5]

    Before implementing this feature, ensure that you have:

    * Integrated the Signaling SDK in your project, and implemented the framework functionality from the [SDK quickstart](../../index) page.
    * Enabled the message history feature by contacting [support@agora.io](mailto\:support@agora.io). You can request a storage duration of 1, 7, 30, 90, or 365 days, or opt for permanent storage.

      <CalloutContainer type="info">
        <CalloutDescription>
          Changes to storage duration only affect future messages.
        </CalloutDescription>
      </CalloutContainer>

    ## Implement message history [#implement-message-history-5]

    This section shows how to store and retrieve messages from history.

    ### Store messages [#store-messages-5]

    To store a message on the server, set `setStoreInHistory` to `true` in the `publish()` method. The following example shows the minimum code to store a message published to a message channel:

    ```cpp
    uint64_t requestId = 0;
    std::string channelName = "chatRoom"
    std::string message = "Hello world!";

    PublishOptions options;
    options.storeInHistory = true;
    options.channelType = RTM_CHANNEL_TYPE_MESSAGE;
    options.messageType = RTM_MESSAGE_TYPE_STRING;

    rtmClient_->publish(channelName.c_str(), message.c_str(), message.size(), options,
                       requestId);
    ```

    To store a message in the history of a user channel, use the same parameter:

    ```cpp
    uint64_t requestId = 0;
    std::string userId = "Tony"
    std::string message = "Hello world!";

    PublishOptions options;
    options.storeInHistory = true;
    options.channelType = RTM_CHANNEL_TYPE_USER;
    options.messageType = RTM_MESSAGE_TYPE_STRING;

    rtmClient_->(userId.c_str(), message.c_str(), message.size(), options,
                requestId);
    ```

    ### Retrieve messages [#retrieve-messages-5]

    The SDK provides the `getMessages()` method to retrieve up to 100 history messages at a time. You can filter messages by setting the `start` and `end` parameters to define a time range. To retrieve only the most recent messages, such as those sent between the last offline time and now, set only the `end` parameter.

    The following example retrieves the latest 50 messages sent before a specific time:

    ```cpp
    uint64_t requestId = 0;
    std::string channelName = "chatRoom";

    GetHistoryMessagesOptions options;
    options.messageCount = 50;
    options.end = 1688978391800;

    rtmClient_->getHistory()->getMessages(channelName.c_str(), RTM_CHANNEL_TYPE_MESSAGE,
                                         options, requestId);
    ```

    The `result` includes a `newStart` field, which indicates whether there are unread messages i the history:

    * If `newStart` is `0`, all messages have been retrieved.
    * If `newStart` is not `0`, use this value as the new `start` parameter in the next `getMessages()` call to continue retrieving messages.

    The following use-cases show how to specify the `start` and `end` parameters to retrieve messages in a specific range:

    #### Retrieve before a start timestamp [#retrieve-before-a-start-timestamp-5]

    | Parameter | Behavior                                                                                |
    | --------- | --------------------------------------------------------------------------------------- |
    | `start`   | Returns messages sent before the specified timestamp. The timestamp itself is excluded. |

    ```text
    Timeline:
    [Older messages] ------------ start timestamp ------------ [Newer messages]
    [             ←-- n messages]
    ```

    #### Retrieve messages up to an end timestamp [#retrieve-messages-up-to-an-end-timestamp-5]

    | Parameter | Behavior                                                                             |
    | --------- | ------------------------------------------------------------------------------------ |
    | `end`     | Returns the n most recent messages sent up to and including the specified timestamp. |

    ```text
    Timeline:
    [Older messages] ------------ end timestamp ------------ [Newer messages]
                                           [  ←-- n messages]
    ```

    #### Retrieve messages between two timestamps [#retrieve-messages-between-two-timestamps-5]

    | Parameter | Behavior                                                  |
    | --------- | --------------------------------------------------------- |
    | `start`   | Returns messages sent before this timestamp (excluded).   |
    | `end`     | Returns messages sent up to and including this timestamp. |

    ```text
    Timeline:
    [Older messages] ---- end timestamp ------------ start timestamp ---- [Newer messages]
                                   [ ←-- n messages]
    ```

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>
</_PlatformTabsGroup>
