# Use in-meeting chat

<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

> The code on this page works with either the **default UI** or the **custom UI**.

The Meeting SDK lets you [use in-meeting chat](https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0064400) to communicate with other participants in the same meeting.
Handle chat functionality through the `IMeetingChatController` interface.

```cpp
IMeetingService* meetingService = /* existing service instance */;
  IMeetingChatController* meetingChatController =
      meetingService ? meetingService->GetMeetingChatController() : nullptr;

  if (meetingChatController) {
      // chat APIs here
  }
```

## Control chat settings

Before you use in-meeting chat, check if the current meeting or webinar supports chat, and then look at which participants or attendees can use chat.

## Understand chat callbacks

For chat-related callbacks, implement a single event interface `IMeetingChatCtrlEvent`, then register it with `IMeetingChatController::SetEvent(...)`.

The core chat callbacks are

-   `onChatMsgNotification(...)`
-   `onChatStatusChangedNotification(...)`
-   `onChatMsgDeleteNotification(...)`
-   `onChatMessageEditNotification(...)`

`IMeetingChatCtrlEvent` also includes share-meeting-chat status and file-transfer callbacks if you use those features.

### Chat settings in a meeting

In a meeting, use `IMeetingParticipantsController`.

```cpp
IMeetingService* meetingService = /* existing service instance */;
  if (meetingService) {
      // Quick check (equivalent to isParticipantsChatAllowed)
      IMeetingParticipantsController* participantsCtrl =
          meetingService->GetMeetingParticipantsController();
      if (participantsCtrl && participantsCtrl->IsParticipantAllowedToChat()) {
          // participant can chat
      }

      // Detailed breakdown (equivalent to getChatStatus().getNormalMeetingPrivilege())
      IMeetingChatController* chatCtrl = meetingService->GetMeetingChatController();
      if (chatCtrl) {
          const ChatStatus* status = chatCtrl->GetChatStatus();
          if (status && !status->is_webinar_meeting && !status->is_chat_off) {
              const NormalMeetingChatStatus& p = status->ut.normal_meeting_status;

              bool canChat = p.can_chat;
              bool canChatToAll = p.can_chat_to_all;
              bool canChatToIndividual = p.can_chat_to_individual;
              bool isOnlyCanChatToHost = p.is_only_can_chat_to_host;
          }
      }
  }
```

The `ZoomSDKNormalMeetingChatPrivilege` has these values.

-   `can_chat` - user can send chat messages.
-   `can_chat_to_all` - user can send to everyone.
-   `can_chat_to_individual` - user can send private messages to individuals.
-   `is_only_can_chat_to_host` - user is restricted to host-only chat.

If the current user is the meeting's host, they can control chat settings by specifying a `SDKChatPrivilege`.

```cpp
IMeetingService* meetingService = /* existing service instance */;
  IMeetingChatController* chatCtrl =
      meetingService ? meetingService->GetMeetingChatController() : nullptr;

  if (chatCtrl) {
      chatCtrl->SetParticipantsChatPrivilege(SDK_CHAT_PRIVILEGE_ALL);
      chatCtrl->SetParticipantsChatPrivilege(SDK_CHAT_PRIVILEGE_HOST);
      chatCtrl->SetParticipantsChatPrivilege(SDK_CHAT_PRIVILEGE_DISABLE_ATTENDEE_CHAT);
      chatCtrl->SetParticipantsChatPrivilege(SDK_CHAT_PRIVILEGE_HOST_PUBLIC);
  }
```

### Chat settings in a webinar

In a webinar, use `IMeetingChatController` or `IMeetingWebinarController`.

```cpp
IMeetingService* meetingService = /* existing service instance */;

  // 1. Webinar controller: panelist chat privilege
  IMeetingWebinarController* webinarCtrl =
      meetingService ? meetingService->GetMeetingWebinarController() : nullptr;

  if (webinarCtrl) {
      SDKPanelistChatPrivilege panelistChatPrivilege = SDKPanelistChatPrivilege_PanelistOnly;
      if (webinarCtrl->GetPanelistChatPrivilege(panelistChatPrivilege) == SDKERR_SUCCESS) {
          switch (panelistChatPrivilege) {
              case SDKPanelistChatPrivilege_PanelistOnly:
                  // panelists chat with panelists only
                  break;
              case SDKPanelistChatPrivilege_All:
                  // panelists chat with everyone
                  break;
              default:
                  break;
          }
      }
  }

  // 2. Detailed attendee/panelist breakdown via ChatStatus
  IMeetingChatController* chatCtrl =
      meetingService ? meetingService->GetMeetingChatController() : nullptr;

  if (chatCtrl) {
      const ChatStatus* status = chatCtrl->GetChatStatus();
      if (status && status->is_webinar_meeting) {
          if (status->is_webinar_attendee) {
              const WebinarAttendeeChatStatus& attendee = status->ut.webinar_attendee_status;
              bool canChat = attendee.can_chat;
              bool canChatToAllPanellist = attendee.can_chat_to_all_panellist;
              bool canChatToAllPanellistAndAttendee = attendee.can_chat_to_all_panellist_and_attendee;
          } else {
              const WebinarOtherUserRoleChatStatus& other = status->ut.webinar_other_status;
              bool canChatToIndividual = other.can_chat_to_individual;
              bool canChatToAllPanellist = other.can_chat_to_all_panellist;
              bool canChatToAllPanellistAndAttendee = other.can_chat_to_all_panellist_and_attendee;
          }
      }
  }
```

