Kotlin Chat SDK 1.0.0 migration guide
The 1.0.0 release of the Kotlin Chat SDK introduces a set of API improvements that make real-time subscriptions more discoverable, event payloads strongly typed, and membership management more expressive. The core change is the shift from generic listener methods to entity-owned callbacks: instead of calling chat.listenForEvents() or passing a callback to channel.connect(), you now attach callbacks directly to entity objects (channel.onMessageReceived, user.onMentioned, etc.).
This guide is for developers who use Kotlin Chat SDK 0.x.x in existing applications.
If your application uses 0.x.x, it will not compile against 1.0.0 without changes to join(), Message.reactions, and GetCurrentUserMentionsResult. This guide summarizes the differences between the versions and shows how to migrate to Kotlin Chat SDK 1.0.0.
Most notable changes include:
join()signature change: The callback parameter is removed andjoin()no longer subscribes to messages automatically. CallonMessageReceived()separately after joining to start message delivery.- Entity-first callbacks: All streaming methods gain entity-owned alternatives.
connect()→onMessageReceived(),getTyping()→onTypingChanged(),streamPresence()→onPresenceChanged(),streamUpdates()→onUpdated()+onDeleted(). The old methods are deprecated but still compile. - Typed event payloads: Events that previously required manual
EventContentparsing now deliver purpose-built types.onMentioned()deliversMention,onInvited()deliversInvite,onRestrictionChanged()deliversRestriction, andonReadReceiptReceived()deliversReadReceipt. Message.reactionstype change:Message.reactionschanged fromMap<String, List<Action>>toList<MessageReaction>. EachMessageReactionexposesvalue,count,isMine, anduserIds.GetCurrentUserMentionsResult.mentions: TheenhancedMentionsDatafield is deprecated (still available) and a newmentionsfield is added. Its element type changes from the union ofUserMentionData,ChannelMentionData, andThreadMentionDatato a unifiedUserMentiontype with fieldsmessage(aMessageobject),userId,channelId, andparentChannelId?.- New read receipt capabilities:
channel.fetchReadReceipts()returns a paginated snapshot of read positions. A newemitReadReceiptEventsconfiguration option controls which channel types emit read receipt signals. sendText()simplified: A newsendText(text, params: SendTextParams)overload consolidates the previous multi-parameter signature. The old overload is deprecated.- New membership methods:
channel.hasMember(),channel.getMember(),user.isMemberOf(), anduser.getMembership()let you check and retrieve a specific membership without fetching the full list.
Differences between 0.x.x and 1.0.0
| Feature/Method | 0.x.x | 1.0.0 |
|---|---|---|
join() return type | PNFuture<JoinResult> (includes disconnect) | PNFuture<Membership> |
join() signature | join(custom, callback) | join(status?, type?, custom?) |
JoinResult | Available | Removed |
Message delivery after join() | Automatic (via callback) | Requires explicit onMessageReceived() call |
connect() | Primary method for message delivery | Deprecated — use channel.onMessageReceived() |
getTyping() | Typing indicator streaming | Deprecated — use channel.onTypingChanged() |
streamPresence() | Presence streaming | Deprecated — use channel.onPresenceChanged() |
streamUpdates() on entity | Update streaming (single entity) | Deprecated — use onUpdated() |
streamUpdatesOn() on entity | Update streaming (multiple entities) | Still supported |
| Deletion events | Part of streamUpdates() callback | Separate onDeleted() event |
streamReadReceipts() | Map<Long, List<String>> (timetoken → user IDs) | Deprecated — use channel.onReadReceiptReceived() with typed ReadReceipt |
streamMessageReports() | Raw event callback | Deprecated — use channel.onMessageReported() |
Message.reactions type | Map<String, List<Action>> | List<MessageReaction> |
Message.actions | Available | Deprecated |
| Mention events | chat.listenForEvents<EventContent.Mention>() | user.onMentioned(Mention) |
| Invite events | chat.listenForEvents<EventContent.Invite>() | user.onInvited(Invite) |
| Moderation events | chat.listenForEvents<EventContent.Moderation>() | user.onRestrictionChanged(Restriction) |
| Custom events (emit) | chat.emitEvent() | Deprecated — use channel.emitCustomEvent() |
| Custom events (listen) | chat.listenForEvents<EventContent.Custom>() | Deprecated — use channel.onCustomEvent() |
EventContent.* types | Available | Deprecated |
sendText() options | Multi-parameter overload (with quotedMessage, mentionedUsers, files) | New sendText(text, params: SendTextParams) overload for publishing options (meta, shouldStore, usePost, ttl, customPushData); old overload deprecated |
| Read receipts snapshot | Not available | channel.fetchReadReceipts() → ReadReceiptsResponse |
emitReadReceiptEvents config | Not available | Available in ChatConfiguration |
GetCurrentUserMentionsResult field | enhancedMentionsData: List<...> | mentions: List<UserMention> |
| Membership existence check | getMembers() then filter | channel.hasMember(userId), channel.getMember(userId) |
| User membership check | getMemberships() then filter | user.isMemberOf(channelId), user.getMembership(channelId) |
removeThread() return | PNFuture<Pair<ThreadChannel, Message>> | PNFuture<Unit> |
Channel.delete() / User.delete() | Accepts soft parameter | soft parameter removed; returns PNFuture<Unit> |
Membership.update() signature | update(custom) | update(status?, type?, custom?) |
join() and onMessageReceived()
Important change
This change affects any code that calls join() and then listens for messages. Update it before releasing to users.
In 0.x.x, calling join() on a channel both created the membership server-side and subscribed to message delivery automatically — messages arrived via the callback you passed to join(). In 1.0.0, join() creates the membership only. Call onMessageReceived() separately to start receiving messages.
The JoinResult type is removed. join() now returns PNFuture<Membership> directly.
- 0.x.x
- 1.0.0
1channel.join(custom = mapOf("role" to "member")) { message ->
2 println("New message: ${message.text}")
3}.async { result ->
4 result.onSuccess { joinResult ->
5 val membership = joinResult.membership
6 val disconnect = joinResult.disconnect
7 }.onFailure { exception ->
8 println("Join failed: ${exception.message}")
9 }
10}
1// join() creates the membership — messages are not delivered automatically
2channel.join(
3 status = "active",
4 custom = mapOf("role" to "member")
5).async { result ->
6 result.onSuccess { membership ->
7 // Subscribe to messages separately
8 val disconnect: AutoCloseable = channel.onMessageReceived { message ->
9 println("New message: ${message.text}")
10 }
11 }.onFailure { exception ->
12 println("Join failed: ${exception.message}")
13 }
14}
15
show all 19 linesEntity-first callbacks
In 0.x.x, streaming subscriptions were created by calling methods on the Chat or Channel object and passing a callback as a parameter. In 1.0.0, entity objects expose dedicated callback properties. Attach a lambda directly to the entity; the method returns an AutoCloseable that you call .close() on to stop the subscription.
The old instance methods (connect(), getTyping(), streamPresence(), streamUpdates()) are deprecated but still compile. The companion streamUpdatesOn() helper remains supported. Migrate to the new names at your own pace.
- 0.x.x
- 1.0.0
1// Channel — message delivery
2val stopMessages: AutoCloseable = channel.connect { message ->
3 println("Message: ${message.text}")
4}
5
6// Channel — typing indicator
7val stopTyping: AutoCloseable = channel.getTyping { typingUserIds ->
8 println("Typing: $typingUserIds")
9}
10
11// Channel — presence
12val stopPresence: AutoCloseable = channel.streamPresence { presence ->
13 println("Present: ${presence.occupants}")
14}
15
show all 29 lines1// Channel — message delivery
2val stopMessages: AutoCloseable = channel.onMessageReceived { message ->
3 println("Message: ${message.text}")
4}
5
6// Channel — typing indicator
7val stopTyping: AutoCloseable = channel.onTypingChanged { typingUserIds ->
8 println("Typing: $typingUserIds")
9}
10
11// Channel — presence
12val stopPresence: AutoCloseable = channel.onPresenceChanged { userIds ->
13 println("Present: $userIds")
14}
15
show all 41 linesThe same pattern applies to Message.onUpdated() for individual message update subscriptions.
Typed event payloads
In 0.x.x, invite, mention, and moderation events required calling chat.listenForEvents() with a generic EventContent type parameter and reading fields from the raw payload. In 1.0.0, these events are exposed as dedicated entity callbacks with purpose-built types. The chat.listenForEvents() method and all EventContent.* subtypes are deprecated.
- 0.x.x
- 1.0.0
1// Mention events — raw ChatEvent with EventContent.Mention payload
2val stopMentions: AutoCloseable = chat.listenForEvents<EventContent.Mention>(
3 channelId = currentUser.id
4) { event ->
5 val payload = event.payload as? EventContent.Mention
6 println("Mentioned in: ${payload?.channelId}")
7}
8
9// Invite events — raw ChatEvent with EventContent.Invite payload
10val stopInvites: AutoCloseable = chat.listenForEvents<EventContent.Invite>(
11 channelId = currentUser.id
12) { event ->
13 val payload = event.payload as? EventContent.Invite
14 println("Invited to: ${payload?.channelId} by ${payload?.invitedByUserId}")
15}
show all 29 lines1// Mention events — typed Mention struct
2val stopMentions: AutoCloseable = user.onMentioned { mention ->
3 println("Mentioned in: ${mention.channelId}")
4 println("By: ${mention.mentionedByUserId}")
5 println("Message timetoken: ${mention.messageTimetoken}")
6}
7
8// Invite events — typed Invite struct
9val stopInvites: AutoCloseable = user.onInvited { invite ->
10 println("Invited to: ${invite.channelId} (${invite.channelType})")
11 println("By: ${invite.invitedByUserId} at ${invite.invitationTimetoken}")
12}
13
14// Moderation events — typed Restriction struct
15val stopModeration: AutoCloseable = user.onRestrictionChanged { restriction ->
show all 24 linesRead receipts
The read receipts API gains two new capabilities in 1.0.0: a typed real-time callback (onReadReceiptReceived()) and an on-demand snapshot (fetchReadReceipts()). The old streamReadReceipts() method is deprecated.
In 0.x.x, streamReadReceipts() delivered a Map<Long, List<String>> on every event, where keys were timetokens and values were lists of user IDs who had read up to that point.
In 1.0.0, onReadReceiptReceived() fires once per incoming receipt signal and delivers a single ReadReceipt value containing the user ID and their last-read timetoken. fetchReadReceipts() provides a paginated snapshot of the current read state for all channel members.
- 0.x.x
- 1.0.0
1val stop: AutoCloseable = channel.streamReadReceipts { receipts ->
2 // receipts: Map<Long, List<String>> — timetoken to user IDs
3 receipts.forEach { (timetoken, userIds) ->
4 println("Timetoken $timetoken read by $userIds")
5 }
6}
1// Real-time updates — one event per user receipt
2val stop: AutoCloseable = channel.onReadReceiptReceived { receipt ->
3 println("User ${receipt.userId} read up to ${receipt.lastReadTimetoken}")
4}
5
6// On-demand snapshot of all current read positions
7channel.fetchReadReceipts().async { result ->
8 result.onSuccess { response ->
9 response.receipts.forEach { receipt ->
10 println("${receipt.userId}: ${receipt.lastReadTimetoken}")
11 }
12 }
13}
Controlling which channel types emit read receipts
A new emitReadReceiptEvents parameter in ChatConfiguration controls which channel types produce read receipt signals. By default, public channels do not emit read receipts, which is appropriate for large-scale deployments.
1val config = ChatConfiguration(
2 // Default: public = false, group = true, direct = true
3 // To restore the old behavior (always emit), set all to true:
4 emitReadReceiptEvents = EmitReadReceiptEvents(
5 public = true,
6 group = true,
7 direct = true
8 )
9)
Behavioral change
In 1.0.0, setLastReadMessage(), setLastReadMessageTimetoken(), and markAllMessagesAsRead() now emit receipt events by default on direct and group channels. If your app previously relied on these methods not emitting events, you may see unexpected receipt signals and additional network traffic after upgrading. Set emitReadReceiptEvents to EmitReadReceiptEvents(direct = false, group = false) or the equivalent Map<ChannelType, Boolean> to restore the old behavior.
Message reactions
Important change
This change affects any code that reads Message.reactions or Message.actions. Update it before releasing to users.
In 0.x.x, Message.reactions returned a Map<String, List<Action>>. Grouping reactions by value, counting them, or checking whether the current user had reacted required manual processing of the raw action list.
In 1.0.0, Message.reactions returns List<MessageReaction>. Each MessageReaction aggregates reactions of the same value and exposes count (how many users reacted), isMine (whether the current user is among them), and userIds (list of reacting user IDs). The raw Message.actions property is deprecated.
- 0.x.x
- 1.0.0
1// Map<String, List<Action>> — manual grouping required
2val rawReactions: Map<String, List<Action>> = message.actions
3rawReactions.forEach { (type, actions) ->
4 println("$type: ${actions.size} reaction(s)")
5 val isMine = actions.any { it.uuid == chat.currentUser.id }
6 println("Mine: $isMine")
7}
1// List<MessageReaction> — aggregated by reaction value
2val reactions: List<MessageReaction> = message.reactions
3reactions.forEach { reaction ->
4 println("${reaction.value}: ${reaction.count} reaction(s), mine: ${reaction.isMine}")
5 println("Reacted by: ${reaction.userIds}")
6}
Sending messages in 1.0.0
1.0.0 separates message sending into two distinct paths: a lightweight API for plain text and a composition API for rich content.
| Use case | API |
|---|---|
Send plain text with delivery options (meta, ttl, shouldStore, push options) | channel.sendText(text, params: SendTextParams) |
| Build messages with mentions, channel references, links, files, or quoted messages | channel.createMessageDraft() → draft.send() |
Existing multi-parameter sendText() calls with quotedMessage, mentionedUsers, files | Still compiles — deprecated; migrate to MessageDraft for new integrations |
SendTextParams covers lightweight delivery options only: meta, shouldStore, usePost, ttl, and customPushData. It does not accept rich-content parameters.
For rich composition, prefer channel.createMessageDraft() for new integrations. The old multi-parameter sendText() overload remains available for compatibility but is deprecated and should not be the recommended path in new code.
Simple send
1channel.sendText(
2 text = "Hi everyone!",
3 params = SendTextParams(
4 meta = mapOf("priority" to "high"),
5 shouldStore = true,
6 ttl = 15
7 )
8).async { result ->
9 result.onSuccess { message ->
10 println("Sent: ${message.timetoken}")
11 }
12}
Rich send with MessageDraft
1chat.getChannel("support").async { channelResult ->
2 channelResult.onSuccess { channel ->
3 if (channel != null) {
4 val draft = channel.createMessageDraft(
5 isTypingIndicatorTriggered = true,
6 userLimit = 10,
7 channelLimit = 10
8 )
9 val listener = MessageDraftChangeListener { elements, suggestedMentionsFuture ->
10 renderComposer(elements)
11 suggestedMentionsFuture.async { suggestionsResult ->
12 suggestionsResult.onSuccess { suggestions ->
13 renderSuggestions(suggestions)
14 }
15 }
show all 30 linesMigrating from 0.x.x
- 0.x.x
- 1.0.0
1channel.sendText(
2 text = "See you there!",
3 quotedMessage = someMessage,
4 mentionedUsers = mapOf(8 to mentionedUser),
5 files = listOf(myFile)
6).async { result ->
7 result.onSuccess { message ->
8 println("Sent: ${message.timetoken}")
9 }
10}
1// Plain text with delivery options
2val params = SendTextParams(
3 meta = mapOf("priority" to "high"),
4 shouldStore = true,
5 ttl = 24
6)
7channel.sendText(text = "See you there!", params = params).async { result ->
8 result.onSuccess { message ->
9 println("Sent: ${message.timetoken}")
10 }
11}
12
13// Rich content — use MessageDraft
14// The old multi-parameter sendText() overload is deprecated; prefer createMessageDraft() for new integrations
removeThread() return type
In 0.x.x, removeThread() returned PNFuture<Pair<ThreadChannel, Message>> containing the removed thread channel and the updated parent message. In 1.0.0, it returns PNFuture<Unit> — the operation completes without returning data.
- 0.x.x
- 1.0.0
1message.removeThread().async { result ->
2 result.onSuccess { (threadChannel, parentMessage) ->
3 println("Thread removed from: ${threadChannel.id}")
4 println("Parent hasThread: ${parentMessage.hasThread}")
5 }
6}
1message.removeThread().async { result ->
2 result.onSuccess {
3 println("Thread removed successfully.")
4 }
5}
Soft-delete removed from entity deletion
In 0.x.x, Channel.delete(), ThreadChannel.delete(), User.delete(), Chat.deleteChannel(), and Chat.deleteUser() accepted a soft parameter for soft-deletion. In 1.0.0, the soft parameter is removed — all deletions are permanent. Return types are simplified to PNFuture<Unit>.
- 0.x.x
- 1.0.0
1channel.delete(soft = true).async { result ->
2 result.onSuccess { deletedChannel ->
3 println("Channel soft-deleted: ${deletedChannel.id}")
4 }
5}
1channel.delete().async { result ->
2 result.onSuccess {
3 println("Channel deleted.")
4 }
5}
Message soft-delete
You can still soft-delete messages by calling Message.delete(soft).
Membership.update() signature expanded
In 0.x.x, Membership.update() accepted only custom. In 1.0.0, it also accepts status and type.
- 0.x.x
- 1.0.0
1membership.update(custom = mapOf("role" to "admin")).async { result -> }
1membership.update(
2 status = "active",
3 type = "admin",
4 custom = mapOf("role" to "admin")
5).async { result -> }
Custom events
chat.emitEvent() and chat.listenForEvents() are deprecated. Use channel.emitCustomEvent() to emit a custom event and channel.onCustomEvent() to listen for one. Pass the payload directly as a map; the old EventContent.Custom wrapper type is deprecated.
- 0.x.x
- 1.0.0
1// Emit
2chat.emitEvent(
3 channelId = "my-channel",
4 payload = EventContent.Custom(
5 data = mapOf("action" to "ping"),
6 method = EmitEventMethod.PUBLISH
7 )
8).async { result -> }
9
10// Listen
11val stop: AutoCloseable = chat.listenForEvents<EventContent.Custom>(
12 channelId = "my-channel"
13) { event ->
14 val data = (event.payload as? EventContent.Custom)?.data
15 println("Custom event: $data")
show all 16 lines1// Emit — pass payload directly
2channel.emitCustomEvent(
3 payload = mapOf("action" to "ping")
4).async { result -> }
5
6// Listen — typed callback on the channel
7val stop: AutoCloseable = channel.onCustomEvent { event ->
8 println("Custom event: ${event.payload}")
9}
GetCurrentUserMentionsResult
Important change
This change affects any code that reads GetCurrentUserMentionsResult. Update it before releasing to users.
The enhancedMentionsData field on GetCurrentUserMentionsResult is renamed to mentions. Its element type changes from the union of UserMentionData, ChannelMentionData, and ThreadMentionData to a single UserMention type that covers all mention contexts.
- 0.x.x
- 1.0.0
1chat.getCurrentUserMentions().async { result ->
2 result.onSuccess { mentionsResult ->
3 mentionsResult.enhancedMentionsData.forEach { mentionData ->
4 when (mentionData) {
5 is UserMentionData -> println("User mention in: ${mentionData.channelId}")
6 is ChannelMentionData -> println("Channel mention: ${mentionData.channelId}")
7 is ThreadMentionData -> println("Thread mention in: ${mentionData.parentChannelId}")
8 }
9 }
10 }
11}
1chat.getCurrentUserMentions().async { result ->
2 result.onSuccess { mentionsResult ->
3 mentionsResult.mentions.forEach { userMention ->
4 println("Mention in channel: ${userMention.channelId}")
5 println("By: ${userMention.userId}")
6 println("Message timetoken: ${userMention.message.timetoken}")
7 if (userMention.parentChannelId != null) {
8 println("Thread in: ${userMention.parentChannelId}")
9 }
10 }
11 }
12}
New capabilities in 1.0.0
The following features are new in 1.0.0 and have no counterpart in 0.x.x.
| Feature | Description |
|---|---|
channel.fetchReadReceipts() | Returns a paginated ReadReceiptsResponse with the last-read timetoken for each channel member. Use for initial load or on-demand checks. |
channel.hasMember(userId) | Returns PNFuture<Boolean> — checks whether a user is a member of the channel without fetching the full membership list. |
channel.getMember(userId) | Returns PNFuture<Membership?> — retrieves a specific member's membership object directly from the channel. |
user.isMemberOf(channelId) | Returns PNFuture<Boolean> — checks whether the user is a member of the given channel. |
user.getMembership(channelId) | Returns PNFuture<Membership?> — retrieves the user's membership for a specific channel. |
Membership.onUpdated() | Fires when membership metadata changes. Returns AutoCloseable. |
Membership.onDeleted() | Fires when the membership is deleted. Returns AutoCloseable. |
channel.emitCustomEvent() | Emits a custom event directly on the channel. Replaces chat.emitEvent(). |
channel.onCustomEvent() | Subscribes to custom events on the channel. Replaces chat.listenForEvents<EventContent.Custom>(). |
user.onMentioned() | Subscribes to mention events for the user. Delivers typed Mention objects. |
user.onInvited() | Subscribes to invite events for the user. Delivers typed Invite objects. |
user.onRestrictionChanged() | Subscribes to moderation events for the user. Delivers typed Restriction objects. |
emitReadReceiptEvents config | ChatConfiguration.emitReadReceiptEvents (Map<ChannelType, Boolean>) controls which channel types emit read receipt signals. |
ChannelGroup.onMessageReceived() | Subscribes to incoming messages on all channels in the group. |
ChannelGroup.onPresenceChanged() | Subscribes to presence events across all channels in the group. |
Custom interface implementations
If your application implements or extends Chat SDK interfaces (such as Channel, User, Message, or Membership), 1.0.0 adds new required methods to these interfaces. Compilation will fail until you implement the new methods.
Key additions include entity-owned callback methods (onUpdated(), onDeleted(), onMessageReceived(), etc.) and new membership/presence methods (hasMember(), getMember(), isMemberOf(), getMembership()).
Review the entity documentation for each interface your code extends to identify the new required members:
Migration steps
To migrate from Kotlin Chat SDK 0.x.x to 1.0.0:
- Replace every
channel.join(custom, callback)call. Remove the callback parameter fromjoin(). Addchannel.onMessageReceived { message -> }after a successful join to restore message delivery. Update your code to useMembershipdirectly from thejoin()result —JoinResultno longer exists. - Rename streaming methods on
Channel:connect()→onMessageReceived(),getTyping()→onTypingChanged(),streamPresence()→onPresenceChanged(). - Replace
channel.streamUpdates()withchannel.onUpdated(). Addchannel.onDeleted()wherever you previously detected deletion via thestreamUpdates()callback. Note:streamUpdatesOn()is still supported and does not need to be replaced. - Apply the same
onUpdated()/onDeleted()pattern toUser,Membership, andMessage. - Replace
channel.streamReadReceipts()withchannel.onReadReceiptReceived(). Update the handler to useReadReceipt(fields:userId,lastReadTimetoken) instead of the oldMap<Long, List<String>>type. - Replace
channel.streamMessageReports()withchannel.onMessageReported()and update the handler to use the typedReportstruct. - Replace
chat.listenForEvents<EventContent.Mention>()withuser.onMentioned()and update the handler to use theMentiontype. - Replace
chat.listenForEvents<EventContent.Invite>()withuser.onInvited()and update the handler to use theInvitetype. - Replace
chat.listenForEvents<EventContent.Moderation>()withuser.onRestrictionChanged()and update the handler to use theRestrictiontype. - Update all code that reads
Message.reactions. ReplaceMap<String, List<Action>>iteration withList<MessageReaction>iteration. Accessreaction.value,reaction.count,reaction.isMine, andreaction.userIdsdirectly. Remove any references toMessage.actions. - Replace
sendText()calls that pass only publishing options with the newsendText(text, params: SendTextParams)overload. Rich-content features likequotedMessage,mentionedUsers, andfilesrequireMessageDraftor the deprecated multi-parameter overload. - Replace
chat.emitEvent()calls withchannel.emitCustomEvent(). Replacechat.listenForEvents<EventContent.Custom>()calls withchannel.onCustomEvent(). Remove all references toEventContent.*types. - Replace
mentionsResult.enhancedMentionsDatawithmentionsResult.mentionsin any code that callschat.getCurrentUserMentions(). Update element handling to useUserMention(fields:message,userId,channelId,parentChannelId). Note thatmessageis aMessageobject, not a timetoken.enhancedMentionsDatais deprecated but still available. - Review
ChatConfigurationinitialization and addemitReadReceiptEventsif you need non-default read receipt emission behavior. - Remove the
softparameter from allChannel.delete(),User.delete(),Chat.deleteChannel(), andChat.deleteUser()calls. Update return-type handling from the old result type toUnit. - Update
Membership.update()calls to use the expanded signature (status,type,custom). - Update any code that destructures
Message.removeThread()results — the method now returnsUnit. - If you implement Chat SDK interfaces, add the new required methods to your implementations.
If you encounter issues during migration, contact PubNub Support.