Use in-meeting chat

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

The Meeting SDK lets you use in-meeting chat to communicate with other participants in the same meeting. Handle chat functionality through the IMeetingChatController interface.

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.

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.

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.

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.

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.

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. Once you've retrieved the thread ID, pass it in and set the type based on whether the parent message is public or private.

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.

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.

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.

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.

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
          }
      }
  }