The `WebinarAttendeeChatStatus` uses one of these values.

-   `can_chat` - attendee can send chat.
-   `can_chat_to_all_panellist` - attendee can send to all panelists.
-   `can_chat_to_all_panellist_and_attendee` - attendee can send to panelists and attendees.

For panelists, hosts, or co-hosts use `WebinarOtherUserRoleChatStatus`.

-   `can_chat_to_individual` - can send to individual attendee.
-   `can_chat_to_all_panellist` - can send to all panelists.
-   `can_chat_to_all_panellist_and_attendee` - can send to everyone.

Update a webinar's chat settings by setting a `SDKChatPrivilege`.

```cpp
IMeetingService* meetingService = /* existing service instance */;
  IMeetingChatController* chatCtrl =
      meetingService ? meetingService->GetMeetingChatController() : nullptr;

  if (chatCtrl) {
      chatCtrl->SetParticipantsChatPrivilege(SDK_CHAT_PRIVILEGE_ALL);
      chatCtrl->SetParticipantsChatPrivilege(SDK_CHAT_PRIVILEGE_ALL_PANELIST);
      chatCtrl->SetParticipantsChatPrivilege(SDK_CHAT_PRIVILEGE_DISABLE_ATTENDEE_CHAT);
  }
```

## Send messages

To send a public message in either a meeting or a webinar, instantiate a `IChatMsgInfoBuilder` and set the content and message type.

```cpp
IMeetingService* meetingService = /* existing service instance */;
  IMeetingChatController* meetingChatController =
      meetingService ? meetingService->GetMeetingChatController() : nullptr;

  if (meetingChatController) {
      IChatMsgInfoBuilder* builder = meetingChatController->GetChatMessageBuilder();
      if (builder) {
          IChatMsgInfo* msg = builder
              ->Clear()
              ->SetContent("Hello World!")
              ->SetReceiver(0) // 0 = all/all-panelists (depends on message type)
              ->SetMessageType(SDKChatMessageType_To_All)
              ->Build();

          if (msg) {
              SDKError err = meetingChatController->SendChatMsgTo(msg);
              // handle err if needed
          }
      }
  }
```

