---
title: "Chat Channel"
canonical_url: "https://apidocs.sportradar.com/resources/virtual-stadium/docs/sdk/chatChannel"
markdown_url: "https://apidocs.sportradar.com/resources/virtual-stadium/docs/sdk/chatChannel.md"
last_updated: "2026-04-16T10:19:11Z"
---

# Chat Channel

This guide covers chat channel management and messaging functionality in the Virtual Stadium Data SDK.

***

## Chat Manager

The `ChatManager` contains and handles all chat channels within your application.

## Key Functions

**`getOrAddChannel(channelConfig)`**

Creates a new channel if it doesn't exist, or returns the existing channel with the specified ID. Always returns a `ChatChannel` object.

**`removeChannel(channelId)`**

Removes a specific channel from the manager.

**`removeAllChannels()`**

Removes all channels from the manager.

***

## Insights Callbacks

When creating a chat channel, you can provide callbacks to receive new insights data. The SDK provides two callback options to accommodate platform differences:

**`onNewInsights`**

Regular callback function for iOS (iOS doesn't support suspend functions in lambdas).

**`onNewInsightsSuspended`**

Suspend callback function for Android (allows async operations).

The SDK automatically uses the appropriate callback based on what you provide. These callbacks receive a list of `OutcomePayloadRequest` objects containing new betting insights data.

***

## Chat Manager Implementation

Create channels with optional insights callbacks. The `ChannelConfig` allows you to specify channel ID, whether to fetch stats, and other configuration options.

Handle insights data in the callback to update your UI or process betting information.

**Android**

```kotlin
import ag.sportradar.virtualstadium.datasdk.model.ChannelConfig
import ag.sportradar.virtualstadium.datasdk.model.OutcomePayloadRequest
import ag.sportradar.virtualstadium.datasdk.services.ChatManager
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.get

class ChatManagerViewModel :
    ViewModel(),
    KoinComponent {

    private val chatManager: ChatManager
        get() = get()

    val chatChannels = chatManager.channels

    fun addChannelWithInsights(
        channelId: String, 
        getChannelStats: Boolean = true
    ) {
        val channelConfig = ChannelConfig(
            channelId = channelId,
            getChannelStats = getChannelStats
        )

        chatManager.getOrAddChannel(
            channelConfig = channelConfig,
            onNewInsightsSuspended = { insights ->
                // Handle new insights data (suspend function)
                processInsightsData(insights)
            }
        )
    }
}
```

**iOS**

```swift
import VirtualStadiumDataSDK

class ChatManagerViewModel: ObservableObject {
    
    private let chatManager = KoinHelper().getChatManager()
    @Published var channels: [String: ChatChannel] = [:]
    
    init() {
        subscribeToChannels()
    }
    
    func addChannelWithInsights(
        channelId: String, 
        getChannelStats: Bool = true
    ) {
        let channelConfig = ChannelConfig(
            channelId: channelId,
            getChannelStats: getChannelStats
        )

        let channel = chatManager.getOrAddChannel(
            channelConfig: channelConfig,
            onNewInsights: { [weak self] insights in
                // Handle new insights data (regular function)
                self?.handleNewInsights(insights: insights)
            },
            onNewInsightsSuspended: nil
        )
    }
    
    private func handleNewInsights(
        insights: [OutcomePayloadRequest]
    ) {
        // Process insights data on main thread
        DispatchQueue.main.async {
            // Update UI, process data, etc.
            self.processInsightsData(insights)
        }
    }
    
    private func subscribeToChannels() {
        chatManager.channels.subscribe(onCollect: { [weak self] channels in
            self?.channels = channels as? [String: ChatChannel] ?? [:]
        })
    }
}
```

***

## Chat Channel

The `ChatChannel` encapsulates all functionalities related to a chat channel within the application. It defines how chat channels behave and interact with other parts of the application.

The channel provides comprehensive state management, message handling, bet sharing, reactions, and lifecycle management through a well-defined interface.

***

## State Management

The `ChatChannel` maintains observable states that allow the UI to react and update based on changes in chat messages, bet shares, network connectivity, insights, and tag filters.

## Available States

**`MessagesState`**

Available as `state`, provides a robust, thread-safe, and reactive way to manage and observe chat messages within a channel, facilitating dynamic, real-time chat applications.

**`BetShareState`**

- **myBetShareListState** - Bets shared by the current user (personalized tracking)
- **betShareListState** - All bets shared within the chat channel (holistic view)

**`NetworkState`**

Available as `networkStatusState`, provides real-time information about network connectivity. Helps the application respond appropriately to network changes, such as displaying offline indicators or retrying failed operations.

**`InsightsState`**

Available as `insightsState`, manages match insights and predictions. Includes real-time updates about match statistics, betting odds, and other relevant information that enhances the chat experience.

**`filteredTagsState`**

Manages and observes tags that result from a search query using `filterTags(query: String)`. Updates dynamically to reflect tags matching the search query, allowing the application to display relevant tags based on user input.

***

## Message Handling

The `ChatChannel` facilitates sending, replying to, and managing messages. This includes message pagination, sending new messages, replying to specific messages, and requesting specific messages by ID.

## Key Functions

**`loadPreviousPage()`**

Fetches and appends a previous page of messages to the current list. Crucial for implementing pagination or infinite scrolling in chat applications. Updates the observable `MessagesState` with newly fetched messages at the beginning of the list.

**`sendMessage(message, onFailure)`**

Sends a message to the chat channel with immediate UI feedback. The message displays with a "Loading" status during transmission. Includes an `onFailure` callback for error handling, enabling graceful error management and user notifications.

**`replyToMessage(message, messageId, onFailure)`**

Sends a new message as a reply to an existing message. Particularly useful in group chat scenarios with multiple concurrent conversations. Includes an `onFailure` callback for error handling.

**`getMessage(messageId, onSuccess, onFailure)`**

Retrieves details of a specific message using its unique identifier. Useful when accessing full content or metadata not immediately available in local state. Includes both `onSuccess` and `onFailure` callbacks.

***

## Bet Sharing

Provides functionalities to share bets within the chat channel, access shared bets, and manage bet sharing activities. These functions collectively support a rich, interactive betting experience, allowing users to share, view, and manage bets in a social context.

## Key Functions

**`shareBet(betPayload, comment, showStake, onSuccess, onFailure)`**

Allows users to share a bet within the chat channel with an optional comment. Users can decide whether to show stake and payout. Enhances the interactive aspect of chat by integrating betting functionality directly into the conversation flow.

**`getAllBetShares()`**

Retrieves all bets shared within the channel. Displays a comprehensive view of betting activity to users.

**`loadMoreAllBetShares()`**

Loads additional pages of shared bets for pagination or infinite scrolling features.

**`getMyBetShares()`**

Fetches bets shared by the currently authenticated user, enabling personalized bet history tracking.

**`loadMoreMyBetShares()`**

Loads additional pages of the current user's shared bets for pagination.

**`copySharedBet(messageId, onSuccess, onFailure)`**

Copies a bet that has been shared in the channel with success/failure callbacks.

**`markBetSlipsAsAlreadyShared(betSlips, onSuccess, onFailure)`**

Marks specified bet slips as already shared to prevent duplicate sharing. Maintains integrity of shared bet data.

***

## Reaction Management

Allows users to react to messages with predefined reaction types from the backend and manage these reactions. These functions enhance the chat experience by enabling dynamic, interactive reactions to messages.

## Key Functions

**`reactToMessage(message, reactionType)`**

Adds a reaction to a message using predefined reaction types (thumbs up, heart, etc.). Enhances interactivity and engagement within the chat.

**`deleteReaction(message, reactionType)`**

Removes a previously added reaction from a message. Useful for correcting mistakes or changing reactions.

**`toggleReactionOnMessage(message, reactionType)`**

Intelligently handles both adding and removing reactions based on current state. If the user has already reacted with the specified type, the reaction is removed; otherwise, it's added. Simplifies reaction logic for developers.

***

## Message Reporting and Tags

Message Reporting

**`reportMessage(messageId, onSuccess, onFailure)`**

Reports inappropriate messages for moderation. Essential for community management and user safety. Provides immediate feedback via callbacks.

## Tags Management

The backend supplies a list of tags upon channel initialization in `MessagesState`. These enable displaying/highlighting tags in sent messages and incorporating them into new messages.

**`filterTags(query, cursorPosition)`**

Filters tags based on query string and cursor position. Updates `filteredTagsState` dynamically as users type, providing real-time search feedback.

**`sanitizeMessage(input)`**

Sanitizes user input according to allowed character ranges defined in chat settings. Maintains content quality and prevents security issues from malformed input.

**`clearTagFilter()`**

Clears the current tag filter, resetting `filteredTagsState` to show all available tags.

***

## Insights Management

Provides comprehensive insights management functionality that enhances user experience with real-time match data and betting information.

## Key Functions

**`markInsightAsSeen(insightId)`**

Marks a specific insight as seen by the user. Tracks user engagement with insights for prioritization.

**`selectInsightOdds(insightId)`**

Handles selection of odds from an insight. Called when users interact with betting odds displayed in insights.

**`updateInsights(outcomesPayload)`**

Updates insights data with new outcomes payload. Enables real-time updates ensuring users have access to latest information and betting odds.

***

## Channel Lifecycle Management

Manages the chat channel lifecycle including starting, stopping, restarting, resuming, and destroying channels. Ensures efficient resource allocation and cleanup.

The channel receives new messages by default, but receiving can be stopped and restarted.

## Key Functions

**`isDeltaMessageActive()`**

Checks if the channel is currently listening for new messages. Returns boolean indicating active status.

**`restart()`**

Resets the channel to a fresh state. Clears current state, fetches latest messages, and starts listening. Useful when refreshing the chat.

**`stop()`**

Stops listening for new messages. Conserves resources when chat is not in active use, preventing unnecessary data usage and battery drain.

**`resume()`**

Resumes channel operations after being stopped. Fetches latest messages, restarts listening, and resumes insights functionality. Useful when returning to foreground.

**`destroy()`**

Completely shuts down the chat channel. Cancels all internal jobs and resets state. Used when chat functionality is no longer needed (logout, app closure).

***

## Complete Implementation Example

This example demonstrates a complete chat implementation with all major features including messaging, reactions, bet sharing, tags, insights, and lifecycle management.

The ViewModel exposes all channel states and provides methods for every chat operation.

**Android**

```kotlin
import ag.sportradar.virtualstadium.datasdk.model.*
import ag.sportradar.virtualstadium.datasdk.services.*
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.*
import org.koin.core.component.KoinComponent
import org.koin.core.component.get

class ChatViewModel : ViewModel(), KoinComponent {
    private val chatManager: ChatManager get() = get()
    private val userProvider: UserProvider get() = get()
    private val settingsProvider: SettingsProvider get() = get()

    private var channel: ChatChannel? = null
    val settings = settingsProvider.settings

    fun initializeChannel(channelId: String) {
        channel = chatManager.getOrAddChannel(
            channelId = channelId,
            getChannelStats = true
        )
    }

    // Message operations
    fun loadPreviousPage() = channel?.loadPreviousPage()
    fun sendMessage(message: String) =
        channel?.sendMessage(message)
    fun replyToMessage(messageId: String, message: String) =
        channel?.replyToMessage(message, messageId)

    // Reaction operations
    fun toggleReactionOnMessage(reaction: ReactionType, message: Message) =
        channel?.toggleReactionOnMessage(message, reaction)
    fun addReaction(reaction: ReactionType, message: Message) =
        channel?.reactToMessage(message, reaction)

    // Moderation
    fun reportMessage(messageId: String, callback: (Boolean) -> Unit) {
        channel?.reportMessage(
            messageId = messageId,
            onSuccess = { callback(true) },
            onFailure = { callback(false) }
        )
    }

    // Bet sharing operations
    fun shareNewBet(
        betPayload: BetPayload,
        showStake: Boolean,
        comment: String
    ) {
        channel?.shareBet(
            betPayload = betPayload,
            comment = comment,
            showStake = showStake,
            onSuccess = { /* Handle success */ },
            onFailure = { /* Handle failure */ }
        )
    }

    fun getAllBetShares() = channel?.getAllBetShares()
    fun loadMoreBetShares() = channel?.loadMoreAllBetShares()
    fun getMyBetShares() = channel?.getMyBetShares()
    fun loadMoreMyBetShares() = channel?.loadMoreMyBetShares()
    fun copySharedBet(messageId: String) =
        channel?.copySharedBet(messageId)

    // Tags operations
    fun filterTags(query: String, cursorPosition: Int) =
        channel?.filterTags(query, cursorPosition)
    fun sanitizeMessage(input: String): String =
        channel?.sanitizeMessage(input) ?: input
    fun clearTagFilter() = channel.value?.clearTagFilter()

    // Insights operations
    fun markInsightAsSeen(insightId: String) =
        channel?.markInsightAsSeen(insightId)
    fun selectInsightOdds(insightId: String) =
        channel?.selectInsightOdds(insightId)
    fun updateInsights(outcomesPayload: OutcomesPayload) =
        channel?.updateInsights(outcomesPayload)

    // Lifecycle management
    fun stopChannel(channelId: String) {
        chatManager.removeChannel(channelId = channelId)
        channel = null
    }

    fun restartChannel() = channel?.restart()
}
```

**iOS**

```swift
import VirtualStadiumDataSDK

class ChatViewModel: ObservableObject {
    // Published states
    @Published var chatMessagesState: MessagesState?
    @Published var betShareListState: BetShareState?
    @Published var myBetShareListState: BetShareState?
    @Published var filteredTagsState: [Tag]?
    @Published var networkStatusState: NetworkState?
    @Published var insightsState: InsightsState?

    // Disposable handles
    private var disposables: [DisposableHandle] = []
    
    private let chatManager = KoinHelper().getChatManager()
    private var channel: ChatChannel?

    init(channelId: String) {
        channel = chatManager.getOrAddChannel(
            channelId: channelId, 
            getChannelStats: true
        )
        subscribeToStates()
    }

    // Message operations
    func loadPreviousPage() { channel?.loadPreviousPage() }
    
    func sendMessage(message: String) {
        channel?.sendMessage(message: message, onFailure: {
            print("Error sending message")
        })
    }
    
    func replyToMessage(messageId: String, message: String) {
        channel?.replyToMessage(
            message: message, 
            messageId: messageId, 
            onFailure: { print("Error replying") }
        )
    }

    // Reaction operations
    func toggleReactionOnMessage(
        reactionType: ReactionType, 
        message: Message
    ) {
        channel?.toggleReactionOnMessage(
            message: message, 
            reactionType: reactionType
        )
    }

    // Moderation
    func reportMessage(messageId: String) {
        channel?.reportMessage(
            messageId: messageId,
            onSuccess: { print("Message reported") },
            onFailure: { _ in print("Report failed") }
        )
    }

    // Bet sharing operations
    func shareNewBet(
        betPayload: BetPayload, 
        showStake: Bool, 
        comment: String
    ) {
        channel?.shareBet(
            betPayload: betPayload,
            comment: comment,
            showStake: showStake,
            onSuccess: { print("Bet shared") },
            onFailure: { _ in print("Share failed") }
        )
    }

    func getAllBetShares() { channel?.getAllBetShares() }
    func loadMoreBetShares() { channel?.loadMoreAllBetShares() }
    func getMyBetShares() { channel?.getMyBetShares() }
    func loadMoreMyBetShares() { channel?.loadMoreMyBetShares() }

    // Tags operations
    func filterTags(query: String, cursorPosition: Int32) {
        channel?.filterTags(
            message: query, 
            cursorPosition: cursorPosition
        )
    }
    
    func sanitizeMessage(input: String) -> String {
        channel?.sanitizeMessage(input: input) ?? input
    }
    
    func clearTagFilter() { channel?.clearTagFilter() }

    // Insights operations
    func markInsightAsSeen(insightId: String) {
        channel?.markInsightAsSeen(insightId: insightId)
    }
    
    func selectInsightOdds(insightId: String) {
        channel?.selectInsightOdds(insightId: insightId)
    }
    
    func updateInsights(outcomesPayload: OutcomesPayload) {
        channel?.updateInsights(outcomesPayload: outcomesPayload)
    }

    // Lifecycle management
    func stopChannel() {
        channel?.stop()
        disposeSubscriptions()
    }

    func restartChannel() {
        channel?.restart()
        subscribeToStates()
    }

    private func subscribeToStates() {
        disposables.append(
            channel?.state.subscribe { [weak self] state in
                self?.chatMessagesState = state
            } ?? DisposableHandle(dispose: {})
        )
        disposables.append(
            channel?.betShareListState.subscribe { [weak self] state in
                self?.betShareListState = state
            } ?? DisposableHandle(dispose: {})
        )
        disposables.append(
            channel?.myBetShareListState.subscribe { [weak self] state in
                self?.myBetShareListState = state
            } ?? DisposableHandle(dispose: {})
        )
        disposables.append(
            channel?.filteredTagsState.subscribe { [weak self] state in
                self?.filteredTagsState = state as? [Tag]
            } ?? DisposableHandle(dispose: {})
        )
        disposables.append(
            channel?.networkStatusState.subscribe { [weak self] state in
                self?.networkStatusState = state
            } ?? DisposableHandle(dispose: {})
        )
        disposables.append(
            channel?.insightsState.subscribe { [weak self] state in
                self?.insightsState = state
            } ?? DisposableHandle(dispose: {})
        )
    }

    private func disposeSubscriptions() {
        disposables.forEach { $0.dispose() }
        disposables.removeAll()
    }
}
```