To reply to an existing message in a thread, first get a `threadId` for the message being responded to. For more information on how to get the `threadId`, see [Receive messages](#receive-messages). Once you've retrieved the thread ID, pass it in and set the type based on whether the parent message is public or private.

```cpp
IMeetingService* meetingService = /* existing service instance */;
  IMeetingChatController* meetingChatController =
      meetingService ? meetingService->GetMeetingChatController() : nullptr;

  if (meetingChatController) {
      // 1. Identify parent message and get thread ID
      IChatMsgInfo* parentMsg = /* from callback or GetChatMessageById(...) */;
      if (!parentMsg) return;

      const zchar_t* threadID = parentMsg->GetThreadID();
      if (!threadID) return;

      // 2. Build reply with thread ID
      IChatMsgInfoBuilder* builder = meetingChatController->GetChatMessageBuilder();
      if (!builder) return;

      IChatMsgInfo* threadReplyMessage = builder
          ->Clear()
          ->SetThreadId(threadID)
          ->SetContent("Reply to Hello World!")
          ->SetMessageType(SDKChatMessageType_To_All)
          ->Build();

      // 3) Send reply
      if (threadReplyMessage) {
          SDKError err = meetingChatController->SendChatMsgTo(threadReplyMessage);
          // handle err if needed
      }
  }
```

Messages sent from the SDK can also include quoted text. The quote position specifies the start and end characters in the quoted text string.

```cpp
IMeetingService* meetingService = /* existing service instance */;
  IMeetingChatController* meetingChatController =
      meetingService ? meetingService->GetMeetingChatController() : nullptr;

  if (meetingChatController) {
      IChatMsgInfoBuilder* builder = meetingChatController->GetChatMessageBuilder();
      if (builder) {
          // positionStart=0, positionEnd=42 as example
          IChatMsgInfo* quoteMessage = builder
              ->Clear()
              ->SetQuotePosition(0, 42)
              ->SetContent("This is a sample message stylized as a quote")
              ->SetMessageType(SDKChatMessageType_To_All)
              ->Build();

          if (quoteMessage) {
              SDKError err = meetingChatController->SendChatMsgTo(quoteMessage);
              // handle err if needed
          }
      }
  }
```

## Receive messages

To display content from chat messages, the SDK provides chat event callbacks that fire when a new message is posted in the meeting. You can also use these callbacks to confirm that a message sent by the current user was delivered to meeting chat.

Implement the `IMeetingChatCtrlEvent` interface, register it with `IMeetingChatController::SetEvent(...)`, and read message details from the `IChatMsgInfo*` passed to `onChatMsgNotification(...)`. Some fields, such as the receiver user ID or display name, are only populated for message types that target specific recipients.

```cpp
class ChatEventHandler : public IMeetingChatCtrlEvent {
  public:
      void onChatMsgNotification(IChatMsgInfo* chatMsg, const zchar_t* content = nullptr) override {
          if (!chatMsg) return;

          const zchar_t* messageId = chatMsg->GetMessageID();
          const zchar_t* msgContent = chatMsg->GetContent();
          time_t timestamp = chatMsg->GetTimeStamp();
          unsigned int senderUserId = chatMsg->GetSenderUserId();
          const zchar_t* senderDisplayName = chatMsg->GetSenderDisplayName();
          unsigned int receiverUserId = chatMsg->GetReceiverUserId();
          const zchar_t* receiverDisplayName = chatMsg->GetReceiverDisplayName();
          bool isToWaitingRoom = chatMsg->IsChatToWaitingroom();
          SDKChatMessageType messageType = chatMsg->GetChatMessageType();
          bool isComment = chatMsg->IsComment();
          bool isThread = chatMsg->IsThread();
          const zchar_t* threadId = chatMsg->GetThreadID();
          IList<SegmentDetails>* segments = chatMsg->GetSegmentDetails();

          // use values as needed
          (void)messageId;
          (void)msgContent;
          (void)timestamp;
          (void)senderUserId;
          (void)senderDisplayName;
          (void)receiverUserId;
          (void)receiverDisplayName;
          (void)isToWaitingRoom;
          (void)messageType;
          (void)isComment;
          (void)isThread;
          (void)threadId;
          (void)segments;
          (void)content;
      }

      void onChatStatusChangedNotification(ChatStatus* status_) override {}
      void onChatMsgDeleteNotification(const zchar_t* msgID, SDKChatMessageDeleteType deleteBy) override {}
      void onChatMessageEditNotification(IChatMsgInfo* chatMsg) override {}
      void onShareMeetingChatStatusChanged(bool isStart) override {}

      // Also implement remaining pure-virtual file-transfer callbacks from IMeetingChatCtrlEvent.
  };

  void BindChatEvents(IMeetingService* meetingService, ChatEventHandler& handler) {
      if (!meetingService) return;

      IMeetingChatController* meetingChatController = meetingService->GetMeetingChatController();
      if (!meetingChatController) return;

      meetingChatController->SetEvent(&handler);
  }
```

## Delete messages

To delete a message sent by the current user, call `IMeetingChatController::DeleteChatMessage(msgID)` after checking `IMeetingChatController::IsChatMessageCanBeDeleted(msgID)`. A successful deletion triggers `onChatMsgDeleteNotification(...)` on your registered `IMeetingChatCtrlEvent` handler.

```cpp
IMeetingService* meetingService = /* existing service instance */;
  IMeetingChatController* chatCtrl =
      meetingService ? meetingService->GetMeetingChatController() : nullptr;

  if (chatCtrl) {
      const zchar_t* msgID = /* message ID */;
      if (msgID && chatCtrl->IsChatMessageCanBeDeleted(msgID)) {
          SDKError err = chatCtrl->DeleteChatMessage(msgID);
          // optional: handle err
      }
  }
```

## Parse text format

The SDK supports rich-text chat formatting such as bold, italic, quote, font color, font size, and more. Formatting details are exposed as an `IList<SegmentDetails>*` returned by `IChatMsgInfo::GetSegmentDetails()` for each chat message.

```cpp
IChatMsgInfo* chatInfo = /* from onChatMsgNotification(...) */;
  if (chatInfo) {
      IList<SegmentDetails>* chatInfoSegmentDetails = chatInfo->GetSegmentDetails();
      if (chatInfoSegmentDetails) {
          for (int i = 0; i < chatInfoSegmentDetails->GetCount(); ++i) {
              SegmentDetails segment = chatInfoSegmentDetails->GetItem(i);

              const zchar_t* text = segment.strContent;
              BoldAttrs bold = segment.boldAttrs;
              ItalicAttrs italic = segment.italicAttrs;
              UnderlineAttrs underline = segment.underlineAttrs; // and more
          }
      }
  }
```

---
