diff --git a/calls/android/actions.mdx b/calls/android/actions.mdx
index 663870464..fcd61a8f6 100644
--- a/calls/android/actions.mdx
+++ b/calls/android/actions.mdx
@@ -185,6 +185,48 @@ callSession.stopRecording();
+### Start Transcription
+
+*Available since v5.0.4*
+
+Begins server-side transcription of the call. This also enables the live closed captions overlay.
+
+
+
+```kotlin
+callSession.startTranscription()
+```
+
+
+```java
+callSession.startTranscription();
+```
+
+
+
+
+Transcription requires the feature to be enabled for your CometChat app.
+
+
+### Stop Transcription
+
+*Available since v5.0.4*
+
+Stops the current transcription and clears any captions currently on screen. The transcript is saved and can be retrieved with [`TranscriptRequest`](/calls/android/transcription#retrieving-transcripts).
+
+
+
+```kotlin
+callSession.stopTranscription()
+```
+
+
+```java
+callSession.stopTranscription();
+```
+
+
+
### Mute Participant
Mutes a specific participant's audio. This is a moderator action.
diff --git a/calls/android/overview.mdx b/calls/android/overview.mdx
index e038f09da..cad0cc66e 100644
--- a/calls/android/overview.mdx
+++ b/calls/android/overview.mdx
@@ -56,6 +56,10 @@ sequenceDiagram
Record call sessions for later playback
+
+ Transcribe calls, show live closed captions, and retrieve transcripts
+
+
Retrieve call history and details
diff --git a/calls/android/recording.mdx b/calls/android/recording.mdx
index 5fba04c4c..32745ed30 100644
--- a/calls/android/recording.mdx
+++ b/calls/android/recording.mdx
@@ -72,6 +72,13 @@ SessionSettings sessionSettings = new CometChatCalls.SessionSettingsBuilder()
+
+**A recording runs until it is stopped.** Whether it was started manually or automatically through `enableAutoStartRecording`, it keeps running until someone stops it. If nobody does, it stops on its own when either:
+
+- Everyone leaves the session — the recording ends about a minute later.
+- Everyone in the session stays muted for 10 minutes.
+
+
## Hide Recording Button
Hide the recording button from the default call UI:
diff --git a/calls/android/session-settings.mdx b/calls/android/session-settings.mdx
index 8235eccca..077716c7e 100644
--- a/calls/android/session-settings.mdx
+++ b/calls/android/session-settings.mdx
@@ -326,6 +326,56 @@ Automatically starts recording the session as soon as it begins. When enabled, r
|-----------|------|---------|
| `enabled` | boolean | false |
+### Auto Start Transcription
+
+*Available since v5.0.4*
+
+**Method:** `enableAutoStartTranscription(boolean)`
+
+Automatically starts transcribing the session as soon as it begins. See [Transcription](/calls/android/transcription) for details.
+
+
+
+```kotlin
+.enableAutoStartTranscription(true)
+```
+
+
+```java
+.enableAutoStartTranscription(true)
+```
+
+
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `enabled` | boolean | false |
+
+### Caption Language
+
+*Available since v5.0.4*
+
+**Method:** `setCaptionLanguage(String)`
+
+Sets the language used for transcription and closed captions. See [Transcription](/calls/android/transcription) for the full list of supported codes.
+
+
+
+```kotlin
+.setCaptionLanguage("en-US")
+```
+
+
+```java
+.setCaptionLanguage("en-US")
+```
+
+
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `captionLanguage` | String | "en-US" |
+
### Hide Control Panel
**Method:** `hideControlPanel(boolean)`
@@ -510,6 +560,56 @@ Hides the recording start/stop button from the control panel. Set to `false` to
|-----------|------|---------|
| `hide` | boolean | true |
+### Hide Transcription Button
+
+*Available since v5.0.4*
+
+**Method:** `hideTranscriptionButton(boolean)`
+
+Hides the transcription start/stop item from the control panel's **More** menu. Set to `false` to show it, allowing users to manually control session transcription.
+
+
+
+```kotlin
+.hideTranscriptionButton(false)
+```
+
+
+```java
+.hideTranscriptionButton(false)
+```
+
+
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `hide` | boolean | true |
+
+### Hide Closed Caption Button
+
+*Available since v5.0.4*
+
+**Method:** `hideClosedCaptionButton(boolean)`
+
+Hides the closed-caption (Show/Hide Captions) item from the control panel's **More** menu. Set to `false` to show it, allowing users to toggle the live captions overlay. Even when set to `false`, the item only appears while transcription is running.
+
+
+
+```kotlin
+.hideClosedCaptionButton(false)
+```
+
+
+```java
+.hideClosedCaptionButton(false)
+```
+
+
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `hide` | boolean | true |
+
### Hide Screen Sharing Button
**Method:** `hideScreenSharingButton(boolean)`
diff --git a/calls/android/setup.mdx b/calls/android/setup.mdx
index 90ca85ac9..ce6e04d95 100644
--- a/calls/android/setup.mdx
+++ b/calls/android/setup.mdx
@@ -59,14 +59,14 @@ Add the Calls SDK dependency to your **app level** `build.gradle` file:
```groovy
dependencies {
- implementation "com.cometchat:calls-sdk-android:5.0.0"
+ implementation "com.cometchat:calls-sdk-android:5.0.4"
}
```
```kotlin
dependencies {
- implementation("com.cometchat:calls-sdk-android:5.0.0")
+ implementation("com.cometchat:calls-sdk-android:5.0.4")
}
```
diff --git a/calls/android/transcription.mdx b/calls/android/transcription.mdx
new file mode 100644
index 000000000..a32634986
--- /dev/null
+++ b/calls/android/transcription.mdx
@@ -0,0 +1,508 @@
+---
+title: "Transcription & Closed Captions"
+sidebarTitle: "Transcription"
+sdk_version: "5.x"
+description: "Use CometChat Calls SDK v5 transcription on Android to transcribe calls, show live closed captions, and retrieve transcripts after the call."
+---
+
+
+**Available since v5.0.4** — transcription and closed captions require CometChat Calls SDK v5.0.4 or later for Android. See [Setup](/calls/android/setup) to install or upgrade.
+
+
+Transcribe call sessions in real time and display live closed captions on screen. Transcripts are stored server-side and can be retrieved after the call using `TranscriptRequest`.
+
+
+Transcription must be enabled for your CometChat app. Contact support if you need to enable this feature.
+
+
+## How It Works
+
+Transcription and closed captions are two related but separate things:
+
+| Concept | What it does |
+|---------|--------------|
+| **Transcription** | Server-side speech-to-text for the session. Starting it brings a transcriber into the call, which produces the transcript that is stored for later retrieval. |
+| **Closed captions** | The on-screen overlay that renders the live transcript as it arrives. Captions are produced from the running transcription, so they only appear while transcription is active. |
+
+Starting transcription is a prerequisite for captions — toggling captions on without an active transcription shows nothing.
+
+## Starting Transcription
+
+### Auto-Start Transcription
+
+Configure transcription to start automatically when the session begins:
+
+
+
+```kotlin
+val sessionSettings = CometChatCalls.SessionSettingsBuilder()
+ .enableAutoStartTranscription(true)
+ // ... other settings
+ .build()
+```
+
+
+```java
+SessionSettings sessionSettings = new CometChatCalls.SessionSettingsBuilder()
+ .enableAutoStartTranscription(true)
+ // ... other settings
+ .build();
+```
+
+
+
+**Default:** `false`
+
+### Manual Transcription Control
+
+Transcription can be started and stopped during an active call through the `CallSession` singleton.
+
+#### Start Transcription
+
+
+
+```kotlin
+val callSession = CallSession.getInstance()
+callSession.startTranscription()
+```
+
+
+```java
+CallSession callSession = CallSession.getInstance();
+callSession.startTranscription();
+```
+
+
+
+#### Stop Transcription
+
+Stops the current transcription. Any captions currently on screen are cleared:
+
+
+
+```kotlin
+val callSession = CallSession.getInstance()
+callSession.stopTranscription()
+```
+
+
+```java
+CallSession callSession = CallSession.getInstance();
+callSession.stopTranscription();
+```
+
+
+
+
+Always check `isSessionActive()` before calling these actions to ensure there's an active call.
+
+
+## Built-in UI Controls
+
+On Android, both transcription controls live in the control panel's **More** menu and are hidden by default.
+
+### Transcription Button
+
+To show the **Start Transcription** / **Stop Transcription** item:
+
+
+
+```kotlin
+val sessionSettings = CometChatCalls.SessionSettingsBuilder()
+ .hideTranscriptionButton(false)
+ .build()
+```
+
+
+```java
+SessionSettings sessionSettings = new CometChatCalls.SessionSettingsBuilder()
+ .hideTranscriptionButton(false)
+ .build();
+```
+
+
+
+**Default:** `true`
+
+The menu item toggles between **Start Transcription** and **Stop Transcription** based on the current state.
+
+### Closed Caption Button
+
+To show the **Show Captions** / **Hide Captions** item, which toggles the on-screen captions overlay:
+
+
+
+```kotlin
+val sessionSettings = CometChatCalls.SessionSettingsBuilder()
+ .hideClosedCaptionButton(false)
+ .build()
+```
+
+
+```java
+SessionSettings sessionSettings = new CometChatCalls.SessionSettingsBuilder()
+ .hideClosedCaptionButton(false)
+ .build();
+```
+
+
+
+**Default:** `true`
+
+
+Even with `hideClosedCaptionButton(false)`, the captions item only appears once transcription is running for the session, because captions are generated from the live transcript.
+
+
+## Caption Language
+
+**Method:** `setCaptionLanguage(String)`
+
+Sets the language used for transcription and captions. This is fixed for the session — there is no in-call language picker on Android.
+
+
+
+```kotlin
+val sessionSettings = CometChatCalls.SessionSettingsBuilder()
+ .setCaptionLanguage("en-US")
+ .build()
+```
+
+
+```java
+SessionSettings sessionSettings = new CometChatCalls.SessionSettingsBuilder()
+ .setCaptionLanguage("en-US")
+ .build();
+```
+
+
+
+**Default:** `en-US`
+
+
+| Code | Language |
+|------|----------|
+| `en-US` | English (United States) |
+| `de-DE` | German (Germany) |
+| `en-GB` | English (United Kingdom) |
+| `es-ES` | Spanish (Spain) |
+| `fr-FR` | French (France) |
+| `hi-IN` | Hindi (India) |
+| `hu-HU` | Hungarian (Hungary) |
+| `it-IT` | Italian (Italy) |
+| `ja-JP` | Japanese (Japan) |
+| `ko-KR` | Korean (South Korea) |
+| `lt-LT` | Lithuanian (Lithuania) |
+| `ms-MY` | Malay (Malaysia) |
+| `nl-NL` | Dutch (Netherlands) |
+| `pt-PT` | Portuguese (Portugal) |
+| `ru-RU` | Russian (Russia) |
+| `sv-SE` | Swedish (Sweden) |
+| `tr-TR` | Turkish (Turkey) |
+| `zh` | Chinese Mandarin (Simplified, China) |
+| `zh-TW` | Chinese Mandarin (Traditional, Taiwan) |
+
+
+## Retrieving Transcripts
+
+After a call, use `TranscriptRequest` to list the transcript artifacts for a session. Each record is a **pointer to a downloadable transcript file**, not the transcript text itself.
+
+
+The SDK must be initialized with `CometChatCalls.init()` and a user must be logged in. The auth token is read from the logged-in user at fetch time, so it automatically tracks re-logins.
+
+
+### Building a Request
+
+
+
+```kotlin
+val transcriptRequest = TranscriptRequest.TranscriptRequestBuilder()
+ .setSessionId("v1.us.2547167fe69871fd.alice") // required
+ .setLimit(10) // optional
+ .build()
+
+transcriptRequest.fetchNext(object : CometChatCalls.CallbackListener>() {
+ override fun onSuccess(transcripts: List) {
+ for (transcript in transcripts) {
+ Log.d(TAG, "Transcript ID: ${transcript.tid}")
+ Log.d(TAG, "Transcript URL: ${transcript.transcriptUrl}")
+ }
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e(TAG, "Error: ${e.code} ${e.message}")
+ }
+})
+```
+
+
+```java
+TranscriptRequest transcriptRequest = new TranscriptRequest.TranscriptRequestBuilder()
+ .setSessionId("v1.us.2547167fe69871fd.alice") // required
+ .setLimit(10) // optional
+ .build();
+
+transcriptRequest.fetchNext(new CometChatCalls.CallbackListener>() {
+ @Override
+ public void onSuccess(List transcripts) {
+ for (Transcript transcript : transcripts) {
+ Log.d(TAG, "Transcript ID: " + transcript.getTid());
+ Log.d(TAG, "Transcript URL: " + transcript.getTranscriptUrl());
+ }
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e(TAG, "Error: " + e.getCode() + " " + e.getMessage());
+ }
+});
+```
+
+
+
+| Method | Required | Description |
+|--------|----------|-------------|
+| `setSessionId(String)` | Yes | The session ID whose transcripts to fetch. A null or empty value is reported as `ERROR_INVALID_SESSIONID` on the first fetch. |
+| `setLimit(int)` | No | Page size. Defaults to `30` and is capped at `1000`. A value of `0` or less is reported as `ERROR_NON_POSITIVE_LIMIT` on the first fetch. |
+| `build()` | Yes | Returns a `TranscriptRequest`. Never throws — validation happens at fetch time. |
+
+### Paginating
+
+A `TranscriptRequest` is a stateful cursor. Create one per session ID and drive it with `fetchNext()` and `fetchPrevious()`:
+
+
+
+```kotlin
+// Fetch next page
+transcriptRequest.fetchNext(object : CometChatCalls.CallbackListener>() {
+ override fun onSuccess(transcripts: List) {
+ if (transcripts.isEmpty()) {
+ // No more pages
+ }
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e(TAG, "Error: ${e.message}")
+ }
+})
+
+// Fetch previous page
+transcriptRequest.fetchPrevious(object : CometChatCalls.CallbackListener>() {
+ override fun onSuccess(transcripts: List) {
+ // Handle previous page
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e(TAG, "Error: ${e.message}")
+ }
+})
+```
+
+
+```java
+// Fetch next page
+transcriptRequest.fetchNext(new CometChatCalls.CallbackListener>() {
+ @Override
+ public void onSuccess(List transcripts) {
+ if (transcripts.isEmpty()) {
+ // No more pages
+ }
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e(TAG, "Error: " + e.getMessage());
+ }
+});
+
+// Fetch previous page
+transcriptRequest.fetchPrevious(new CometChatCalls.CallbackListener>() {
+ @Override
+ public void onSuccess(List transcripts) {
+ // Handle previous page
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e(TAG, "Error: " + e.getMessage());
+ }
+});
+```
+
+
+
+- `fetchNext()` delivers the next page, or an empty list when there are no more pages. A session with no transcripts delivers an empty list.
+- `fetchPrevious()` delivers the previous page, or an empty list when already on the first page. It never requests a page below `1`.
+- Only one fetch may be in flight at a time. Calling `fetchNext()` or `fetchPrevious()` while another request is pending reports `ERROR_REQUEST_IN_PROGRESS`; the original call is unaffected and still completes.
+- Callbacks are always delivered on the main thread.
+
+### Transcript Object
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `tid` | String | Transcript ID |
+| `mid` | String | Meeting ID |
+| `roomName` | String | Room name of the meeting |
+| `startTime` | long | Meeting start time, in epoch **seconds** |
+| `endTime` | long | Meeting end time, in epoch **seconds** |
+| `url` | String | Meeting URL |
+| `transcriptDate` | String | Transcript date |
+| `transcriptUrl` | String | URL of the downloadable transcript JSON |
+| `metaData` | JSONObject | The full raw record as returned by the server, so any fields not modelled above are still available |
+
+
+Every property is optional. The server omits keys whose value is empty, so String getters may return `null` and numeric getters `0`. Sparse records are normal and should not be treated as an error.
+
+
+### Reading the Transcript Content
+
+`transcriptUrl` points at the transcript file. Download it with your HTTP client of choice to read the actual utterances:
+
+
+
+```kotlin
+transcriptRequest.fetchNext(object : CometChatCalls.CallbackListener>() {
+ override fun onSuccess(transcripts: List) {
+ val transcriptUrl = transcripts.firstOrNull()?.transcriptUrl
+ if (transcriptUrl != null) {
+ // Download the JSON at transcriptUrl using your HTTP client
+ }
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e(TAG, "Error: ${e.message}")
+ }
+})
+```
+
+
+```java
+transcriptRequest.fetchNext(new CometChatCalls.CallbackListener>() {
+ @Override
+ public void onSuccess(List transcripts) {
+ if (!transcripts.isEmpty() && transcripts.get(0).getTranscriptUrl() != null) {
+ String transcriptUrl = transcripts.get(0).getTranscriptUrl();
+ // Download the JSON at transcriptUrl using your HTTP client
+ }
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e(TAG, "Error: " + e.getMessage());
+ }
+});
+```
+
+
+
+### Error Handling
+
+All errors — including pre-flight validation — are delivered to `onError()` with a `CometChatException` carrying a `code`. `build()` never throws.
+
+| Condition | Code |
+|-----------|------|
+| `init()` was not called | `ERROR_COMETCHAT_CALLS_SDK_INIT` |
+| No logged-in user (no auth token) | `ERROR_AUTH_TOKEN` |
+| `sessionId` missing or empty | `ERROR_INVALID_SESSIONID` |
+| `limit` is `0` or negative | `ERROR_NON_POSITIVE_LIMIT` |
+| A fetch is already in flight | `ERROR_REQUEST_IN_PROGRESS` |
+| Missing or malformed response | `ERROR_JSON_EXCEPTION` |
+| Network failure or server-side API error | The network or server's own code (e.g. `AUTH_ERR_EMPTY_APPID`) |
+
+## Complete Example
+
+
+
+```kotlin
+// 1. Join a session with transcription enabled
+val sessionSettings = CometChatCalls.SessionSettingsBuilder()
+ .setType(SessionType.VIDEO)
+ .enableAutoStartTranscription(true)
+ .hideTranscriptionButton(false)
+ .hideClosedCaptionButton(false)
+ .setCaptionLanguage("en-US")
+ .build()
+
+CometChatCalls.joinSession(callToken, sessionSettings, callViewContainer,
+ object : CometChatCalls.CallbackListener() {
+ override fun onSuccess(callSession: CallSession) {
+ // 2. Control transcription during the call
+ callSession.startTranscription()
+ callSession.stopTranscription()
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e(TAG, "Error: ${e.message}")
+ }
+})
+
+// 3. Retrieve transcripts after the call
+val transcriptRequest = TranscriptRequest.TranscriptRequestBuilder()
+ .setSessionId(sessionId)
+ .setLimit(10)
+ .build()
+
+transcriptRequest.fetchNext(object : CometChatCalls.CallbackListener>() {
+ override fun onSuccess(transcripts: List) {
+ transcripts.forEach { Log.d(TAG, "Transcript URL: ${it.transcriptUrl}") }
+ }
+
+ override fun onError(e: CometChatException) {
+ Log.e(TAG, "Error: ${e.message}")
+ }
+})
+```
+
+
+```java
+// 1. Join a session with transcription enabled
+SessionSettings sessionSettings = new CometChatCalls.SessionSettingsBuilder()
+ .setType(SessionType.VIDEO)
+ .enableAutoStartTranscription(true)
+ .hideTranscriptionButton(false)
+ .hideClosedCaptionButton(false)
+ .setCaptionLanguage("en-US")
+ .build();
+
+CometChatCalls.joinSession(callToken, sessionSettings, callViewContainer,
+ new CometChatCalls.CallbackListener() {
+ @Override
+ public void onSuccess(CallSession callSession) {
+ // 2. Control transcription during the call
+ callSession.startTranscription();
+ callSession.stopTranscription();
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e(TAG, "Error: " + e.getMessage());
+ }
+});
+
+// 3. Retrieve transcripts after the call
+TranscriptRequest transcriptRequest = new TranscriptRequest.TranscriptRequestBuilder()
+ .setSessionId(sessionId)
+ .setLimit(10)
+ .build();
+
+transcriptRequest.fetchNext(new CometChatCalls.CallbackListener>() {
+ @Override
+ public void onSuccess(List transcripts) {
+ for (Transcript transcript : transcripts) {
+ Log.d(TAG, "Transcript URL: " + transcript.getTranscriptUrl());
+ }
+ }
+
+ @Override
+ public void onError(CometChatException e) {
+ Log.e(TAG, "Error: " + e.getMessage());
+ }
+});
+```
+
+
+
+## Related Documentation
+
+- [Session Settings](/calls/android/session-settings)
+- [Actions](/calls/android/actions)
+- [Call Logs](/calls/android/call-logs)
diff --git a/calls/flutter/actions.mdx b/calls/flutter/actions.mdx
index 71411aefa..6a4776445 100644
--- a/calls/flutter/actions.mdx
+++ b/calls/flutter/actions.mdx
@@ -99,6 +99,30 @@ Stops the current recording. The recording is saved and accessible via the dashb
await CallSession.getInstance()?.stopRecording();
```
+### Start Transcription
+
+*Available since v5.0.7*
+
+Begins server-side transcription of the call. This also enables the live closed captions overlay.
+
+```dart
+await CallSession.getInstance()?.startTranscription();
+```
+
+
+Transcription requires the feature to be enabled for your CometChat app.
+
+
+### Stop Transcription
+
+*Available since v5.0.7*
+
+Stops the current transcription and clears any captions currently on screen. The transcript is saved and can be retrieved with [`TranscriptRequestBuilder`](/calls/flutter/transcription#retrieving-transcripts).
+
+```dart
+await CallSession.getInstance()?.stopTranscription();
+```
+
### Mute Participant
Mutes a specific participant's audio. This is a moderator action.
@@ -327,6 +351,7 @@ Read-only properties on `CallSession` that return the current state of the local
| `isVideoPaused` | `bool` | Whether local video is currently paused |
| `isHandRaised` | `bool` | Whether the local user's hand is raised |
| `isRecording` | `bool` | Whether the call is currently being recorded |
+| `isTranscribing` | `bool` | Whether the call is currently being transcribed |
```dart
CallSession? session = CallSession.getInstance();
@@ -335,6 +360,7 @@ bool? muted = session?.isAudioMuted;
bool? videoPaused = session?.isVideoPaused;
bool? handRaised = session?.isHandRaised;
bool? recording = session?.isRecording;
+bool? transcribing = session?.isTranscribing;
```
diff --git a/calls/flutter/call-logs.mdx b/calls/flutter/call-logs.mdx
index e5a202dcd..145c37098 100644
--- a/calls/flutter/call-logs.mdx
+++ b/calls/flutter/call-logs.mdx
@@ -40,6 +40,7 @@ Configure the request using the builder methods:
| `setSessionType(String)` | String | Filter by call type: `video` or `audio` |
| `setCallStatus(String)` | String | Filter by call status |
| `setHasRecording(bool)` | bool | Filter calls that have recordings |
+| `setHasTranscriptions(bool)` | bool | Filter calls that have transcripts, and attach them to each log |
| `setCallCategory(String)` | String | Filter by category: `call` or `meet` |
| `setCallDirection(String)` | String | Filter by direction: `incoming` or `outgoing` |
| `setUid(String)` | String | Filter calls with a specific user |
@@ -119,6 +120,7 @@ Each `CallLog` object contains detailed information about a call:
| `totalParticipants` | int | Number of participants |
| `hasRecording` | bool | Whether the call was recorded |
| `recordings` | List\ | List of recording objects |
+| `transcriptions` | List\? | List of transcript objects; null unless the request opted in |
| `participantInfoList` | List\ | List of participant details |
## Access Recordings
@@ -144,6 +146,35 @@ callLogRequest.fetchNext(
);
```
+## Access Transcripts
+
+*Available since v5.0.7*
+
+Use `setHasTranscriptions(true)` to fetch only calls that were transcribed. Opting in also makes the server attach each call's transcripts to the log:
+
+```dart
+CallLogRequest transcribedCallsRequest = CallLogRequest.CallLogRequestBuilder()
+ .setLimit(30)
+ .setHasTranscriptions(true)
+ .build();
+
+transcribedCallsRequest.fetchNext(
+ onSuccess: (List callLogs) {
+ for (CallLog callLog in callLogs) {
+ for (final transcript in callLog.getTranscriptions()) {
+ debugPrint("Transcript ID: ${transcript.tid}");
+ debugPrint("Transcript URL: ${transcript.transcriptUrl}");
+ }
+ }
+ },
+ onError: (CometChatCallsException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+`getTranscriptions()` returns an empty list when the server omitted transcripts, so it never needs a null check. Leaving the filter off sends no filter at all, so the list comes back unfiltered. To page through a single session's transcripts directly, use [`TranscriptRequestBuilder`](/calls/flutter/transcription#retrieving-transcripts).
+
| Status | Description |
|--------|-------------|
diff --git a/calls/flutter/migration-guide-v5.mdx b/calls/flutter/migration-guide-v5.mdx
index 7168487fb..4ae34c2f4 100644
--- a/calls/flutter/migration-guide-v5.mdx
+++ b/calls/flutter/migration-guide-v5.mdx
@@ -13,7 +13,7 @@ Calls SDK v5 is a **drop-in replacement** for v4. All v4 APIs are preserved as d
```yaml
dependencies:
- cometchat_calls_sdk: ^5.0.3
+ cometchat_calls_sdk: ^5.0.7
```
diff --git a/calls/flutter/recording.mdx b/calls/flutter/recording.mdx
index 735e72bfc..9f8c426ed 100644
--- a/calls/flutter/recording.mdx
+++ b/calls/flutter/recording.mdx
@@ -39,6 +39,13 @@ SessionSettings sessionSettings = CometChatCalls.SessionSettingsBuilder()
.build();
```
+
+**A recording runs until it is stopped.** Whether it was started manually or automatically through `enableAutoStartRecording`, it keeps running until someone stops it. If nobody does, it stops on its own when either:
+
+- Everyone leaves the session — the recording ends about a minute later.
+- Everyone in the session stays muted for 10 minutes.
+
+
## Hide Recording Button
Hide the recording button from the default call UI:
diff --git a/calls/flutter/session-settings.mdx b/calls/flutter/session-settings.mdx
index 04f7361b8..1636ba3f6 100644
--- a/calls/flutter/session-settings.mdx
+++ b/calls/flutter/session-settings.mdx
@@ -214,6 +214,38 @@ Automatically starts recording the session as soon as it begins. When enabled, r
|-----------|------|---------|
| `enabled` | bool | false |
+### Auto Start Transcription
+
+*Available since v5.0.7*
+
+**Method:** `enableAutoStartTranscription(bool)`
+
+Automatically starts transcribing the session as soon as it begins, without requiring anyone to press the transcription button. See [Transcription](/calls/flutter/transcription) for details.
+
+```dart
+..enableAutoStartTranscription(true)
+```
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `enabled` | bool | false |
+
+### Caption Language
+
+*Available since v5.0.7*
+
+**Method:** `setCaptionLanguage(String)`
+
+Sets the language used for transcription and closed captions. See [Transcription](/calls/flutter/transcription#caption-language) for the full list of supported codes.
+
+```dart
+..setCaptionLanguage("en-US")
+```
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `captionLanguage` | String | en-US |
+
### Hide Control Panel
**Method:** `hideControlPanel(bool)`
@@ -326,6 +358,38 @@ Hides the recording start/stop button from the control panel. Set to `false` to
|-----------|------|---------|
| `hide` | bool | true |
+### Hide Transcription Button
+
+*Available since v5.0.7*
+
+**Method:** `hideTranscriptionButton(bool)`
+
+Hides the transcription start/stop item from the control panel's **More** menu. Set to `false` to show it, allowing users to manually control session transcription.
+
+```dart
+..hideTranscriptionButton(false)
+```
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `hide` | bool | true |
+
+### Hide Closed Caption Button
+
+*Available since v5.0.7*
+
+**Method:** `hideClosedCaptionButton(bool)`
+
+Hides the closed-caption (CC) button from the control panel. Even when set to `false`, the button only appears while transcription is running, since captions are generated from the live transcript.
+
+```dart
+..hideClosedCaptionButton(false)
+```
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `hide` | bool | true |
+
### Hide Screen Sharing Button
**Method:** `hideScreenSharingButton(bool)`
diff --git a/calls/flutter/setup.mdx b/calls/flutter/setup.mdx
index 63e0e3d38..5774a33d0 100644
--- a/calls/flutter/setup.mdx
+++ b/calls/flutter/setup.mdx
@@ -28,7 +28,7 @@ Add the `cometchat_calls_sdk` dependency to your `pubspec.yaml` file:
```yaml
dependencies:
- cometchat_calls_sdk: ^5.0.3
+ cometchat_calls_sdk: ^5.0.7
```
### Step 2: Install Dependencies
diff --git a/calls/flutter/transcription.mdx b/calls/flutter/transcription.mdx
new file mode 100644
index 000000000..cfd02a4ac
--- /dev/null
+++ b/calls/flutter/transcription.mdx
@@ -0,0 +1,342 @@
+---
+title: "Transcription & Closed Captions"
+sidebarTitle: "Transcription"
+sdk_version: "5.x"
+description: "Use CometChat Calls SDK v5 transcription on Flutter to transcribe calls, show live closed captions, and retrieve transcripts after the call."
+---
+
+
+**Available since v5.0.7** — transcription and closed captions require CometChat Calls SDK v5.0.7 or later for Flutter. See [Setup](/calls/flutter/setup) to install or upgrade.
+
+
+Transcribe call sessions in real time and display live closed captions on screen. Transcripts are stored server-side and can be retrieved after the call using `TranscriptRequestBuilder`.
+
+
+Transcription must be enabled for your CometChat app. Contact support if you need to enable this feature.
+
+
+## How It Works
+
+Transcription and closed captions are two related but separate things:
+
+| Concept | What it does |
+|---------|--------------|
+| **Transcription** | Server-side speech-to-text for the session. Starting it brings a transcriber into the call, which produces the transcript that is stored for later retrieval. |
+| **Closed captions** | The on-screen overlay that renders the live transcript as it arrives. Captions are produced from the running transcription, so they only appear while transcription is active. |
+
+Starting transcription is a prerequisite for captions — toggling captions on without an active transcription shows nothing.
+
+## Starting Transcription
+
+### Auto-Start Transcription
+
+Configure transcription to start automatically when the session begins:
+
+```dart
+final sessionSettings = (SessionSettingsBuilder()
+ ..enableAutoStartTranscription(true))
+ .build();
+```
+
+**Default:** `false`
+
+### Manual Transcription Control
+
+#### Start Transcription
+
+Begin transcribing during an active call:
+
+```dart
+await CallSession.getInstance()?.startTranscription();
+```
+
+#### Stop Transcription
+
+Stop the current transcription. Any captions currently on screen are cleared:
+
+```dart
+await CallSession.getInstance()?.stopTranscription();
+```
+
+#### Check Transcription State
+
+`CallSession` exposes the local transcription state so you can drive a custom control:
+
+```dart
+final isTranscribing = CallSession.getInstance()?.isTranscribing ?? false;
+```
+
+Both actions throw a `CometChatCallsException` if the underlying call fails — `ERROR_START_TRANSCRIPTION` and `ERROR_STOP_TRANSCRIPTION` respectively:
+
+```dart
+try {
+ await CallSession.getInstance()?.startTranscription();
+} on CometChatCallsException catch (e) {
+ debugPrint("${e.code}: ${e.message}");
+}
+```
+
+## Built-in UI Controls
+
+### Transcription Button
+
+The transcription start/stop item in the control panel's **More** menu is hidden by default. To show it:
+
+```dart
+final sessionSettings = (SessionSettingsBuilder()
+ ..hideTranscriptionButton(false))
+ .build();
+```
+
+**Default:** `true`
+
+The menu item toggles between **Start Transcription** and **Stop Transcription** based on the current state.
+
+### Closed Caption Button
+
+The closed-caption (CC) button in the control panel is hidden by default. To show it:
+
+```dart
+final sessionSettings = (SessionSettingsBuilder()
+ ..hideClosedCaptionButton(false))
+ .build();
+```
+
+**Default:** `true`
+
+
+Even with `hideClosedCaptionButton(false)`, the CC button only appears once transcription is running for the session, because captions are generated from the live transcript.
+
+
+### Closed Caption Settings
+
+When the CC button is visible, the settings dialog gains a **Closed Caption** tab where the user can pick the caption language and enable or disable the on-screen captions. The gear icon on the captions overlay opens the dialog directly on that tab.
+
+## Caption Language
+
+**Method:** `setCaptionLanguage(String)`
+
+Sets the language used for transcription and captions.
+
+```dart
+final sessionSettings = (SessionSettingsBuilder()
+ ..setCaptionLanguage("en-US"))
+ .build();
+```
+
+**Default:** `en-US`
+
+
+| Code | Language |
+|------|----------|
+| `en-US` | English (United States) |
+| `de-DE` | German (Germany) |
+| `en-GB` | English (United Kingdom) |
+| `es-ES` | Spanish (Spain) |
+| `fr-FR` | French (France) |
+| `hi-IN` | Hindi (India) |
+| `hu-HU` | Hungarian (Hungary) |
+| `it-IT` | Italian (Italy) |
+| `ja-JP` | Japanese (Japan) |
+| `ko-KR` | Korean (South Korea) |
+| `lt-LT` | Lithuanian (Lithuania) |
+| `ms-MY` | Malay (Malaysia) |
+| `nl-NL` | Dutch (Netherlands) |
+| `pt-PT` | Portuguese (Portugal) |
+| `ru-RU` | Russian (Russia) |
+| `sv-SE` | Swedish (Sweden) |
+| `tr-TR` | Turkish (Turkey) |
+| `zh` | Chinese Mandarin (Simplified, China) |
+| `zh-TW` | Chinese Mandarin (Traditional, Taiwan) |
+
+
+## Retrieving Transcripts
+
+After a call, use `TranscriptRequestBuilder` to list the transcript artifacts for a session. Each record is a **pointer to a downloadable transcript file**, not the transcript text itself.
+
+
+The SDK must be initialized with `CometChatCalls.init()` and a user must be logged in. The auth token is read from the logged-in user at fetch time, so it automatically tracks re-logins — there is no auth token setter on the builder.
+
+
+### Building a Request
+
+```dart
+final request = (TranscriptRequestBuilder()
+ ..setSessionId("v1.us.2547167fe69871fd.alice") // required
+ ..setLimit(10)) // optional
+ .build();
+
+request.fetchNext(
+ onSuccess: (List transcripts) {
+ for (final transcript in transcripts) {
+ debugPrint("Transcript URL: ${transcript.transcriptUrl}");
+ }
+ },
+ onError: (CometChatCallsException e) {
+ debugPrint("Error: ${e.code} ${e.message}");
+ },
+);
+```
+
+| Method | Required | Description |
+|--------|----------|-------------|
+| `setSessionId(String)` | Yes | The session ID whose transcripts to fetch. A missing or blank value is reported to `onError` as `ERR_SESSION_ID_EMPTY`. |
+| `setLimit(int)` | No | Page size. Defaults to `30` and is clamped to a maximum of `1000`. A non-positive value is reported as `ERROR_NON_POSITIVE_LIMIT`. |
+| `build()` | Yes | Returns a `TranscriptRequest`. It never throws — all validation happens at fetch time and is delivered to `onError`. |
+
+
+Both builder methods have equivalent public fields, so `TranscriptRequestBuilder()..sessionId = "..."` works too.
+
+
+### Paginating
+
+A `TranscriptRequest` is a stateful cursor. Create one per session ID and drive it with `fetchNext()` and `fetchPrevious()`. Both also return a `Future` that resolves with the same page handed to `onSuccess`, so you can `await` them instead of nesting callbacks:
+
+```dart
+final request = (TranscriptRequestBuilder()
+ ..setSessionId(sessionId)
+ ..setLimit(10))
+ .build();
+
+var page = await request.fetchNext(
+ onSuccess: (transcripts) {},
+ onError: (e) => debugPrint("Error: ${e.message}"),
+);
+
+while (page.isNotEmpty) {
+ for (final transcript in page) {
+ debugPrint(transcript.transcriptUrl ?? "");
+ }
+ page = await request.fetchNext(
+ onSuccess: (transcripts) {},
+ onError: (e) => debugPrint("Error: ${e.message}"),
+ );
+}
+```
+
+- `fetchNext()` delivers the next page, or an empty list once the last page has been reached. A session with no transcripts delivers an empty list on the first call.
+- `fetchPrevious()` delivers the previous page, or an empty list when already on the first page. It never requests a page below `1`.
+- Only one fetch may be in flight at a time. Calling `fetchNext()` or `fetchPrevious()` while another request is pending reports `ERROR_REQUEST_IN_PROGRESS` to `onError`; the original call is unaffected and still completes.
+- The returned `Future` never completes with an error — failures always arrive through `onError` and the future resolves with an empty list, so a caller that does not `await` can never trip an unhandled async exception.
+
+### Transcript Properties
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `tid` | String? | Transcript ID |
+| `mid` | String? | Meeting ID |
+| `roomName` | String? | Room name of the meeting |
+| `startTime` | int? | Meeting start time, in epoch **seconds** |
+| `endTime` | int? | Meeting end time, in epoch **seconds** |
+| `url` | String? | Meeting URL |
+| `transcriptDate` | String? | Transcript date |
+| `transcriptUrl` | String? | URL of the downloadable transcript JSON |
+| `metaData` | Map\? | The raw server record, so new or unknown keys are never lost |
+
+
+Every property is optional. The server omits keys whose value is empty, so sparse records are normal and should not be treated as an error. A malformed entry within a page is skipped rather than failing the whole page.
+
+
+### Reading the Transcript Content
+
+`transcriptUrl` points at the transcript file. Fetch it yourself — with `package:http` or any client of your choice — to read the actual utterances:
+
+```dart
+request.fetchNext(
+ onSuccess: (List transcripts) async {
+ for (final transcript in transcripts) {
+ final url = transcript.transcriptUrl;
+ if (url == null) continue;
+
+ final response = await http.get(Uri.parse(url));
+ debugPrint(response.body);
+ }
+ },
+ onError: (CometChatCallsException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+### Error Handling
+
+Every failure — pre-flight validation and server errors alike — is delivered to `onError` as a `CometChatCallsException` carrying a `code`, `message` and `details`:
+
+| Condition | Code |
+|-----------|------|
+| `init()` was not called | `ERR_SDK_NOT_INITIALIZED` |
+| No logged-in user / auth token | `USER_AUTH_TOKEN_NULL` |
+| `sessionId` missing or blank | `ERR_SESSION_ID_EMPTY` |
+| `limit` is zero or negative | `ERROR_NON_POSITIVE_LIMIT` |
+| A fetch is already in flight | `ERROR_REQUEST_IN_PROGRESS` |
+| Missing or malformed response | `ERROR_JSON_EXCEPTION` |
+| Server-side API error | The server's own code |
+
+## Transcripts in Call Logs
+
+Call logs can be filtered to transcribed calls, which also attaches each call's transcripts to the log:
+
+```dart
+CallLogRequest callLogRequest = CallLogRequest.CallLogRequestBuilder()
+ .setLimit(30)
+ .setHasTranscriptions(true)
+ .build();
+
+callLogRequest.fetchNext(
+ onSuccess: (List callLogs) {
+ for (CallLog callLog in callLogs) {
+ for (final transcript in callLog.getTranscriptions()) {
+ debugPrint("${transcript.tid}: ${transcript.transcriptUrl}");
+ }
+ }
+ },
+ onError: (CometChatCallsException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+`getTranscriptions()` returns an empty list when the server omitted transcripts, so it never needs a null check. Leaving the filter off sends no filter at all, so the list comes back unfiltered exactly as if it had never been set.
+
+## Complete Example
+
+```dart
+// 1. Join a session with transcription enabled
+final sessionSettings = (SessionSettingsBuilder()
+ ..setType(SessionType.video)
+ ..enableAutoStartTranscription(true)
+ ..hideTranscriptionButton(false)
+ ..hideClosedCaptionButton(false)
+ ..setCaptionLanguage("en-US"))
+ .build();
+
+// Pass sessionSettings to the CometChatCallsView / joinSession() call.
+
+// 2. Control transcription during the call
+await CallSession.getInstance()?.startTranscription();
+await CallSession.getInstance()?.stopTranscription();
+
+// 3. Retrieve transcripts after the call
+final request = (TranscriptRequestBuilder()
+ ..setSessionId(sessionId)
+ ..setLimit(10))
+ .build();
+
+request.fetchNext(
+ onSuccess: (List transcripts) {
+ for (final transcript in transcripts) {
+ debugPrint(transcript.transcriptUrl ?? "");
+ }
+ },
+ onError: (CometChatCallsException e) {
+ debugPrint("Error: ${e.message}");
+ },
+);
+```
+
+## Related Documentation
+
+- [SessionSettingsBuilder](/calls/flutter/session-settings)
+- [Actions](/calls/flutter/actions)
+- [Call Logs](/calls/flutter/call-logs)
+- [Recording](/calls/flutter/recording)
diff --git a/calls/ios/actions.mdx b/calls/ios/actions.mdx
index 5d9a770f8..96819670d 100644
--- a/calls/ios/actions.mdx
+++ b/calls/ios/actions.mdx
@@ -185,6 +185,48 @@ CallSession.shared.stopRecording()
+### Start Transcription
+
+*Available since v5.0.4*
+
+Begins server-side transcription of the call. This also enables the live closed captions overlay.
+
+
+
+```swift
+CallSession.shared.startTranscription()
+```
+
+
+```objectivec
+[[CallSession shared] startTranscription];
+```
+
+
+
+
+Transcription requires the feature to be enabled for your CometChat app.
+
+
+### Stop Transcription
+
+*Available since v5.0.4*
+
+Stops the current transcription and clears any captions currently on screen. The transcript is saved and can be retrieved with [`TranscriptsRequest`](/calls/ios/transcription#retrieving-transcripts).
+
+
+
+```swift
+CallSession.shared.stopTranscription()
+```
+
+
+```objectivec
+[[CallSession shared] stopTranscription];
+```
+
+
+
### Mute Participant
Mutes a specific participant's audio. This is a moderator action.
@@ -192,12 +234,15 @@ Mutes a specific participant's audio. This is a moderator action.
```swift
-CallSession.shared.muteParticipant(participantId: participant.pid)
+guard let uid = participant.uid else { return }
+CallSession.shared.muteParticipant(participantId: uid)
```
```objectivec
-[[CallSession shared] muteParticipantWithParticipantId:participant.pid];
+if (participant.uid) {
+ [[CallSession shared] muteParticipantWithParticipantId:participant.uid];
+}
```
@@ -209,12 +254,15 @@ Pauses a specific participant's video. This is a moderator action.
```swift
-CallSession.shared.pauseParticipantVideo(participantId: participant.pid)
+guard let uid = participant.uid else { return }
+CallSession.shared.pauseParticipantVideo(participantId: uid)
```
```objectivec
-[[CallSession shared] pauseParticipantVideoWithParticipantId:participant.pid];
+if (participant.uid) {
+ [[CallSession shared] pauseParticipantVideoWithParticipantId:participant.uid];
+}
```
@@ -421,14 +469,23 @@ CallSession.shared.hideSettingsPanel()
| Property | Type | Description |
|----------|------|-------------|
-| `uid` | `String` | Unique identifier (CometChat user ID) |
-| `name` | `String` | Display name |
-| `avatar` | `String` | URL of avatar image |
-| `pid` | `String` | Participant ID for this call session |
-| `role` | `String` | Role in the call |
-| `audioMuted` | `Bool` | Whether audio is muted |
-| `videoPaused` | `Bool` | Whether video is paused |
-| `isPinned` | `Bool` | Whether pinned in layout |
-| `isPresenting` | `Bool` | Whether screen sharing |
-| `raisedHandTimestamp` | `Int` | Timestamp when hand was raised |
+| `uid` | `String?` | CometChat user ID — the identifier every moderator action takes |
+| `name` | `String?` | Display name |
+| `avatar` | `String?` | URL of avatar image |
+| `mid` | `String?` | Media ID for this call session |
+| `state` | `String?` | Participant state as reported by the server |
+| `isJoined` | `Bool?` | Whether the participant is currently joined |
+| `joinedAt` | `Int?` | Join timestamp |
+| `leftAt` | `Int?` | Leave timestamp |
+| `deviceID` | `String?` | Device identifier |
+| `totalAudioMinutes` | `Double?` | Audio minutes consumed |
+| `totalVideoMinutes` | `Double?` | Video minutes consumed |
+| `totalDurationInMinutes` | `Double?` | Total session minutes |
+
+
+ **Every property is Optional.** `Participant` carries no mute / video / pin / hand-raise /
+ screen-share flags, and the SDK exposes no getter for them — track that state in your own app
+ from the [participant events](/calls/ios/events). Because the events fire only on change, a
+ client that joins late cannot recover state that was already in effect.
+
diff --git a/calls/ios/audio-modes.mdx b/calls/ios/audio-modes.mdx
index 9f1d04c91..0ea423d5b 100644
--- a/calls/ios/audio-modes.mdx
+++ b/calls/ios/audio-modes.mdx
@@ -43,7 +43,7 @@ CometChatCalls.joinSession(
```objectivec
SessionSettings *sessionSettings = [[[CometChatCalls sessionSettingsBuilder]
- setAudioMode:AudioModeTypeSpeaker]
+ setAudioMode:AudioModeSpeaker]
build];
[CometChatCalls joinSessionWithSessionID:sessionId
@@ -113,8 +113,8 @@ class CallViewController: UIViewController, MediaEventsListener {
CallSession.shared.removeMediaEventsListener(self)
}
- func onAudioModeChanged(audioModeType: AudioModeType) {
- switch audioModeType {
+ func onAudioModeChanged(audioMode: AudioMode) {
+ switch audioMode {
case .speaker:
print("Switched to speaker")
case .earpiece:
@@ -127,7 +127,7 @@ class CallViewController: UIViewController, MediaEventsListener {
break
}
// Update audio mode button icon
- updateAudioModeIcon(audioModeType)
+ updateAudioModeIcon(audioMode)
}
// Other callbacks...
@@ -137,8 +137,6 @@ class CallViewController: UIViewController, MediaEventsListener {
func onVideoResumed() {}
func onRecordingStarted() {}
func onRecordingStopped() {}
- func onScreenShareStarted() {}
- func onScreenShareStopped() {}
func onCameraFacingChanged(cameraFacing: CameraFacing) {}
}
```
@@ -159,9 +157,9 @@ class CallViewController: UIViewController, MediaEventsListener {
[[CallSession shared] removeMediaEventsListener:self];
}
-- (void)onAudioModeChangedWithAudioModeType:(AudioModeType)audioModeType {
+- (void)onAudioModeChangedWithAudioMode:(AudioMode)audioMode {
// Update audio mode button icon
- [self updateAudioModeIcon:audioModeType];
+ [self updateAudioModeIcon:audioMode];
}
// Other callbacks...
@@ -187,7 +185,7 @@ let sessionSettings = CometChatCalls.sessionSettingsBuilder
```objectivec
SessionSettings *sessionSettings = [[[[CometChatCalls sessionSettingsBuilder]
- setAudioMode:AudioModeTypeSpeaker]
+ setAudioMode:AudioModeSpeaker]
hideAudioModeButton:YES]
build];
```
diff --git a/calls/ios/call-logs.mdx b/calls/ios/call-logs.mdx
index b8b66f643..3b4d64e94 100644
--- a/calls/ios/call-logs.mdx
+++ b/calls/ios/call-logs.mdx
@@ -14,15 +14,15 @@ Use `CallLogsRequest` to fetch call logs with pagination support. The builder pa
```swift
-let callLogRequest = CallLogsRequest.CallLogsRequestBuilder()
- .setLimit(30)
+let callLogRequest = CallLogsRequest.CallLogsBuilder()
+ .set(limit: 30)
.build()
callLogRequest.fetchNext(onSuccess: { callLogs in
for callLog in callLogs {
- print("Session: \(callLog.sessionID ?? "")")
- print("Duration: \(callLog.totalDuration ?? "")")
- print("Status: \(callLog.status ?? "")")
+ print("Session: \(callLog.sessionID)")
+ print("Duration: \(callLog.totalDuration)")
+ print("Status: \(callLog.status.value)")
}
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
@@ -31,15 +31,13 @@ callLogRequest.fetchNext(onSuccess: { callLogs in
```objectivec
-CallLogsRequest *callLogRequest = [[[CallLogsRequest CallLogsRequestBuilder]
- setLimit:30]
- build];
+CallLogsBuilder *builder = [[CallLogsBuilder alloc] init];
+CallLogsRequest *callLogRequest = [[builder setWithLimit:30] build];
[callLogRequest fetchNextOnSuccess:^(NSArray * callLogs) {
for (CallLog *callLog in callLogs) {
NSLog(@"Session: %@", callLog.sessionID);
NSLog(@"Duration: %@", callLog.totalDuration);
- NSLog(@"Status: %@", callLog.status);
}
} onError:^(CometChatCallException * error) {
NSLog(@"Error: %@", error.errorDescription);
@@ -48,20 +46,25 @@ CallLogsRequest *callLogRequest = [[[CallLogsRequest CallLogsRequestBuilder]
-## CallLogsRequestBuilder
+## CallLogsBuilder
Configure the request using the builder methods:
| Method | Type | Description |
|--------|------|-------------|
-| `setLimit(Int)` | Int | Number of call logs to fetch per request (default: 30, max: 100) |
-| `setSessionType(String)` | String | Filter by call type: `video` or `audio` |
-| `setCallStatus(String)` | String | Filter by call status |
-| `setHasRecording(Bool)` | Bool | Filter calls that have recordings |
-| `setCallCategory(String)` | String | Filter by category: `call` or `meet` |
-| `setCallDirection(String)` | String | Filter by direction: `incoming` or `outgoing` |
-| `setUid(String)` | String | Filter calls with a specific user |
-| `setGuid(String)` | String | Filter calls with a specific group |
+| `set(limit: Int)` | Int | Number of call logs to fetch per request (default: 30, max: 100) |
+| `set(callType: SessionType)` | SessionType | Filter by call type: `.video` or `.voice` |
+| `set(callStatus: CallStatus)` | CallStatus | Filter by call status |
+| `set(hasRecording: Bool)` | Bool | Filter calls that have recordings |
+| `set(hasTranscriptions: Bool)` | Bool | Filter calls that have transcripts, and attach them to each log |
+| `set(callCategory: CallCategory)` | CallCategory | Filter by category: `.call`, `.meet`, `.presenter` or `.broadcast` |
+| `set(callDirection: CallDirection)` | CallDirection | Filter by direction: `.incoming` or `.outgoing` |
+| `set(uid: String)` | String | Filter calls with a specific user |
+| `set(guid: String)` | String | Filter calls with a specific group |
+
+
+In Objective-C these are `setWithLimit:`, `setWithCallType:`, `setWithHasTranscriptions:` and so on, and the builder is instantiated directly as `[[CallLogsBuilder alloc] init]`.
+
### Filter Examples
@@ -69,50 +72,60 @@ Configure the request using the builder methods:
```swift
// Fetch only video calls
-let videoCallsRequest = CallLogsRequest.CallLogsRequestBuilder()
- .setSessionType("video")
- .setLimit(20)
+let videoCallsRequest = CallLogsRequest.CallLogsBuilder()
+ .set(callType: .video)
+ .set(limit: 20)
.build()
// Fetch calls with recordings
-let recordedCallsRequest = CallLogsRequest.CallLogsRequestBuilder()
- .setHasRecording(true)
+let recordedCallsRequest = CallLogsRequest.CallLogsBuilder()
+ .set(hasRecording: true)
+ .build()
+
+// Fetch calls with transcripts
+let transcribedCallsRequest = CallLogsRequest.CallLogsBuilder()
+ .set(hasTranscriptions: true)
.build()
// Fetch missed incoming calls
-let missedCallsRequest = CallLogsRequest.CallLogsRequestBuilder()
- .setCallStatus("missed")
- .setCallDirection("incoming")
+let missedCallsRequest = CallLogsRequest.CallLogsBuilder()
+ .set(callStatus: .missed)
+ .set(callDirection: .incoming)
.build()
// Fetch calls with a specific user
-let userCallsRequest = CallLogsRequest.CallLogsRequestBuilder()
- .setUid("user_id")
+let userCallsRequest = CallLogsRequest.CallLogsBuilder()
+ .set(uid: "user_id")
.build()
```
```objectivec
// Fetch only video calls
-CallLogsRequest *videoCallsRequest = [[[[CallLogsRequest CallLogsRequestBuilder]
- setSessionType:@"video"]
- setLimit:20]
+CallLogsRequest *videoCallsRequest = [[[[[CallLogsBuilder alloc] init]
+ setWithCallType:SessionTypeVideo]
+ setWithLimit:20]
build];
// Fetch calls with recordings
-CallLogsRequest *recordedCallsRequest = [[[CallLogsRequest CallLogsRequestBuilder]
- setHasRecording:YES]
+CallLogsRequest *recordedCallsRequest = [[[[CallLogsBuilder alloc] init]
+ setWithHasRecording:YES]
+ build];
+
+// Fetch calls with transcripts
+CallLogsRequest *transcribedCallsRequest = [[[[CallLogsBuilder alloc] init]
+ setWithHasTranscriptions:YES]
build];
// Fetch missed incoming calls
-CallLogsRequest *missedCallsRequest = [[[[CallLogsRequest CallLogsRequestBuilder]
- setCallStatus:@"missed"]
- setCallDirection:@"incoming"]
+CallLogsRequest *missedCallsRequest = [[[[[CallLogsBuilder alloc] init]
+ setWithCallStatus:CallStatusMissed]
+ setWithCallDirection:CallDirectionIncoming]
build];
// Fetch calls with a specific user
-CallLogsRequest *userCallsRequest = [[[CallLogsRequest CallLogsRequestBuilder]
- setUid:@"user_id"]
+CallLogsRequest *userCallsRequest = [[[[CallLogsBuilder alloc] init]
+ setWithUid:@"user_id"]
build];
```
@@ -120,7 +133,7 @@ CallLogsRequest *userCallsRequest = [[[CallLogsRequest CallLogsRequestBuilder]
## Pagination
-Use `fetchNext()` and `fetchPrevious()` for pagination:
+Use `fetchNext` and `fetchPrevious` for pagination. `fetchPrevious` takes an optional `authToken`; pass `nil` to use the logged-in user's stored token:
@@ -133,7 +146,7 @@ callLogRequest.fetchNext(onSuccess: { callLogs in
})
// Fetch previous page
-callLogRequest.fetchPrevious(onSuccess: { callLogs in
+callLogRequest.fetchPrevious(authToken: nil, onSuccess: { callLogs in
// Handle previous page
}, onError: { error in
print("Error: \(error?.errorDescription ?? "")")
@@ -150,11 +163,12 @@ callLogRequest.fetchPrevious(onSuccess: { callLogs in
}];
// Fetch previous page
-[callLogRequest fetchPreviousOnSuccess:^(NSArray * callLogs) {
- // Handle previous page
-} onError:^(CometChatCallException * error) {
- NSLog(@"Error: %@", error.errorDescription);
-}];
+[callLogRequest fetchPreviousWithAuthToken:nil
+ onSuccess:^(NSArray * callLogs) {
+ // Handle previous page
+ } onError:^(CometChatCallException * error) {
+ NSLog(@"Error: %@", error.errorDescription);
+ }];
```
@@ -166,21 +180,25 @@ Each `CallLog` object contains detailed information about a call:
| Property | Type | Description |
|----------|------|-------------|
| `sessionID` | String | Unique identifier for the call session |
+| `mid` | String | Meeting identifier |
| `initiator` | CallEntity | User who initiated the call |
| `receiver` | CallEntity | User or group that received the call |
-| `receiverType` | String | `user` or `group` |
-| `type` | String | Call type: `video` or `audio` |
-| `status` | String | Final status of the call |
-| `callCategory` | String | Category: `call` or `meet` |
+| `receiverType` | CallEntityType | `.callUser` or `.callGroup` |
+| `type` | SessionType | Call type: `.video` or `.voice` |
+| `status` | CallStatus | Final status of the call |
+| `mode` | CallCategory | Category: `.call`, `.meet`, `.presenter` or `.broadcast` |
| `initiatedAt` | Int | Timestamp when call was initiated |
-| `endedAt` | Int | Timestamp when call ended |
+| `startedAt` | Int? | Timestamp when call started |
+| `endedAt` | Int? | Timestamp when call ended |
| `totalDuration` | String | Human-readable duration (e.g., "5:30") |
| `totalDurationInMinutes` | Double | Duration in minutes |
| `totalAudioMinutes` | Double | Audio duration in minutes |
| `totalVideoMinutes` | Double | Video duration in minutes |
| `totalParticipants` | Int | Number of participants |
+| `participants` | [Participant] | List of participants who joined |
| `hasRecording` | Bool | Whether the call was recorded |
| `recordings` | [Recording] | List of recording objects |
+| `transcriptions` | [Transcript] | List of transcript objects. Populated only when the request opted in with `set(hasTranscriptions:)` |
## Access Recordings
@@ -192,10 +210,10 @@ If a call has recordings, access them through the `recordings` property:
callLogRequest.fetchNext(onSuccess: { callLogs in
for callLog in callLogs {
if callLog.hasRecording {
- for recording in callLog.recordings ?? [] {
+ for recording in callLog.recordings {
print("Recording ID: \(recording.rid ?? "")")
print("Recording URL: \(recording.recordingURL ?? "")")
- print("Duration: \(recording.duration) seconds")
+ print("Duration: \(recording.duration ?? 0) seconds")
}
}
}
@@ -223,6 +241,55 @@ callLogRequest.fetchNext(onSuccess: { callLogs in
+## Access Transcripts
+
+*Available since v5.0.4*
+
+Opting in with `set(hasTranscriptions: true)` restricts the list to transcribed calls **and** makes the server attach each call's transcripts to the log:
+
+
+
+```swift
+let callLogRequest = CallLogsRequest.CallLogsBuilder()
+ .set(limit: 30)
+ .set(hasTranscriptions: true)
+ .build()
+
+callLogRequest.fetchNext(onSuccess: { callLogs in
+ for callLog in callLogs {
+ for transcript in callLog.transcriptions {
+ print("Transcript ID: \(transcript.tid)")
+ print("Transcript URL: \(transcript.transcriptUrl)")
+ }
+ }
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+
+```objectivec
+CallLogsRequest *callLogRequest = [[[[[CallLogsBuilder alloc] init]
+ setWithLimit:30]
+ setWithHasTranscriptions:YES]
+ build];
+
+[callLogRequest fetchNextOnSuccess:^(NSArray * callLogs) {
+ for (CallLog *callLog in callLogs) {
+ for (Transcript *transcript in callLog.transcriptions) {
+ NSLog(@"Transcript ID: %@", transcript.tid);
+ NSLog(@"Transcript URL: %@", transcript.transcriptUrl);
+ }
+ }
+} onError:^(CometChatCallException * error) {
+ NSLog(@"Error: %@", error.errorDescription);
+}];
+```
+
+
+
+`transcriptions` is an empty array when the server omitted transcripts, so it never needs a nil check. To page through a single session's transcripts directly, use [`TranscriptsRequest`](/calls/ios/transcription#retrieving-transcripts).
+
| Status | Description |
|--------|-------------|
@@ -249,3 +316,8 @@ callLogRequest.fetchNext(onSuccess: { callLogs in
| `incoming` | Call received by the user |
| `outgoing` | Call initiated by the user |
+
+## Related Documentation
+
+- [Transcription](/calls/ios/transcription)
+- [Recording](/calls/ios/recording)
diff --git a/calls/ios/custom-control-panel.mdx b/calls/ios/custom-control-panel.mdx
index e38d02327..bc85aaaef 100644
--- a/calls/ios/custom-control-panel.mdx
+++ b/calls/ios/custom-control-panel.mdx
@@ -359,9 +359,7 @@ extension CallViewController: MediaEventsListener {
// Other MediaEventsListener callbacks
func onRecordingStarted() {}
func onRecordingStopped() {}
- func onScreenShareStarted() {}
- func onScreenShareStopped() {}
- func onAudioModeChanged(audioModeType: AudioModeType) {}
+ func onAudioModeChanged(audioMode: AudioMode) {}
func onCameraFacingChanged(cameraFacing: CameraFacing) {}
}
```
diff --git a/calls/ios/custom-participant-list.mdx b/calls/ios/custom-participant-list.mdx
index 2b290ed8f..c9ac94934 100644
--- a/calls/ios/custom-participant-list.mdx
+++ b/calls/ios/custom-participant-list.mdx
@@ -59,6 +59,10 @@ class ParticipantListViewController: UIViewController {
private let searchBar = UISearchBar()
private var participants: [Participant] = []
private var filteredParticipants: [Participant] = []
+ /// Per-participant state the app maintains itself, keyed by `Participant.uid`, because
+ /// `Participant` carries no such flags. Starts empty — anything that happened before this
+ /// client joined is not recoverable.
+ fileprivate var statuses: [String: ParticipantStatus] = [:]
override func viewDidLoad() {
super.viewDidLoad()
@@ -116,12 +120,16 @@ class ParticipantListViewController: UIViewController {
@property (nonatomic, strong) UISearchBar *searchBar;
@property (nonatomic, strong) NSArray *participants;
@property (nonatomic, strong) NSArray *filteredParticipants;
+/// Per-participant state the app maintains itself, keyed by uid — `Participant` carries no
+/// such flags. Starts empty; state from before this client joined is not recoverable.
+@property (nonatomic, strong) NSMutableDictionary *statuses;
@end
@implementation ParticipantListViewController
- (void)viewDidLoad {
[super viewDidLoad];
+ self.statuses = [NSMutableDictionary dictionary];
[self setupUI];
[self setupParticipantListener];
}
@@ -177,11 +185,29 @@ class ParticipantListViewController: UIViewController {
## Step 3: Create Participant Cell
-Build a custom table view cell to display participant information:
+Build a custom table view cell to display participant information.
+
+
+ `Participant` carries **only** identity and timing fields — `uid`, `name`, `avatar`, `mid`,
+ `state`, `isJoined`, `joinedAt`, `leftAt`, `deviceID` and the `total*Minutes` counters. It has
+ **no** mute / video / screen-share / hand-raise / pin flags, and the SDK exposes no getter for
+ them. Accumulate that state in your own app from the [participant events](/calls/ios/events),
+ as below. Because those events fire only on change, a client that joins mid-call cannot recover
+ state that was already in effect.
+
```swift
+/// Per-participant state the app maintains itself, keyed by `Participant.uid`.
+struct ParticipantStatus {
+ var isAudioMuted = false
+ var isVideoPaused = false
+ var isPresenting = false
+ var isHandRaised = false
+ var isPinned = false
+}
+
class ParticipantCell: UITableViewCell {
private let avatarImageView = UIImageView()
@@ -257,23 +283,23 @@ class ParticipantCell: UITableViewCell {
])
}
- func configure(with participant: Participant) {
+ func configure(with participant: Participant, status: ParticipantStatus) {
self.participant = participant
nameLabel.text = participant.name
-
- // Build status text
+
+ // Build status text from the app's own state, not from `participant`.
var statusParts: [String] = []
- if participant.isAudioMuted { statusParts.append("🔇 Muted") }
- if participant.isVideoPaused { statusParts.append("📹 Video Off") }
- if participant.isPresenting { statusParts.append("🖥️ Presenting") }
- if participant.raisedHandTimestamp > 0 { statusParts.append("✋ Hand Raised") }
- if participant.isPinned { statusParts.append("📌 Pinned") }
-
+ if status.isAudioMuted { statusParts.append("🔇 Muted") }
+ if status.isVideoPaused { statusParts.append("📹 Video Off") }
+ if status.isPresenting { statusParts.append("🖥️ Presenting") }
+ if status.isHandRaised { statusParts.append("✋ Hand Raised") }
+ if status.isPinned { statusParts.append("📌 Pinned") }
+
statusLabel.text = statusParts.isEmpty ? "Active" : statusParts.joined(separator: " • ")
-
+
// Update button states
- muteButton.alpha = participant.isAudioMuted ? 0.5 : 1.0
- pinButton.tintColor = participant.isPinned ? .systemBlue : .systemGray
+ muteButton.alpha = status.isAudioMuted ? 0.5 : 1.0
+ pinButton.tintColor = status.isPinned ? .systemBlue : .systemGray
}
@objc private func muteButtonTapped() {
@@ -290,11 +316,23 @@ class ParticipantCell: UITableViewCell {
```objectivec
+/// Per-participant state the app maintains itself, keyed by `Participant.uid`.
+@interface ParticipantStatus : NSObject
+@property (nonatomic, assign) BOOL isAudioMuted;
+@property (nonatomic, assign) BOOL isVideoPaused;
+@property (nonatomic, assign) BOOL isPresenting;
+@property (nonatomic, assign) BOOL isHandRaised;
+@property (nonatomic, assign) BOOL isPinned;
+@end
+
+@implementation ParticipantStatus
+@end
+
@interface ParticipantCell : UITableViewCell
@property (nonatomic, strong) Participant *participant;
@property (nonatomic, copy) void (^onMuteAction)(Participant *);
@property (nonatomic, copy) void (^onPinAction)(Participant *);
-- (void)configureWithParticipant:(Participant *)participant;
+- (void)configureWithParticipant:(Participant *)participant status:(ParticipantStatus *)status;
@end
@implementation ParticipantCell {
@@ -372,23 +410,23 @@ class ParticipantCell: UITableViewCell {
]];
}
-- (void)configureWithParticipant:(Participant *)participant {
+- (void)configureWithParticipant:(Participant *)participant status:(ParticipantStatus *)status {
self.participant = participant;
_nameLabel.text = participant.name;
-
- // Build status text
+
+ // Build status text from the app's own state, not from `participant`.
NSMutableArray *statusParts = [NSMutableArray array];
- if (participant.isAudioMuted) [statusParts addObject:@"🔇 Muted"];
- if (participant.isVideoPaused) [statusParts addObject:@"📹 Video Off"];
- if (participant.isPresenting) [statusParts addObject:@"🖥️ Presenting"];
- if (participant.raisedHandTimestamp > 0) [statusParts addObject:@"✋ Hand Raised"];
- if (participant.isPinned) [statusParts addObject:@"📌 Pinned"];
-
+ if (status.isAudioMuted) [statusParts addObject:@"🔇 Muted"];
+ if (status.isVideoPaused) [statusParts addObject:@"📹 Video Off"];
+ if (status.isPresenting) [statusParts addObject:@"🖥️ Presenting"];
+ if (status.isHandRaised) [statusParts addObject:@"✋ Hand Raised"];
+ if (status.isPinned) [statusParts addObject:@"📌 Pinned"];
+
_statusLabel.text = statusParts.count == 0 ? @"Active" : [statusParts componentsJoinedByString:@" • "];
-
+
// Update button states
- _muteButton.alpha = participant.isAudioMuted ? 0.5 : 1.0;
- _pinButton.tintColor = participant.isPinned ? [UIColor systemBlueColor] : [UIColor systemGrayColor];
+ _muteButton.alpha = status.isAudioMuted ? 0.5 : 1.0;
+ _pinButton.tintColor = status.isPinned ? [UIColor systemBlueColor] : [UIColor systemGrayColor];
}
- (void)muteButtonTapped {
@@ -437,22 +475,37 @@ extension ParticipantListViewController: ParticipantEventListener {
}
func onParticipantJoined(participant: Participant) {
- print("\(participant.name) joined")
+ print("\(participant.name ?? "") joined")
}
-
+
func onParticipantLeft(participant: Participant) {
- print("\(participant.name) left")
+ guard let uid = participant.uid else { return }
+ DispatchQueue.main.async {
+ self.statuses.removeValue(forKey: uid) // don't leak state for someone who left
+ self.tableView.reloadData()
+ }
}
-
- func onParticipantAudioMuted(participant: Participant) {
- // Table will update via onParticipantListChanged
+
+ // `onParticipantListChanged` carries no flags, so every status below comes from these
+ // transition events. Each one mutates the app's own `statuses` map and redraws.
+ func onParticipantAudioMuted(participant: Participant) { update(participant) { $0.isAudioMuted = true } }
+ func onParticipantAudioUnmuted(participant: Participant) { update(participant) { $0.isAudioMuted = false } }
+ func onParticipantVideoPaused(participant: Participant) { update(participant) { $0.isVideoPaused = true } }
+ func onParticipantVideoResumed(participant: Participant) { update(participant) { $0.isVideoPaused = false } }
+ func onParticipantHandRaised(participant: Participant) { update(participant) { $0.isHandRaised = true } }
+ func onParticipantHandLowered(participant: Participant) { update(participant) { $0.isHandRaised = false } }
+ func onParticipantStartedScreenShare(participant: Participant) { update(participant) { $0.isPresenting = true } }
+ func onParticipantStoppedScreenShare(participant: Participant) { update(participant) { $0.isPresenting = false } }
+
+ private func update(_ participant: Participant, _ change: @escaping (inout ParticipantStatus) -> Void) {
+ guard let uid = participant.uid else { return }
+ DispatchQueue.main.async {
+ var status = self.statuses[uid] ?? ParticipantStatus()
+ change(&status)
+ self.statuses[uid] = status
+ self.tableView.reloadData()
+ }
}
-
- func onParticipantAudioUnmuted(participant: Participant) {}
- func onParticipantVideoPaused(participant: Participant) {}
- func onParticipantVideoResumed(participant: Participant) {}
- func onParticipantHandRaised(participant: Participant) {}
- func onParticipantHandLowered(participant: Participant) {}
}
```
@@ -483,7 +536,49 @@ extension ParticipantListViewController: ParticipantEventListener {
}
- (void)onParticipantLeftWithParticipant:(Participant *)participant {
- NSLog(@"%@ left", participant.name);
+ if (!participant.uid) { return; }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self.statuses removeObjectForKey:participant.uid]; // don't leak state
+ [self.tableView reloadData];
+ });
+}
+
+// `onParticipantListChanged` carries no flags, so every status below comes from these
+// transition events. self.statuses is an NSMutableDictionary.
+- (void)updateParticipant:(Participant *)participant change:(void (^)(ParticipantStatus *))change {
+ NSString *uid = participant.uid;
+ if (!uid) { return; }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ ParticipantStatus *status = self.statuses[uid] ?: [ParticipantStatus new];
+ change(status);
+ self.statuses[uid] = status;
+ [self.tableView reloadData];
+ });
+}
+
+- (void)onParticipantAudioMutedWithParticipant:(Participant *)p {
+ [self updateParticipant:p change:^(ParticipantStatus *s) { s.isAudioMuted = YES; }];
+}
+- (void)onParticipantAudioUnmutedWithParticipant:(Participant *)p {
+ [self updateParticipant:p change:^(ParticipantStatus *s) { s.isAudioMuted = NO; }];
+}
+- (void)onParticipantVideoPausedWithParticipant:(Participant *)p {
+ [self updateParticipant:p change:^(ParticipantStatus *s) { s.isVideoPaused = YES; }];
+}
+- (void)onParticipantVideoResumedWithParticipant:(Participant *)p {
+ [self updateParticipant:p change:^(ParticipantStatus *s) { s.isVideoPaused = NO; }];
+}
+- (void)onParticipantHandRaisedWithParticipant:(Participant *)p {
+ [self updateParticipant:p change:^(ParticipantStatus *s) { s.isHandRaised = YES; }];
+}
+- (void)onParticipantHandLoweredWithParticipant:(Participant *)p {
+ [self updateParticipant:p change:^(ParticipantStatus *s) { s.isHandRaised = NO; }];
+}
+- (void)onParticipantStartedScreenShareWithParticipant:(Participant *)p {
+ [self updateParticipant:p change:^(ParticipantStatus *s) { s.isPresenting = YES; }];
+}
+- (void)onParticipantStoppedScreenShareWithParticipant:(Participant *)p {
+ [self updateParticipant:p change:^(ParticipantStatus *s) { s.isPresenting = NO; }];
}
```
@@ -506,18 +601,26 @@ extension ParticipantListViewController: UITableViewDelegate, UITableViewDataSou
let cell = tableView.dequeueReusableCell(withIdentifier: "ParticipantCell", for: indexPath) as! ParticipantCell
let participant = filteredParticipants[indexPath.row]
- cell.configure(with: participant)
-
- cell.onMuteAction = { [weak self] participant in
- CallSession.shared.muteParticipant(participant.uid)
+ let status = participant.uid.flatMap { statuses[$0] } ?? ParticipantStatus()
+ cell.configure(with: participant, status: status)
+
+ cell.onMuteAction = { participant in
+ guard let uid = participant.uid else { return }
+ CallSession.shared.muteParticipant(participantId: uid)
}
-
+
cell.onPinAction = { [weak self] participant in
- if participant.isPinned {
+ guard let self, let uid = participant.uid else { return }
+ if self.statuses[uid]?.isPinned == true {
CallSession.shared.unpinParticipant()
+ self.statuses[uid]?.isPinned = false
} else {
- CallSession.shared.pinParticipant(participantId: participant.uid, type: "pin")
+ // Only one participant can be pinned at a time — `unpinParticipant()` takes no id.
+ for key in Array(self.statuses.keys) { self.statuses[key]?.isPinned = false }
+ CallSession.shared.pinParticipant(participantId: uid, type: "pin")
+ self.statuses[uid, default: ParticipantStatus()].isPinned = true
}
+ self.tableView.reloadData()
}
return cell
@@ -531,7 +634,7 @@ extension ParticipantListViewController: UISearchBarDelegate {
filteredParticipants = participants
} else {
filteredParticipants = participants.filter {
- $0.name.localizedCaseInsensitiveContains(searchText)
+ ($0.name ?? "").localizedCaseInsensitiveContains(searchText)
}
}
tableView.reloadData()
@@ -549,19 +652,30 @@ extension ParticipantListViewController: UISearchBarDelegate {
ParticipantCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ParticipantCell" forIndexPath:indexPath];
Participant *participant = self.filteredParticipants[indexPath.row];
- [cell configureWithParticipant:participant];
-
+ ParticipantStatus *status = participant.uid ? self.statuses[participant.uid] : nil;
+ [cell configureWithParticipant:participant status:(status ?: [ParticipantStatus new])];
+
__weak typeof(self) weakSelf = self;
cell.onMuteAction = ^(Participant *p) {
- [[CallSession shared] muteParticipant:p.uid];
+ if (!p.uid) { return; }
+ [[CallSession shared] muteParticipantWithParticipantId:p.uid];
};
-
+
cell.onPinAction = ^(Participant *p) {
- if (p.isPinned) {
+ __strong typeof(weakSelf) self = weakSelf;
+ if (!self || !p.uid) { return; }
+ if (self.statuses[p.uid].isPinned) {
[[CallSession shared] unpinParticipant];
+ self.statuses[p.uid].isPinned = NO;
} else {
+ // Only one participant can be pinned at a time — unpinParticipant takes no id.
+ for (NSString *key in self.statuses) { self.statuses[key].isPinned = NO; }
[[CallSession shared] pinParticipantWithParticipantId:p.uid type:@"pin"];
+ ParticipantStatus *s = self.statuses[p.uid] ?: [ParticipantStatus new];
+ s.isPinned = YES;
+ self.statuses[p.uid] = s;
}
+ [self.tableView reloadData];
};
return cell;
diff --git a/calls/ios/events.mdx b/calls/ios/events.mdx
index 9440bd09a..ecbded7d6 100644
--- a/calls/ios/events.mdx
+++ b/calls/ios/events.mdx
@@ -276,10 +276,7 @@ class CallViewController: UIViewController, MediaEventsListener {
// Call recording stopped
}
- func onScreenShareStarted() {}
- func onScreenShareStopped() {}
-
- func onAudioModeChanged(audioModeType: AudioModeType) {
+ func onAudioModeChanged(audioMode: AudioMode) {
// Audio output device changed
}
@@ -329,7 +326,7 @@ class CallViewController: UIViewController, MediaEventsListener {
// Call recording stopped
}
-- (void)onAudioModeChangedWithAudioModeType:(AudioModeType)audioModeType {
+- (void)onAudioModeChangedWithAudioMode:(AudioMode)audioMode {
// Audio output device changed
}
@@ -352,13 +349,18 @@ class CallViewController: UIViewController, MediaEventsListener {
| `onVideoResumed` | - | Your camera was turned on |
| `onRecordingStarted` | - | Call recording started |
| `onRecordingStopped` | - | Call recording stopped |
-| `onScreenShareStarted` | - | You started screen sharing |
-| `onScreenShareStopped` | - | You stopped screen sharing |
-| `onAudioModeChanged` | `AudioModeType` | Audio output device changed |
+| `onAudioModeChanged` | `AudioMode` | Audio output device changed |
| `onCameraFacingChanged` | `CameraFacing` | Camera switched between front and back |
+
+ `MediaEventsListener` has **no** screen-share callback. Screen sharing is observed on
+ [`ParticipantEventListener`](#participanteventlistener) via `onParticipantStartedScreenShare` /
+ `onParticipantStoppedScreenShare` — and on iOS those are **receive-only**: the SDK reports a share
+ started from another client, but cannot start one locally (that needs a Broadcast Upload Extension).
+
+
-
+
| Value | Description |
|-------|-------------|
| `.speaker` | Audio routed through device loudspeaker |
diff --git a/calls/ios/idle-timeout.mdx b/calls/ios/idle-timeout.mdx
index 0cd70de52..1199ce13d 100644
--- a/calls/ios/idle-timeout.mdx
+++ b/calls/ios/idle-timeout.mdx
@@ -36,7 +36,7 @@ Set the idle timeout period using `setIdleTimeoutPeriod()` in `SessionSettingsBu
```swift
let sessionSettings = CometChatCalls.sessionSettingsBuilder
.setIdleTimeoutPeriod(120) // 2 minutes
- .setType(.video)
+ .setSessionType(.video)
.build()
CometChatCalls.joinSession(
@@ -56,7 +56,7 @@ CometChatCalls.joinSession(
```objectivec
SessionSettings *sessionSettings = [[[[CometChatCalls sessionSettingsBuilder]
setIdleTimeoutPeriod:120]
- setType:CallTypeVideo]
+ setSessionType:SessionTypeVideo]
build];
[CometChatCalls joinSessionWithSessionID:sessionId
diff --git a/calls/ios/join-session.mdx b/calls/ios/join-session.mdx
index 8c4bfd0f8..9ad710f6e 100644
--- a/calls/ios/join-session.mdx
+++ b/calls/ios/join-session.mdx
@@ -62,7 +62,7 @@ let sessionId = "SESSION_ID"
let sessionSettings = CometChatCalls.sessionSettingsBuilder
.setDisplayName("John Doe")
- .setType(.video)
+ .setSessionType(.video)
.build()
CometChatCalls.joinSession(
@@ -84,7 +84,7 @@ NSString *sessionId = @"SESSION_ID";
SessionSettings *sessionSettings = [[[[CometChatCalls sessionSettingsBuilder]
setDisplayName:@"John Doe"]
- setType:CallTypeVideo]
+ setSessionType:SessionTypeVideo]
build];
[CometChatCalls joinSessionWithSessionID:sessionId
@@ -162,7 +162,7 @@ Use the generated token to join the session. This gives you control over when an
```swift
let sessionSettings = CometChatCalls.sessionSettingsBuilder
.setDisplayName("John Doe")
- .setType(.video)
+ .setSessionType(.video)
.build()
// Use the previously generated token
@@ -183,7 +183,7 @@ CometChatCalls.joinSession(
```objectivec
SessionSettings *sessionSettings = [[[[CometChatCalls sessionSettingsBuilder]
setDisplayName:@"John Doe"]
- setType:CallTypeVideo]
+ setSessionType:SessionTypeVideo]
build];
// Use the previously generated token
@@ -221,7 +221,7 @@ CometChatCalls.generateToken(sessionID: sessionId, onSuccess: { [weak self] toke
// Step 2: Join with token
let sessionSettings = CometChatCalls.sessionSettingsBuilder
.setDisplayName("John Doe")
- .setType(.video)
+ .setSessionType(.video)
.build()
CometChatCalls.joinSession(
@@ -250,7 +250,7 @@ NSString *sessionId = @"SESSION_ID";
// Step 2: Join with token
SessionSettings *sessionSettings = [[[[CometChatCalls sessionSettingsBuilder]
setDisplayName:@"John Doe"]
- setType:CallTypeVideo]
+ setSessionType:SessionTypeVideo]
build];
[CometChatCalls joinSessionWithCallToken:token
diff --git a/calls/ios/migration-guide-v5.mdx b/calls/ios/migration-guide-v5.mdx
index 6f90b986e..1c16205df 100644
--- a/calls/ios/migration-guide-v5.mdx
+++ b/calls/ios/migration-guide-v5.mdx
@@ -32,7 +32,7 @@ While v4 APIs will continue to work, migrating to v5 APIs gives you:
- **Granular event listeners** — 5 focused listener protocols instead of one monolithic `CallsEventsDelegate`
- **`CallSession` singleton** for cleaner session control — all actions on a single object instead of scattered static methods
- **Dedicated `login()` method** — the Calls SDK now handles its own authentication instead of depending on the Chat SDK's auth token or REST APIs
-- **Strongly-typed enums** — `AudioModeType`, `CallType`, `LayoutType`, `CameraFacing` instead of raw strings
+- **Strongly-typed enums** — `AudioMode`, `SessionType`, `LayoutType`, `CameraFacing` instead of raw strings
---
@@ -41,9 +41,10 @@ While v4 APIs will continue to work, migrating to v5 APIs gives you:
No changes required. The `init` API is the same in v5.
```swift
-let callAppSettings = CallAppSettings()
-callAppSettings.set(appId: "APP_ID")
-callAppSettings.set(region: "REGION")
+let callAppSettings = CallAppSettingsBuilder()
+ .set(appID: "APP_ID")
+ .set(region: "REGION")
+ .build()
CometChatCalls(callsAppSettings: callAppSettings, onSuccess: { success in
// Initialized
@@ -118,7 +119,7 @@ let callSettings = CometChatCalls.callSettingsBuilder
```swift
let sessionSettings = CometChatCalls.sessionSettingsBuilder
- .setType(.audio)
+ .setSessionType(.audio)
.startAudioMuted(false)
.startVideoPaused(false)
.setLayout(.tile)
@@ -141,7 +142,7 @@ let sessionSettings = CometChatCalls.sessionSettingsBuilder
| v4 Method | v5 Method | Notes |
|-----------|-----------|-------|
-| `setIsAudioOnly(true)` | `setType(.audio)` | Use `.video` for video calls |
+| `setIsAudioOnly(true)` | `setSessionType(.voice)` | Use `.video` for video calls |
| `setDefaultLayout(bool)` | `hideControlPanel(!bool)` + `hideHeaderPanel(!bool)` | Inverted logic |
| `setEndCallButtonDisable(bool)` | `hideLeaveSessionButton(bool)` | Same logic |
| `setMuteAudioButtonDisable(bool)` | `hideToggleAudioButton(bool)` | Same logic |
@@ -334,7 +335,7 @@ class MyMediaListener: MediaEventsListener {
func onVideoResumed() { }
func onRecordingStarted() { }
func onRecordingStopped() { }
- func onAudioModeChanged(audioModeType: AudioModeType) { }
+ func onAudioModeChanged(audioMode: AudioMode) { }
func onCameraFacingChanged(cameraFacing: CameraFacing) { }
}
callSession.addMediaEventsListener(myMediaListener)
@@ -373,7 +374,7 @@ v5 listeners use weak references internally, so they are automatically cleaned u
| `onUserJoined(rtcUser:)` | `ParticipantEventListener` | `onParticipantJoined(participant:)` |
| `onUserLeft(rtcUser:)` | `ParticipantEventListener` | `onParticipantLeft(participant:)` |
| `onUserListChanged(rtcUsers:)` | `ParticipantEventListener` | `onParticipantListChanged(participants:)` |
-| `onAudioModeChanged(mode:)` | `MediaEventsListener` | `onAudioModeChanged(audioModeType:)` |
+| `onAudioModeChanged(mode:)` | `MediaEventsListener` | `onAudioModeChanged(audioMode:)` |
| `onCallSwitchedToVideo(callSwitchedInfo:)` | *Removed* | — |
| `onUserMuted(rtcMutedUser:)` | `ParticipantEventListener` | `onParticipantAudioMuted(participant:)` |
| `onRecordingToggled(recordingInfo:)` | `MediaEventsListener` | `onRecordingStarted()` / `onRecordingStopped()` |
diff --git a/calls/ios/participant-management.mdx b/calls/ios/participant-management.mdx
index 8d592485e..ca3d44b52 100644
--- a/calls/ios/participant-management.mdx
+++ b/calls/ios/participant-management.mdx
@@ -18,12 +18,15 @@ Mute a specific participant's audio. This affects the participant for all users
```swift
-CallSession.shared.muteParticipant(participantId: participant.pid)
+guard let uid = participant.uid else { return }
+CallSession.shared.muteParticipant(participantId: uid)
```
```objectivec
-[[CallSession shared] muteParticipantWithParticipantId:participant.pid];
+if (participant.uid) {
+ [[CallSession shared] muteParticipantWithParticipantId:participant.uid];
+}
```
@@ -35,12 +38,15 @@ Pause a specific participant's video. This affects the participant for all users
```swift
-CallSession.shared.pauseParticipantVideo(participantId: participant.pid)
+guard let uid = participant.uid else { return }
+CallSession.shared.pauseParticipantVideo(participantId: uid)
```
```objectivec
-[[CallSession shared] pauseParticipantVideoWithParticipantId:participant.pid];
+if (participant.uid) {
+ [[CallSession shared] pauseParticipantVideoWithParticipantId:participant.uid];
+}
```
@@ -198,16 +204,25 @@ The `Participant` object contains information about each call participant:
| Property | Type | Description |
|----------|------|-------------|
-| `uid` | String | Unique identifier (CometChat user ID) |
-| `name` | String | Display name |
-| `avatar` | String | URL of avatar image |
-| `pid` | String | Participant ID for this call session |
-| `role` | String | Role in the call |
-| `audioMuted` | Bool | Whether audio is muted |
-| `videoPaused` | Bool | Whether video is paused |
-| `isPinned` | Bool | Whether pinned in layout |
-| `isPresenting` | Bool | Whether screen sharing |
-| `raisedHandTimestamp` | Int | Timestamp when hand was raised (0 if not raised) |
+| `uid` | `String?` | CometChat user ID — the identifier every moderator action takes |
+| `name` | `String?` | Display name |
+| `avatar` | `String?` | URL of avatar image |
+| `mid` | `String?` | Media ID for this call session |
+| `state` | `String?` | Participant state as reported by the server |
+| `isJoined` | `Bool?` | Whether the participant is currently joined |
+| `joinedAt` | `Int?` | Join timestamp |
+| `leftAt` | `Int?` | Leave timestamp |
+| `deviceID` | `String?` | Device identifier |
+| `totalAudioMinutes` | `Double?` | Audio minutes consumed |
+| `totalVideoMinutes` | `Double?` | Video minutes consumed |
+| `totalDurationInMinutes` | `Double?` | Total session minutes |
+
+
+ **Every property is Optional.** `Participant` carries no mute / video / pin / hand-raise /
+ screen-share flags, and the SDK exposes no getter for them — track that state in your own app
+ from the [participant events](/calls/ios/events). Because the events fire only on change, a
+ client that joins late cannot recover state that was already in effect.
+
## Hide Participant List Button
diff --git a/calls/ios/picture-in-picture.mdx b/calls/ios/picture-in-picture.mdx
index 2a599c34a..b47c3c8cb 100644
--- a/calls/ios/picture-in-picture.mdx
+++ b/calls/ios/picture-in-picture.mdx
@@ -7,16 +7,117 @@ description: "Configure picture-in-picture for CometChat Calls SDK v5 on iOS to
Enable Picture-in-Picture (PiP) mode to allow users to continue their call in a floating window while using other apps. PiP provides a seamless multitasking experience during calls.
+
+ **`enablePictureInPictureLayout()` alone does nothing visible.** It only tells the call UI to
+ re-lay-out for a smaller window — it does not create, move or float a window. Something has to
+ provide the small window, and there are two different ways to do that. Pick one before you write
+ any code.
+
+
+## Two kinds of Picture-in-Picture
+
+| | In-app PiP | System PiP |
+|---|---|---|
+| **What the user sees** | A small draggable call tile floating over **your own app** | A floating window over **other apps** and the Home Screen |
+| **Who provides the window** | **`PiPViewCoordinator`, shipped in the Calls SDK** | Apple's AVKit — **also implemented inside the Calls SDK**, behind an opt-in flag |
+| **Setup** | A few lines — see below | `SessionSettingsBuilder.enableIOSPictureInPicture(true)` + Background Modes; or wire `AVPictureInPictureController` yourself |
+| **Availability** | Any supported iOS version | iPhone iOS 15+, iPad iOS 9+ |
+
+In both cases you still call `enablePictureInPictureLayout()` / `disablePictureInPictureLayout()`
+so the SDK reshapes the call UI to match.
+
+
+ **System PiP is implemented inside the Calls SDK**, not only in Apple's API: the 5.0.4 binary links
+ `AVKit` and carries the whole path (`AVPictureInPictureController`,
+ `AVPictureInPictureControllerContentSource`, `AVPictureInPictureVideoCallViewController`), gated
+ behind `SessionSettingsBuilder.enableIOSPictureInPicture(_:)`. Enable that flag and add the
+ **Background Modes → Audio, AirPlay, and Picture in Picture** capability. You can still host
+ `AVPictureInPictureController` yourself if you want full control over the window.
+
+
+## In-app PiP with `PiPViewCoordinator`
+
+`PiPViewCoordinator` ships with the Calls SDK. Give it the same `UIView` you passed to
+`joinSession(container:)` and it turns that view into a draggable, tappable mini-call.
+
+
+
+```swift
+import CometChatCallsSDK
+
+final class PiPCallViewController: UIViewController, PiPViewCoordinatorDelegate {
+
+ private let callContainer = UIView()
+ private var pipCoordinator: PiPViewCoordinator?
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ // The coordinator manages the SAME view the call is rendered into.
+ let coordinator = PiPViewCoordinator(withView: callContainer)
+ coordinator.delegate = self
+ coordinator.initialPositionInSuperView = .lowerRightCorner
+ coordinator.dragBoundInsets = UIEdgeInsets(top: 25, left: 8, bottom: 8, right: 8)
+
+ // Pass the parent explicitly — with no argument it falls back to the key window.
+ coordinator.configureAsStickyView(withParentView: view)
+ coordinator.show()
+ pipCoordinator = coordinator
+
+ // ...then join the session into `callContainer` as usual.
+ }
+
+ /// Shrink to the floating tile.
+ func enterPiP() {
+ pipCoordinator?.enterPictureInPicture() // resize + enable dragging + exit button
+ CallSession.shared.enablePictureInPictureLayout() // reshape the call UI to match
+ }
+
+ /// Called by the coordinator AFTER it has already restored the full-size view —
+ /// this is a notification that PiP ended, not a request to end it.
+ func exitPictureInPicture() {
+ CallSession.shared.disablePictureInPictureLayout()
+ }
+
+ /// Keep the tile inside the screen on rotation or a size change.
+ override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
+ super.viewWillTransition(to: size, with: coordinator)
+ coordinator.animate { [weak self] _ in
+ guard let self else { return }
+ self.pipCoordinator?.resetBounds(bounds: CGRect(origin: .zero, size: size))
+ }
+ }
+}
+```
+
+
+
+### `PiPViewCoordinator` reference
+
+| Member | Purpose |
+|---|---|
+| `init(withView:)` | The view the call renders into — the same one given to `joinSession(container:)` |
+| `configureAsStickyView(withParentView:)` | Adds the view above everything in the parent. **Pass the parent explicitly**; with no argument it falls back to the key window |
+| `show(completion:)` / `hide(completion:)` | Fade in / out. `completion` is `AnimationCompletion` = `(Bool) -> Void` |
+| `enterPictureInPicture()` | Shrinks the view, enables the drag gesture, and adds a tap-to-reveal exit button |
+| `exitPictureInPicture()` | Restores full size, removes the drag gesture and exit button, then calls the delegate |
+| `resetBounds(bounds:)` | Call on rotation or any size change |
+| `stopDragGesture()` | Pin the tile in place |
+| `configureExitPiPButton(target:action:)` | Supply your own exit button |
+| `initialPositionInSuperView` | `.lowerRightCorner` (default) · `.upperRightCorner` · `.lowerLeftCorner` · `.upperLeftCorner` |
+| `dragBoundInsets` | How far the tile may be dragged toward each edge |
+| `delegate` | `PiPViewCoordinatorDelegate` — one method, `exitPictureInPicture()` |
+
-Picture-in-Picture implementation is handled at the app level using iOS's PiP APIs. The Calls SDK only adjusts the call UI layout to fit the PiP window - it does not manage the PiP window itself.
+ The tile size is fixed at 150px. The `c` property that used to set it is deprecated and ignored.
-## How It Works
+## How the SDK layout hook works
-1. Your app enters PiP mode using iOS's AVPictureInPictureController API
+1. Something provides the small window — `PiPViewCoordinator` (in-app) or `AVPictureInPictureController` (system)
2. You notify the Calls SDK by calling `enablePictureInPictureLayout()`
-3. The SDK adjusts the call UI to fit the smaller PiP window (hides controls, optimizes layout)
-4. When exiting PiP, call `disablePictureInPictureLayout()` to restore the full UI
+3. The SDK adjusts the call UI to fit the smaller window (hides controls, optimizes layout)
+4. When exiting, call `disablePictureInPictureLayout()` to restore the full UI
## Enable Picture-in-Picture
@@ -125,9 +226,9 @@ class CallViewController: UIViewController, LayoutListener {
-## iOS PiP Setup
+## System PiP setup (floating over other apps)
-To enable PiP in your iOS app:
+Only needed for System PiP — In-app PiP via `PiPViewCoordinator` requires none of this.
**1. Enable Background Modes**
diff --git a/calls/ios/raise-hand.mdx b/calls/ios/raise-hand.mdx
index 8142c6aa0..14a564bcd 100644
--- a/calls/ios/raise-hand.mdx
+++ b/calls/ios/raise-hand.mdx
@@ -122,37 +122,74 @@ class CallViewController: UIViewController, ParticipantEventListener {
-## Check Raised Hand Status
+## Track Raised Hands
-The `Participant` object includes a `raisedHandTimestamp` property to check if a participant has their hand raised:
+`Participant` carries **no** raised-hand property, and the SDK exposes no getter for one. Keep an
+ordered list of your own, driven by the `onParticipantHandRaised` / `onParticipantHandLowered`
+callbacks.
+
+
+ These callbacks fire only when a hand goes **up or down**. A client that joins mid-call cannot
+ discover hands that were already raised before it joined.
+
```swift
-func onParticipantListChanged(participants: [Participant]) {
- let raisedHands = participants
- .filter { $0.raisedHandTimestamp > 0 }
- .sorted { $0.raisedHandTimestamp < $1.raisedHandTimestamp }
-
- // Display participants with raised hands in order
- updateRaisedHandsList(raisedHands)
+// Raised hands, oldest first. Keyed by uid, because that is the only identifier
+// `Participant` actually carries.
+private var raisedHands: [(uid: String, name: String, at: Date)] = []
+
+func onParticipantHandRaised(participant: Participant) {
+ guard let uid = participant.uid else { return }
+ DispatchQueue.main.async {
+ guard !self.raisedHands.contains(where: { $0.uid == uid }) else { return }
+ self.raisedHands.append((uid, participant.name ?? uid, Date()))
+ self.updateRaisedHandsList(self.raisedHands)
+ }
+}
+
+func onParticipantHandLowered(participant: Participant) {
+ guard let uid = participant.uid else { return }
+ DispatchQueue.main.async {
+ self.raisedHands.removeAll { $0.uid == uid }
+ self.updateRaisedHandsList(self.raisedHands)
+ }
+}
+
+// Someone who leaves should not stay in the queue.
+func onParticipantLeft(participant: Participant) {
+ guard let uid = participant.uid else { return }
+ DispatchQueue.main.async {
+ self.raisedHands.removeAll { $0.uid == uid }
+ self.updateRaisedHandsList(self.raisedHands)
+ }
}
```
```objectivec
-- (void)onParticipantListChangedWithParticipants:(NSArray *)participants {
- NSMutableArray *raisedHands = [NSMutableArray array];
- for (Participant *p in participants) {
- if (p.raisedHandTimestamp > 0) {
- [raisedHands addObject:p];
+// NSMutableArray of uid strings, oldest first.
+@property (nonatomic, strong) NSMutableArray *raisedHandUIDs;
+
+- (void)onParticipantHandRaisedWithParticipant:(Participant *)participant {
+ NSString *uid = participant.uid;
+ if (!uid) { return; }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (![self.raisedHandUIDs containsObject:uid]) {
+ [self.raisedHandUIDs addObject:uid];
+ [self updateRaisedHandsList:self.raisedHandUIDs];
}
- }
- // Sort by timestamp and display
- [raisedHands sortUsingComparator:^NSComparisonResult(Participant *a, Participant *b) {
- return [@(a.raisedHandTimestamp) compare:@(b.raisedHandTimestamp)];
- }];
- [self updateRaisedHandsList:raisedHands];
+ });
+}
+
+- (void)onParticipantHandLoweredWithParticipant:(Participant *)participant {
+ NSString *uid = participant.uid;
+ if (!uid) { return; }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self.raisedHandUIDs removeObject:uid];
+ [self updateRaisedHandsList:self.raisedHandUIDs];
+ });
}
```
diff --git a/calls/ios/recording.mdx b/calls/ios/recording.mdx
index ab514ce5f..8ec736da7 100644
--- a/calls/ios/recording.mdx
+++ b/calls/ios/recording.mdx
@@ -68,6 +68,13 @@ SessionSettings *sessionSettings = [[[CometChatCalls sessionSettingsBuilder]
+
+**A recording runs until it is stopped.** Whether it was started manually or automatically through `enableAutoStartRecording`, it keeps running until someone stops it. If nobody does, it stops on its own when either:
+
+- Everyone leaves the session — the recording ends about a minute later.
+- Everyone in the session stays muted for 10 minutes.
+
+
## Hide Recording Button
Hide the recording button from the default call UI:
@@ -122,9 +129,7 @@ class CallViewController: UIViewController, MediaEventsListener {
func onAudioUnMuted() {}
func onVideoPaused() {}
func onVideoResumed() {}
- func onScreenShareStarted() {}
- func onScreenShareStopped() {}
- func onAudioModeChanged(audioModeType: AudioModeType) {}
+ func onAudioModeChanged(audioMode: AudioMode) {}
func onCameraFacingChanged(cameraFacing: CameraFacing) {}
}
```
diff --git a/calls/ios/ringing.mdx b/calls/ios/ringing.mdx
index efe23875f..1c94a2433 100644
--- a/calls/ios/ringing.mdx
+++ b/calls/ios/ringing.mdx
@@ -74,8 +74,8 @@ CometChat.initiateCall(call: call, timeout: 30, onSuccess: { call in
```objectivec
NSString *receiverID = @"USER_ID";
-CometChatReceiverType receiverType = CometChatReceiverTypeUser;
-CometChatCallType callType = CometChatCallTypeVideo;
+ReceiverType receiverType = ReceiverTypeUser;
+CallType callType = CallTypeVideo;
Call *call = [[Call alloc] initWithReceiverId:receiverID
callType:callType
@@ -115,7 +115,7 @@ extension CallViewController: CometChatCallDelegate {
func onIncomingCallReceived(incomingCall: Call?, error: CometChatException?) {
guard let call = incomingCall else { return }
- print("Incoming call from: \(call.callInitiator?.name ?? "")")
+ print("Incoming call from: \((call.callInitiator as? User)?.name ?? "")")
// Show incoming call UI with accept/reject options
}
@@ -130,7 +130,7 @@ extension CallViewController: CometChatCallDelegate {
// Dismiss outgoing call UI
}
- func onIncomingCallCancelled(cancelledCall: Call?, error: CometChatException?) {
+ func onIncomingCallCancelled(canceledCall: Call?, error: CometChatException?) {
print("Incoming call cancelled")
// Dismiss incoming call UI
}
@@ -150,7 +150,7 @@ NSString *listenerID = @"UNIQUE_LISTENER_ID";
// Implement CometChatCallDelegate
- (void)onIncomingCallReceivedWithIncomingCall:(Call *)incomingCall
error:(CometChatException *)error {
- NSLog(@"Incoming call from: %@", incomingCall.callInitiator.name);
+ NSLog(@"Incoming call from: %@", ((User *)incomingCall.callInitiator).name);
// Show incoming call UI with accept/reject options
}
@@ -166,7 +166,7 @@ NSString *listenerID = @"UNIQUE_LISTENER_ID";
// Dismiss outgoing call UI
}
-- (void)onIncomingCallCancelledWithCancelledCall:(Call *)cancelledCall
+- (void)onIncomingCallCancelledWithCanceledCall:(Call *)canceledCall
error:(CometChatException *)error {
NSLog(@"Incoming call cancelled");
// Dismiss incoming call UI
@@ -235,7 +235,7 @@ Reject an incoming call:
```swift
func rejectIncomingCall(sessionId: String) {
- let status: CometChat.CallStatus = .rejected
+ let status: CometChat.callStatus = .rejected
CometChat.rejectCall(sessionID: sessionId, status: status, onSuccess: { call in
print("Call rejected")
@@ -250,7 +250,7 @@ func rejectIncomingCall(sessionId: String) {
```objectivec
- (void)rejectIncomingCallWithSessionId:(NSString *)sessionId {
[CometChat rejectCallWithSessionID:sessionId
- status:CometChatCallStatusRejected
+ status:callStatusRejected
onSuccess:^(Call * call) {
NSLog(@"Call rejected");
// Dismiss incoming call UI
@@ -270,7 +270,7 @@ Cancel an outgoing call before it's answered:
```swift
func cancelOutgoingCall(sessionId: String) {
- let status: CometChat.CallStatus = .cancelled
+ let status: CometChat.callStatus = .cancelled
CometChat.rejectCall(sessionID: sessionId, status: status, onSuccess: { call in
print("Call cancelled")
@@ -285,7 +285,7 @@ func cancelOutgoingCall(sessionId: String) {
```objectivec
- (void)cancelOutgoingCallWithSessionId:(NSString *)sessionId {
[CometChat rejectCallWithSessionID:sessionId
- status:CometChatCallStatusCancelled
+ status:callStatusCancelled
onSuccess:^(Call * call) {
NSLog(@"Call cancelled");
// Dismiss outgoing call UI
@@ -306,7 +306,7 @@ After accepting a call (or when your outgoing call is accepted), join the call s
```swift
func joinCallSession(sessionId: String) {
let sessionSettings = CometChatCalls.sessionSettingsBuilder
- .setType(.video)
+ .setSessionType(.video)
.build()
CometChatCalls.joinSession(
@@ -327,7 +327,7 @@ func joinCallSession(sessionId: String) {
```objectivec
- (void)joinCallSessionWithSessionId:(NSString *)sessionId {
SessionSettings *sessionSettings = [[[CometChatCalls sessionSettingsBuilder]
- setType:CallTypeVideo]
+ setSessionType:SessionTypeVideo]
build];
[CometChatCalls joinSessionWithSessionID:sessionId
diff --git a/calls/ios/screen-sharing.mdx b/calls/ios/screen-sharing.mdx
index 337a9836b..410e902cb 100644
--- a/calls/ios/screen-sharing.mdx
+++ b/calls/ios/screen-sharing.mdx
@@ -95,29 +95,63 @@ class CallViewController: UIViewController, ParticipantEventListener {
-## Check Screen Share Status
+## Track Who Is Sharing
-Use the `isPresenting` property on the `Participant` object to check if someone is sharing their screen:
+`Participant` carries **no** screen-share property, and the SDK exposes no getter for one. Track the
+current presenter yourself from the start/stop callbacks.
+
+
+ These callbacks fire only when a share **starts or stops**. A client that joins mid-call cannot
+ discover a share that was already in progress before it joined.
+
```swift
-func onParticipantListChanged(participants: [Participant]) {
- if let presenter = participants.first(where: { $0.isPresenting }) {
- print("\(presenter.name ?? "") is currently sharing their screen")
+// uids of participants currently sharing.
+private var presenterUIDs = Set()
+
+func onParticipantStartedScreenShare(participant: Participant) {
+ guard let uid = participant.uid else { return }
+ DispatchQueue.main.async {
+ self.presenterUIDs.insert(uid)
+ print("\(participant.name ?? uid) started sharing their screen")
+ }
+}
+
+func onParticipantStoppedScreenShare(participant: Participant) {
+ guard let uid = participant.uid else { return }
+ DispatchQueue.main.async {
+ self.presenterUIDs.remove(uid)
}
}
+
+// A participant who leaves while sharing never sends a stop event.
+func onParticipantLeft(participant: Participant) {
+ guard let uid = participant.uid else { return }
+ DispatchQueue.main.async { self.presenterUIDs.remove(uid) }
+}
```
```objectivec
-- (void)onParticipantListChangedWithParticipants:(NSArray *)participants {
- for (Participant *p in participants) {
- if (p.isPresenting) {
- NSLog(@"%@ is currently sharing their screen", p.name);
- break;
- }
- }
+@property (nonatomic, strong) NSMutableSet *presenterUIDs;
+
+- (void)onParticipantStartedScreenShareWithParticipant:(Participant *)participant {
+ NSString *uid = participant.uid;
+ if (!uid) { return; }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self.presenterUIDs addObject:uid];
+ NSLog(@"%@ started sharing their screen", participant.name ?: uid);
+ });
+}
+
+- (void)onParticipantStoppedScreenShareWithParticipant:(Participant *)participant {
+ NSString *uid = participant.uid;
+ if (!uid) { return; }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self.presenterUIDs removeObject:uid];
+ });
}
```
diff --git a/calls/ios/session-settings.mdx b/calls/ios/session-settings.mdx
index 6a8e32243..935cf1346 100644
--- a/calls/ios/session-settings.mdx
+++ b/calls/ios/session-settings.mdx
@@ -19,7 +19,7 @@ These are pre-session configurations that must be set before joining a call. Onc
let sessionSettings = CometChatCalls.sessionSettingsBuilder
.setTitle("Team Meeting")
.setDisplayName("John Doe")
- .setType(.video)
+ .setSessionType(.video)
.setLayout(.tile)
.startAudioMuted(false)
.startVideoPaused(false)
@@ -28,10 +28,10 @@ let sessionSettings = CometChatCalls.sessionSettingsBuilder
```objectivec
-SessionSettings *sessionSettings = [[[[[[[CometChatCalls sessionSettingsBuilder]
+SessionSettings *sessionSettings = [[[[[[[[CometChatCalls sessionSettingsBuilder]
setTitle:@"Team Meeting"]
setDisplayName:@"John Doe"]
- setType:CallTypeVideo]
+ setSessionType:SessionTypeVideo]
setLayout:LayoutTypeTile]
startAudioMuted:NO]
startVideoPaused:NO]
@@ -90,32 +90,32 @@ Sets the display name that will be shown to other participants in the call. This
### Session Type
-**Method:** `setType(_ type: CallType)`
+**Method:** `setSessionType(_ sessionType: SessionType)`
-Defines the type of call session. Choose `.video` for video calls with camera enabled, or `.audio` for audio-only calls. This setting determines whether video streaming is enabled by default.
+Defines the type of call session. Choose `.video` for video calls with camera enabled, or `.voice` for audio-only calls. This setting determines whether video streaming is enabled by default.
```swift
-.setType(.video)
+.setSessionType(.video)
```
```objectivec
-[builder setType:CallTypeVideo]
+[builder setSessionType:SessionTypeVideo]
```
| Parameter | Type | Default |
|-----------|------|---------|
-| `type` | CallType | .video |
+| `sessionType` | SessionType | .video |
-
+
| Value | Description |
|-------|-------------|
| `.video` | Video call with camera enabled |
-| `.audio` | Audio-only call |
+| `.voice` | Audio-only call |
### Layout Mode
@@ -220,7 +220,7 @@ Controls whether the camera is turned off when joining the session. Set to `true
### Audio Mode
-**Method:** `setAudioMode(_ audioMode: AudioModeType)`
+**Method:** `setAudioMode(_ audioMode: AudioMode)`
Sets the initial audio output device for the call. Options include `.speaker` for loudspeaker, `.earpiece` for phone earpiece, `.bluetooth` for connected Bluetooth devices, or `.headphones` for wired headphones.
@@ -232,16 +232,16 @@ Sets the initial audio output device for the call. Options include `.speaker` fo
```objectivec
-[builder setAudioMode:AudioModeTypeSpeaker]
+[builder setAudioMode:AudioModeSpeaker]
```
| Parameter | Type | Default |
|-----------|------|---------|
-| `audioMode` | AudioModeType | .speaker |
+| `audioMode` | AudioMode | .speaker |
-
+
| Value | Description |
|-------|-------------|
| `.speaker` | Device loudspeaker |
@@ -303,6 +303,56 @@ Automatically starts recording the session as soon as it begins. When enabled, r
|-----------|------|---------|
| `enabled` | Bool | false |
+### Auto Start Transcription
+
+*Available since v5.0.4*
+
+**Method:** `enableAutoStartTranscription(_ enabled: Bool)`
+
+Automatically starts transcribing the session as soon as it begins. See [Transcription](/calls/ios/transcription) for details.
+
+
+
+```swift
+.enableAutoStartTranscription(true)
+```
+
+
+```objectivec
+[builder enableAutoStartTranscription:YES]
+```
+
+
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `enabled` | Bool | false |
+
+### Caption Language
+
+*Available since v5.0.4*
+
+**Method:** `setCaptionLanguage(_ captionLanguage: String)`
+
+Sets the language used for transcription and closed captions. See [Transcription](/calls/ios/transcription#caption-language) for the full list of supported codes.
+
+
+
+```swift
+.setCaptionLanguage("en-US")
+```
+
+
+```objectivec
+[builder setCaptionLanguage:@"en-US"]
+```
+
+
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `captionLanguage` | String | en-US |
+
### Hide Control Panel
**Method:** `hideControlPanel(_ hidden: Bool)`
@@ -487,6 +537,56 @@ Hides the recording start/stop button from the control panel. Set to `false` to
|-----------|------|---------|
| `enabled` | Bool | true |
+### Hide Transcription Button
+
+*Available since v5.0.4*
+
+**Method:** `hideTranscriptionButton(_ hidden: Bool)`
+
+Hides the transcription start/stop item from the control panel's **More** menu. Set to `false` to show it, allowing users to manually control session transcription.
+
+
+
+```swift
+.hideTranscriptionButton(false)
+```
+
+
+```objectivec
+[builder hideTranscriptionButton:NO]
+```
+
+
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `hidden` | Bool | true |
+
+### Hide Closed Caption Button
+
+*Available since v5.0.4*
+
+**Method:** `hideClosedCaptionButton(_ hidden: Bool)`
+
+Hides the closed-caption item from the control panel's **More** menu. Set to `false` to show it, allowing users to toggle the live captions overlay. Captions only appear while transcription is running.
+
+
+
+```swift
+.hideClosedCaptionButton(false)
+```
+
+
+```objectivec
+[builder hideClosedCaptionButton:NO]
+```
+
+
+
+| Parameter | Type | Default |
+|-----------|------|---------|
+| `hidden` | Bool | true |
+
### Hide Audio Mode Button
**Method:** `hideAudioModeButton(_ enabled: Bool)`
@@ -628,12 +728,12 @@ Hides the button that opens the in-call chat interface. Set to `false` to show t
| Enum | Value | Description |
|------|-------|-------------|
-| `CallType` | `.video` | Video call with camera enabled |
-| | `.audio` | Audio-only call |
+| `SessionType` | `.video` | Video call with camera enabled |
+| | `.voice` | Audio-only call |
| `LayoutType` | `.tile` | Grid layout showing all participants equally |
| | `.spotlight` | Focus on active speaker with others in sidebar |
| | `.sidebar` | Main speaker with participants in a sidebar |
-| `AudioModeType` | `.speaker` | Device loudspeaker |
+| `AudioMode` | `.speaker` | Device loudspeaker |
| | `.earpiece` | Phone earpiece |
| | `.bluetooth` | Connected Bluetooth device |
| | `.headphones` | Wired headphones |
diff --git a/calls/ios/setup.mdx b/calls/ios/setup.mdx
index 98c54f2ea..9bc375393 100644
--- a/calls/ios/setup.mdx
+++ b/calls/ios/setup.mdx
@@ -31,7 +31,7 @@ platform :ios, '16.0'
use_frameworks!
target 'YourApp' do
- pod 'CometChatCallsSDK', '~> 5.0.0'
+ pod 'CometChatCallsSDK', '~> 5.0.4'
end
```
@@ -44,7 +44,7 @@ pod install
### Using Swift Package Manager
1. In Xcode, go to **File > Add Package Dependencies**
-2. Enter the repository URL: `https://github.com/cometchat/cometchat-calls-sdk-ios`
+2. Enter the repository URL: `https://github.com/cometchat/calls-sdk-ios`
3. Select the version and add to your target
## Add Permissions
@@ -84,7 +84,7 @@ The `CallAppSettings` class configures the SDK initialization:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `appId` | String | Yes | Your CometChat App ID |
-| `region` | String | Yes | Your app region (`us` or `eu`) |
+| `region` | String | Yes | Your app region — `us`, `eu` or `in` |
@@ -92,11 +92,11 @@ The `CallAppSettings` class configures the SDK initialization:
import CometChatCallsSDK
let appId = "APP_ID" // Replace with your App ID
-let region = "REGION" // Replace with your Region ("us" or "eu")
+let region = "REGION" // Replace with your Region ("us", "eu" or "in")
let callAppSettings = CallAppSettingsBuilder()
- .setAppId(appId)
- .setRegion(region)
+ .set(appID: appId)
+ .set(region: region)
.build()
CometChatCalls(callsAppSettings: callAppSettings, onSuccess: { message in
@@ -111,11 +111,11 @@ CometChatCalls(callsAppSettings: callAppSettings, onSuccess: { message in
@import CometChatCallsSDK;
NSString *appId = @"APP_ID"; // Replace with your App ID
-NSString *region = @"REGION"; // Replace with your Region ("us" or "eu")
+NSString *region = @"REGION"; // Replace with your Region ("us", "eu" or "in")
-CallAppSettings *callAppSettings = [[[CallAppSettingsBuilder alloc] init]
- setAppId:appId]
- setRegion:region]
+CallAppSettings *callAppSettings = [[[[[CallAppSettingsBuilder alloc] init]
+ setWithAppID:appId]
+ setWithRegion:region]
build];
[[CometChatCalls alloc] initWithCallsAppSettings:callAppSettings
diff --git a/calls/ios/transcription.mdx b/calls/ios/transcription.mdx
new file mode 100644
index 000000000..426f67a4b
--- /dev/null
+++ b/calls/ios/transcription.mdx
@@ -0,0 +1,492 @@
+---
+title: "Transcription & Closed Captions"
+sidebarTitle: "Transcription"
+sdk_version: "5.x"
+description: "Use CometChat Calls SDK v5 transcription on iOS to transcribe calls, show live closed captions, and retrieve transcripts after the call."
+---
+
+
+**Available since v5.0.4** — transcription and closed captions require CometChat Calls SDK v5.0.4 or later for iOS. See [Setup](/calls/ios/setup) to install or upgrade.
+
+
+Transcribe call sessions in real time and display live closed captions on screen. Transcripts are stored server-side and can be retrieved after the call using `TranscriptsRequest`.
+
+
+Transcription must be enabled for your CometChat app. Contact support if you need to enable this feature.
+
+
+## How It Works
+
+Transcription and closed captions are two related but separate things:
+
+| Concept | What it does |
+|---------|--------------|
+| **Transcription** | Server-side speech-to-text for the session. Starting it brings a transcriber into the call, which produces the transcript that is stored for later retrieval. |
+| **Closed captions** | The on-screen overlay that renders the live transcript as it arrives. Captions are produced from the running transcription, so they only appear while transcription is active. |
+
+Starting transcription is a prerequisite for captions — turning captions on without an active transcription shows nothing.
+
+## Starting Transcription
+
+### Auto-Start Transcription
+
+Configure transcription to start automatically when the session begins:
+
+
+
+```swift
+let sessionSettings = CometChatCalls.sessionSettingsBuilder
+ .enableAutoStartTranscription(true)
+ .build()
+```
+
+
+```objectivec
+SessionSettings *sessionSettings = [[[CometChatCalls sessionSettingsBuilder]
+ enableAutoStartTranscription:YES]
+ build];
+```
+
+
+
+**Default:** `false`
+
+### Manual Transcription Control
+
+#### Start Transcription
+
+Begin transcribing during an active call:
+
+
+
+```swift
+CallSession.shared.startTranscription()
+```
+
+
+```objectivec
+[[CallSession shared] startTranscription];
+```
+
+
+
+#### Stop Transcription
+
+Stop the current transcription. Any captions currently on screen are cleared:
+
+
+
+```swift
+CallSession.shared.stopTranscription()
+```
+
+
+```objectivec
+[[CallSession shared] stopTranscription];
+```
+
+
+
+## Built-in UI Controls
+
+Both transcription controls live in the control panel's **More** menu, and both are hidden by default.
+
+### Transcription Menu Item
+
+To show the transcription item:
+
+
+
+```swift
+let sessionSettings = CometChatCalls.sessionSettingsBuilder
+ .hideTranscriptionButton(false)
+ .build()
+```
+
+
+```objectivec
+SessionSettings *sessionSettings = [[[CometChatCalls sessionSettingsBuilder]
+ hideTranscriptionButton:NO]
+ build];
+```
+
+
+
+**Default:** `true`
+
+The item toggles between **Start Transcription** and **Stop Transcription** based on the current state.
+
+### Closed Caption Menu Item
+
+To show the closed-caption item:
+
+
+
+```swift
+let sessionSettings = CometChatCalls.sessionSettingsBuilder
+ .hideClosedCaptionButton(false)
+ .build()
+```
+
+
+```objectivec
+SessionSettings *sessionSettings = [[[CometChatCalls sessionSettingsBuilder]
+ hideClosedCaptionButton:NO]
+ build];
+```
+
+
+
+**Default:** `true`
+
+The item toggles between **Show Captions** and **Hide Captions**.
+
+
+Captions are off by default, so the overlay appears only after the user turns them on **and** transcription is running for the session. Turning captions on before transcription starts shows nothing until the transcript begins arriving.
+
+
+## Caption Language
+
+**Method:** `setCaptionLanguage(_ captionLanguage: String)`
+
+Sets the language used for transcription and captions.
+
+
+
+```swift
+let sessionSettings = CometChatCalls.sessionSettingsBuilder
+ .setCaptionLanguage("en-US")
+ .build()
+```
+
+
+```objectivec
+SessionSettings *sessionSettings = [[[CometChatCalls sessionSettingsBuilder]
+ setCaptionLanguage:@"en-US"]
+ build];
+```
+
+
+
+**Default:** `en-US`
+
+
+| Code | Language |
+|------|----------|
+| `en-US` | English (United States) |
+| `de-DE` | German (Germany) |
+| `en-GB` | English (United Kingdom) |
+| `es-ES` | Spanish (Spain) |
+| `fr-FR` | French (France) |
+| `hi-IN` | Hindi (India) |
+| `hu-HU` | Hungarian (Hungary) |
+| `it-IT` | Italian (Italy) |
+| `ja-JP` | Japanese (Japan) |
+| `ko-KR` | Korean (South Korea) |
+| `lt-LT` | Lithuanian (Lithuania) |
+| `ms-MY` | Malay (Malaysia) |
+| `nl-NL` | Dutch (Netherlands) |
+| `pt-PT` | Portuguese (Portugal) |
+| `ru-RU` | Russian (Russia) |
+| `sv-SE` | Swedish (Sweden) |
+| `tr-TR` | Turkish (Turkey) |
+| `zh` | Chinese Mandarin (Simplified, China) |
+| `zh-TW` | Chinese Mandarin (Traditional, Taiwan) |
+
+
+## Retrieving Transcripts
+
+After a call, use `TranscriptsRequest` to list the transcript artifacts for a session. Each record is a **pointer to a downloadable transcript file**, not the transcript text itself.
+
+
+The SDK must be initialized with `CometChatCalls.init()` and a user must be logged in. The auth token is read from the logged-in user at fetch time, so it automatically tracks re-logins — there is no `set(authToken:)` on this builder.
+
+
+### Building a Request
+
+
+
+```swift
+let request = TranscriptsRequest.TranscriptsBuilder()
+ .set(sessionId: "v1.us.2547167fe69871fd.alice") // required
+ .set(limit: 10) // optional
+ .build()
+
+request.fetchNext(onSuccess: { transcripts in
+ for transcript in transcripts {
+ print(transcript.tid, transcript.transcriptUrl)
+ }
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+
+```objectivec
+TranscriptsBuilder *builder = [[TranscriptsBuilder alloc] init];
+TranscriptsRequest *request = [[[builder
+ setWithSessionId:@"v1.us.2547167fe69871fd.alice"]
+ setWithLimit:10]
+ build];
+
+[request fetchNextOnSuccess:^(NSArray * transcripts) {
+ for (Transcript *transcript in transcripts) {
+ NSLog(@"%@ %@", transcript.tid, transcript.transcriptUrl);
+ }
+} onError:^(CometChatCallException * error) {
+ NSLog(@"Error: %@", error.errorDescription);
+}];
+```
+
+
+
+| Method | Required | Description |
+|--------|----------|-------------|
+| `set(sessionId: String)` | Yes | The session ID whose transcripts to fetch. A missing or empty value fails the fetch with `ERROR_SESSION_ID_NILL`. |
+| `set(limit: Int)` | No | Page size. Defaults to `30` and is clamped to the range `1`–`1000`. |
+| `build()` | Yes | Returns a `TranscriptsRequest`. |
+
+
+Callbacks are delivered on the main queue, so it is safe to update UI directly from them.
+
+
+### Paginating
+
+A `TranscriptsRequest` is a stateful cursor. Create one per session ID and drive it with `fetchNext` and `fetchPrevious`:
+
+
+
+```swift
+let request = TranscriptsRequest.TranscriptsBuilder()
+ .set(sessionId: sessionId)
+ .set(limit: 10)
+ .build()
+
+// Next page
+request.fetchNext(onSuccess: { transcripts in
+ // [Transcript] — empty once the last page has been consumed
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+
+// Previous page
+request.fetchPrevious(onSuccess: { transcripts in
+ // [Transcript] — empty on a fresh request, or when already at the first page
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+
+```objectivec
+// Next page
+[request fetchNextOnSuccess:^(NSArray * transcripts) {
+ // Empty once the last page has been consumed
+} onError:^(CometChatCallException * error) {
+ NSLog(@"Error: %@", error.errorDescription);
+}];
+
+// Previous page
+[request fetchPreviousOnSuccess:^(NSArray * transcripts) {
+ // Empty on a fresh request, or when already at the first page
+} onError:^(CometChatCallException * error) {
+ NSLog(@"Error: %@", error.errorDescription);
+}];
+```
+
+
+
+- `fetchNext` delivers the next page, or an empty array when there are no more pages. A session with no transcripts delivers an empty array.
+- `fetchPrevious` delivers the previous page, or an empty array when already on the first page. It never requests a page below `1`.
+- The cursor is committed only from a successful response, so a failed fetch can simply be retried.
+- Only one fetch may be in flight per request instance. Calling `fetchNext` or `fetchPrevious` while another is pending fails with `ERROR_REQUEST_IN_PROGRESS`; the original call is unaffected and still completes.
+
+### Transcript Properties
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `tid` | String | Transcript ID |
+| `mid` | String | Meeting ID |
+| `roomName` | String | Room name of the meeting |
+| `startTime` | Int | Meeting start time, in epoch **seconds** |
+| `endTime` | Int | Meeting end time, in epoch **seconds** |
+| `url` | String | Meeting URL |
+| `transcriptDate` | String | Transcript date |
+| `transcriptUrl` | String | URL of the downloadable transcript JSON |
+| `metaData` | [String: Any] | The raw server entry, so fields not modelled above are still reachable |
+
+
+The server omits keys whose value is empty, so sparse records are normal and should not be treated as an error. Missing string fields arrive as `""` and missing numbers as `0`. A single entry that fails to parse is skipped rather than failing the whole page.
+
+
+### Reading the Transcript Content
+
+`transcriptUrl` points at the transcript file. Fetch it yourself to read the actual utterances:
+
+
+
+```swift
+request.fetchNext(onSuccess: { transcripts in
+ guard let transcript = transcripts.first,
+ let url = URL(string: transcript.transcriptUrl),
+ !transcript.transcriptUrl.isEmpty else { return }
+
+ URLSession.shared.dataTask(with: url) { data, _, _ in
+ guard let data = data else { return }
+ let content = try? JSONSerialization.jsonObject(with: data)
+ print(content ?? "")
+ }.resume()
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+
+```objectivec
+[request fetchNextOnSuccess:^(NSArray * transcripts) {
+ Transcript *transcript = transcripts.firstObject;
+ if (transcript.transcriptUrl.length == 0) { return; }
+
+ NSURL *url = [NSURL URLWithString:transcript.transcriptUrl];
+ [[[NSURLSession sharedSession] dataTaskWithURL:url
+ completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ if (data == nil) { return; }
+ id content = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ NSLog(@"%@", content);
+ }] resume];
+} onError:^(CometChatCallException * error) {
+ NSLog(@"Error: %@", error.errorDescription);
+}];
+```
+
+
+
+### Error Handling
+
+Failures are delivered to the `onError` closure as a `CometChatCallException` carrying an `errorCode`:
+
+| Condition | `errorCode` |
+|-----------|-------------|
+| `CometChatCalls.init()` was not called | `INIT_NOT_CALLED` |
+| No logged-in user / empty auth token | `ERROR_NILL_AUTH_TOKEN` |
+| `sessionId` missing or empty | `ERROR_SESSION_ID_NILL` |
+| A fetch is already in flight | `ERROR_REQUEST_IN_PROGRESS` |
+| No network | `ERROR_INTERNET_UNAVAILABLE` |
+| Missing or malformed response | `ERROR_JSON_EXCEPTION` |
+| Server-side API error | The server's own code |
+
+## Transcripts in Call Logs
+
+Call logs can be filtered to transcribed calls, which also attaches each call's transcripts to the log:
+
+
+
+```swift
+let callLogRequest = CallLogsRequest.CallLogsBuilder()
+ .set(limit: 30)
+ .set(hasTranscriptions: true)
+ .build()
+
+callLogRequest.fetchNext(onSuccess: { callLogs in
+ for callLog in callLogs {
+ for transcript in callLog.transcriptions {
+ print(transcript.tid, transcript.transcriptUrl)
+ }
+ }
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+
+```objectivec
+CallLogsBuilder *builder = [[CallLogsBuilder alloc] init];
+CallLogsRequest *callLogRequest = [[[builder
+ setWithLimit:30]
+ setWithHasTranscriptions:YES]
+ build];
+
+[callLogRequest fetchNextOnSuccess:^(NSArray * callLogs) {
+ for (CallLog *callLog in callLogs) {
+ for (Transcript *transcript in callLog.transcriptions) {
+ NSLog(@"%@ %@", transcript.tid, transcript.transcriptUrl);
+ }
+ }
+} onError:^(CometChatCallException * error) {
+ NSLog(@"Error: %@", error.errorDescription);
+}];
+```
+
+
+
+`transcriptions` is an empty array when the server omitted transcripts, so it never needs a nil check. Passing `false` leaves the list unfiltered, exactly as if the filter had never been set — and the server then omits the transcripts.
+
+## Complete Example
+
+
+
+```swift
+// 1. Join a session with transcription enabled
+let sessionSettings = CometChatCalls.sessionSettingsBuilder
+ .enableAutoStartTranscription(true)
+ .hideTranscriptionButton(false)
+ .hideClosedCaptionButton(false)
+ .setCaptionLanguage("en-US")
+ .build()
+
+// 2. Control transcription during the call
+CallSession.shared.startTranscription()
+CallSession.shared.stopTranscription()
+
+// 3. Retrieve transcripts after the call
+let request = TranscriptsRequest.TranscriptsBuilder()
+ .set(sessionId: sessionId)
+ .set(limit: 10)
+ .build()
+
+request.fetchNext(onSuccess: { transcripts in
+ for transcript in transcripts {
+ print(transcript.transcriptUrl)
+ }
+}, onError: { error in
+ print("Error: \(error?.errorDescription ?? "")")
+})
+```
+
+
+```objectivec
+// 1. Join a session with transcription enabled
+SessionSettings *sessionSettings = [[[[[[CometChatCalls sessionSettingsBuilder]
+ enableAutoStartTranscription:YES]
+ hideTranscriptionButton:NO]
+ hideClosedCaptionButton:NO]
+ setCaptionLanguage:@"en-US"]
+ build];
+
+// 2. Control transcription during the call
+[[CallSession shared] startTranscription];
+[[CallSession shared] stopTranscription];
+
+// 3. Retrieve transcripts after the call
+TranscriptsBuilder *builder = [[TranscriptsBuilder alloc] init];
+TranscriptsRequest *request = [[[builder
+ setWithSessionId:sessionId]
+ setWithLimit:10]
+ build];
+
+[request fetchNextOnSuccess:^(NSArray * transcripts) {
+ for (Transcript *transcript in transcripts) {
+ NSLog(@"%@", transcript.transcriptUrl);
+ }
+} onError:^(CometChatCallException * error) {
+ NSLog(@"Error: %@", error.errorDescription);
+}];
+```
+
+
+
+## Related Documentation
+
+- [SessionSettingsBuilder](/calls/ios/session-settings)
+- [Call Actions](/calls/ios/actions)
+- [Call Logs](/calls/ios/call-logs)
diff --git a/calls/ios/voip-calling.mdx b/calls/ios/voip-calling.mdx
index 6cd30beff..aac2fbf80 100644
--- a/calls/ios/voip-calling.mdx
+++ b/calls/ios/voip-calling.mdx
@@ -39,6 +39,14 @@ Before implementing VoIP calling, ensure you have:
- [CometChat Chat SDK](/sdk/ios/overview) and [Calls SDK](/calls/ios/setup) integrated
- Apple Push Notification service (APNs) VoIP certificate configured
- [Push notifications enabled](/notifications/push-overview) in CometChat Dashboard
+
+
+ **Most apps should not implement this by hand.** The
+ [iOS Push Notifications SDK](/notifications/ios-push-notifications-sdk)
+ (`CometChatPushNotifications`) already does everything on this page — PushKit registration,
+ CallKit, accept/decline, missed-call handling — behind three integration steps. Follow this guide
+ only if you need direct control over `PKPushRegistry` and `CXProvider`.
+
- iOS 10.0+ for CallKit support
@@ -567,7 +575,7 @@ extension CallManager: CXProviderDelegate {
return;
}
- [CometChat rejectCallWithSessionID:_activeSessionId status:CometChatCallStatusRejected onSuccess:^(Call * call) {
+ [CometChat rejectCallWithSessionID:_activeSessionId status:callStatusRejected onSuccess:^(Call * call) {
[action fulfill];
} onError:^(CometChatException * error) {
[action fulfill];
diff --git a/calls/javascript/actions.mdx b/calls/javascript/actions.mdx
index 75a3742e0..38ac1267e 100644
--- a/calls/javascript/actions.mdx
+++ b/calls/javascript/actions.mdx
@@ -91,6 +91,30 @@ Stops the current recording. The recording is saved and accessible via the dashb
CometChatCalls.stopRecording();
```
+## Transcription
+
+*Available since v5.0.5*
+
+### Start Transcription
+
+Begins server-side transcription of the call. This also enables the live closed captions overlay.
+
+```javascript
+CometChatCalls.startTranscription();
+```
+
+
+Transcription requires the feature to be enabled for your CometChat app.
+
+
+### Stop Transcription
+
+Stops the current transcription and clears any captions currently on screen. The transcript is saved and can be retrieved with [`TranscriptRequestBuilder`](/calls/javascript/transcription#retrieving-transcripts).
+
+```javascript
+CometChatCalls.stopTranscription();
+```
+
## Participant Management
### Mute Participant
@@ -184,6 +208,10 @@ Ends your participation and disconnects gracefully. The call continues for other
CometChatCalls.leaveSession();
```
+
+`leaveSession()` takes no session ID. `CometChatCalls` is a singleton that tracks one active session at a time, so this leaves whichever session is currently in progress. The same applies to every action on this page. See [Single Active Session](/calls/javascript/overview#single-active-session).
+
+
## UI Controls
### Show Settings Dialog
diff --git a/calls/javascript/call-logs.mdx b/calls/javascript/call-logs.mdx
index b88098dc3..48720ee7b 100644
--- a/calls/javascript/call-logs.mdx
+++ b/calls/javascript/call-logs.mdx
@@ -46,8 +46,32 @@ See the [Get Call API](/calls/api/get-call) documentation for full details.
| `participants` | Array | List of participants who joined |
| `recordingUrl` | String | URL to the call recording (if recorded) |
+## Transcripts
+
+*Available since v5.0.5*
+
+Use `CallLogRequestBuilder` to fetch only calls that were transcribed. Opting in also makes the server attach each call's transcripts to the log:
+
+```javascript
+const callLogRequest = new CometChatCalls.CallLogRequestBuilder()
+ .setLimit(30)
+ .setHasTranscriptions(true)
+ .build();
+
+const callLogs = await callLogRequest.fetchNext();
+
+callLogs.forEach((callLog) => {
+ callLog.getTranscriptions().forEach((transcription) => {
+ console.log(transcription.getTid(), transcription.getTranscriptURL());
+ });
+});
+```
+
+`getTranscriptions()` returns an empty array when the server omitted transcripts, so it never needs a null check. To page through a session's transcripts directly, use [`TranscriptRequestBuilder`](/calls/javascript/transcription#retrieving-transcripts).
+
## Related Documentation
+- [Transcription](/calls/javascript/transcription)
- [List Calls API](/calls/api/list-calls)
- [Get Call API](/calls/api/get-call)
diff --git a/calls/javascript/join-session.mdx b/calls/javascript/join-session.mdx
index 237c25cd5..a0a0ca794 100644
--- a/calls/javascript/join-session.mdx
+++ b/calls/javascript/join-session.mdx
@@ -135,6 +135,21 @@ try {
All participants joining the same call must use the same session ID.
+## One Session at a Time
+
+`CometChatCalls` is a singleton that holds a single active session. Joining a second session while one is in progress is not supported - leave the current session first, wait for `onSessionLeft`, then join the next one.
+
+```javascript
+CometChatCalls.addEventListener("onSessionLeft", async () => {
+ const { token } = await CometChatCalls.generateToken(nextSessionId);
+ await CometChatCalls.joinSession(token, callSettings, container);
+});
+
+CometChatCalls.leaveSession();
+```
+
+This is also why `leaveSession()` and the other [actions](/calls/javascript/actions) take no session ID - they always apply to the session that is currently active. See [Single Active Session](/calls/javascript/overview#single-active-session) for the full behavior.
+
## Error Handling
Common errors when joining a session:
diff --git a/calls/javascript/migration-guide-v5.mdx b/calls/javascript/migration-guide-v5.mdx
index c6ab13382..eaf686912 100644
--- a/calls/javascript/migration-guide-v5.mdx
+++ b/calls/javascript/migration-guide-v5.mdx
@@ -209,6 +209,20 @@ CometChatCalls.generateToken(sessionId).then((token) => {
Static methods remain on `CometChatCalls` but some have been renamed.
+
+**Boolean toggles are now paired methods.** In v4, `muteAudio()` took a boolean: `muteAudio(true)` muted the mic and `muteAudio(false)` unmuted it. In v5 these are two separate, argument-free methods — replace `muteAudio(true)` with `muteAudio()` and `muteAudio(false)` with `unmuteAudio()`. The same split applies to `pauseVideo(true)` / `pauseVideo(false)`, which become `pauseVideo()` and `resumeVideo()`.
+
+
+```javascript
+// v4
+CometChatCalls.muteAudio(true); // mute
+CometChatCalls.muteAudio(false); // unmute
+
+// v5
+CometChatCalls.muteAudio(); // mute
+CometChatCalls.unmuteAudio(); // unmute
+```
+
| v4 Method | v5 Method |
|---|---|
| `CometChatCalls.endSession()` | `CometChatCalls.leaveSession()` |
@@ -289,6 +303,24 @@ CometChatCalls.addEventListener("onCallLayoutChanged", (layoutType) => { });
`addEventListener()` returns an unsubscribe function. Call it to remove the listener. You can also pass an `AbortSignal` for cleanup.
+### New Events Require `addEventListener()`
+
+The events introduced in v5 are only delivered through `addEventListener()`. None of the v4 registration paths — `setCallListener()` / `setCallEventListener()` on `CallSettingsBuilder` and `CometChatCalls.addCallEventListener(name, listener)` — will ever fire them, because `OngoingCallListener` only carries the legacy v4 event set. Events such as `onSessionJoined`, `onConnectionLost`, `onLeaveSessionButtonClicked`, and `onCallLayoutChanged` are unreachable from a v4 listener no matter how it is registered.
+
+You don't have to migrate everything at once. `addEventListener()` works alongside an existing v4 listener without breaking it — keep your current `OngoingCallListener` for the legacy events and add `addEventListener()` subscriptions for the new ones.
+
+```javascript
+// Existing v4 listener keeps working
+CometChatCalls.addCallEventListener("MY_LISTENER", new CometChatCalls.OngoingCallListener({
+ onCallEnded: () => { },
+ onUserJoined: (user) => { },
+}));
+
+// Add v5-only events alongside it
+CometChatCalls.addEventListener("onConnectionLost", () => { });
+CometChatCalls.addEventListener("onCallLayoutChanged", (layoutType) => { });
+```
+
### Event Mapping
| v4 Event | v5 Event |
diff --git a/calls/javascript/overview.mdx b/calls/javascript/overview.mdx
index c4efeee79..6d460c87f 100644
--- a/calls/javascript/overview.mdx
+++ b/calls/javascript/overview.mdx
@@ -142,6 +142,40 @@ The SDK is organized around these core components:
| `CallSettings` | Configuration object for individual call sessions |
| `addEventListener` | Method to register event listeners for session, participant, media, and UI events |
+## Single Active Session
+
+`CometChatCalls` is a singleton, and it tracks one active call session at a time. Every session method acts on that current session, which is why methods like `leaveSession()` take no session ID:
+
+```javascript
+// Joins and becomes the active session
+await CometChatCalls.joinSession(callToken, callSettings, container);
+
+// Acts on whatever session is currently active - no session ID needed
+CometChatCalls.leaveSession();
+```
+
+| Behavior | Detail |
+|----------|--------|
+| One session at a time | The SDK does not support being connected to two sessions simultaneously. Leave the current session before joining another. |
+| Session methods take no session ID | `leaveSession()` and the other [actions](/calls/javascript/actions) apply to the active session only. |
+| Actions apply to the active session | `muteAudio()`, `pauseVideo()`, `startScreenShare()`, and similar methods target the session currently in progress. |
+| Events are not session-scoped | Listeners registered with `addEventListener` fire for the active session. |
+
+To move a user from one call to another, leave the current session, wait for the `onSessionLeft` event, then generate a token for the new session and join:
+
+```javascript
+CometChatCalls.addEventListener("onSessionLeft", async () => {
+ const { token } = await CometChatCalls.generateToken(nextSessionId);
+ await CometChatCalls.joinSession(token, callSettings, container);
+});
+
+CometChatCalls.leaveSession();
+```
+
+
+The limit applies per SDK instance. Separate browser tabs or windows each load their own instance, so they can hold separate sessions independently.
+
+
## Sample App
diff --git a/calls/javascript/recording.mdx b/calls/javascript/recording.mdx
index c49652b21..9b7da976d 100644
--- a/calls/javascript/recording.mdx
+++ b/calls/javascript/recording.mdx
@@ -42,6 +42,13 @@ Stop the current recording:
CometChatCalls.stopRecording();
```
+
+**A recording runs until it is stopped.** Whether it was started manually or automatically through `autoStartRecording`, it keeps running until someone stops it. If nobody does, it stops on its own when either:
+
+- Everyone leaves the session — the recording ends about a minute later.
+- Everyone in the session stays muted for 10 minutes.
+
+
## Recording Events
### Recording Started
diff --git a/calls/javascript/session-settings.mdx b/calls/javascript/session-settings.mdx
index 61cb0e078..daca757d2 100644
--- a/calls/javascript/session-settings.mdx
+++ b/calls/javascript/session-settings.mdx
@@ -97,6 +97,34 @@ autoStartRecording: true
**Default:** `false`
+### Auto Start Transcription
+
+*Available since v5.0.5*
+
+**Property:** `autoStartTranscription`
+
+Automatically starts transcribing the session as soon as it begins. See [Transcription](/calls/javascript/transcription) for details.
+
+```javascript
+autoStartTranscription: true
+```
+
+**Default:** `false`
+
+### Caption Language
+
+*Available since v5.0.5*
+
+**Property:** `captionLanguage`
+
+Sets the language used for transcription and closed captions. See [Transcription](/calls/javascript/transcription) for the full list of supported codes.
+
+```javascript
+captionLanguage: "en-US"
+```
+
+**Default:** `en-US`
+
### Idle Timeout Period Before Prompt
**Property:** `idleTimeoutPeriodBeforePrompt`
@@ -183,6 +211,34 @@ hideRecordingButton: true
**Default:** `true`
+### Hide Transcription Button
+
+*Available since v5.0.5*
+
+**Property:** `hideTranscriptionButton`
+
+Hides the transcription start/stop item from the control panel's **More** menu.
+
+```javascript
+hideTranscriptionButton: false
+```
+
+**Default:** `true`
+
+### Hide Closed Caption Button
+
+*Available since v5.0.5*
+
+**Property:** `hideClosedCaptionButton`
+
+Hides the closed-caption (CC) button from the control panel. Even when set to `false`, the button only appears while transcription is running.
+
+```javascript
+hideClosedCaptionButton: false
+```
+
+**Default:** `true`
+
### Hide Screen Sharing Button
**Property:** `hideScreenSharingButton`
@@ -241,6 +297,8 @@ const callSettings = {
startAudioMuted: false,
startVideoPaused: false,
autoStartRecording: false,
+ autoStartTranscription: false,
+ captionLanguage: "en-US",
// Timeout settings
idleTimeoutPeriodBeforePrompt: 60000,
@@ -252,6 +310,8 @@ const callSettings = {
hideToggleAudioButton: false,
hideToggleVideoButton: false,
hideRecordingButton: true,
+ hideTranscriptionButton: true,
+ hideClosedCaptionButton: true,
hideScreenSharingButton: false,
hideChangeLayoutButton: false,
hideVirtualBackgroundButton: false,
@@ -267,6 +327,8 @@ const callSettings = {
| `startAudioMuted` | Boolean | `false` | Start with microphone muted |
| `startVideoPaused` | Boolean | `false` | Start with camera off |
| `autoStartRecording` | Boolean | `false` | Auto-start recording |
+| `autoStartTranscription` | Boolean | `false` | Auto-start transcription |
+| `captionLanguage` | String | `en-US` | Transcription / caption language code |
| `idleTimeoutPeriodBeforePrompt` | Number | `60000` | Idle timeout before prompt (ms) |
| `idleTimeoutPeriodAfterPrompt` | Number | `180000` | Idle timeout after prompt (ms) |
| `hideControlPanel` | Boolean | `false` | Hide control panel |
@@ -274,6 +336,8 @@ const callSettings = {
| `hideToggleAudioButton` | Boolean | `false` | Hide audio toggle |
| `hideToggleVideoButton` | Boolean | `false` | Hide video toggle |
| `hideRecordingButton` | Boolean | `true` | Hide recording button |
+| `hideTranscriptionButton` | Boolean | `true` | Hide transcription button |
+| `hideClosedCaptionButton` | Boolean | `true` | Hide closed caption button |
| `hideScreenSharingButton` | Boolean | `false` | Hide screen share button |
| `hideChangeLayoutButton` | Boolean | `false` | Hide layout change button |
| `hideVirtualBackgroundButton` | Boolean | `false` | Hide virtual background button |
diff --git a/calls/javascript/setup.mdx b/calls/javascript/setup.mdx
index 42b91f1e6..144cb5617 100644
--- a/calls/javascript/setup.mdx
+++ b/calls/javascript/setup.mdx
@@ -27,12 +27,12 @@ Install the CometChat Calls SDK using npm or yarn:
```bash
-npm install @cometchat/calls-sdk-javascript@5.0.0
+npm install @cometchat/calls-sdk-javascript@5.0.5
```
```bash
-yarn add @cometchat/calls-sdk-javascript@5.0.0
+yarn add @cometchat/calls-sdk-javascript@5.0.5
```
diff --git a/calls/javascript/transcription.mdx b/calls/javascript/transcription.mdx
new file mode 100644
index 000000000..24ee75701
--- /dev/null
+++ b/calls/javascript/transcription.mdx
@@ -0,0 +1,306 @@
+---
+title: "Transcription & Closed Captions"
+sidebarTitle: "Transcription"
+sdk_version: "5.x"
+description: "Use CometChat Calls SDK v5 transcription on JavaScript to transcribe calls, show live closed captions, and retrieve transcripts after the call."
+---
+
+
+**Available since v5.0.5** — transcription and closed captions require CometChat Calls SDK v5.0.5 or later for JavaScript. See [Setup](/calls/javascript/setup) to install or upgrade.
+
+
+Transcribe call sessions in real time and display live closed captions on screen. Transcripts are stored server-side and can be retrieved after the call using `TranscriptRequestBuilder`.
+
+
+Transcription must be enabled for your CometChat app. Contact support if you need to enable this feature.
+
+
+## How It Works
+
+Transcription and closed captions are two related but separate things:
+
+| Concept | What it does |
+|---------|--------------|
+| **Transcription** | Server-side speech-to-text for the session. Starting it brings a transcriber into the call, which produces the transcript that is stored for later retrieval. |
+| **Closed captions** | The on-screen overlay that renders the live transcript as it arrives. Captions are produced from the running transcription, so they only appear while transcription is active. |
+
+Starting transcription is a prerequisite for captions — toggling captions on without an active transcription shows nothing.
+
+## Starting Transcription
+
+### Auto-Start Transcription
+
+Configure transcription to start automatically when the session begins:
+
+```javascript
+const callSettings = {
+ autoStartTranscription: true,
+ // ... other settings
+};
+
+await CometChatCalls.joinSession(callToken, callSettings, container);
+```
+
+**Default:** `false`
+
+### Manual Transcription Control
+
+#### Start Transcription
+
+Begin transcribing during an active call:
+
+```javascript
+CometChatCalls.startTranscription();
+```
+
+#### Stop Transcription
+
+Stop the current transcription. Any captions currently on screen are cleared:
+
+```javascript
+CometChatCalls.stopTranscription();
+```
+
+## Built-in UI Controls
+
+### Transcription Button
+
+By default, the transcription button in the control panel's **More** menu is hidden. To show it:
+
+```javascript
+const callSettings = {
+ hideTranscriptionButton: false,
+ // ... other settings
+};
+```
+
+**Default:** `true`
+
+The menu item toggles between **Start Transcription** and **Stop Transcription** based on the current state.
+
+### Closed Caption Button
+
+By default, the closed-caption (CC) button in the control panel is hidden. To show it:
+
+```javascript
+const callSettings = {
+ hideClosedCaptionButton: false,
+ // ... other settings
+};
+```
+
+**Default:** `true`
+
+
+Even with `hideClosedCaptionButton: false`, the CC button only appears once transcription is running for the session, because captions are generated from the live transcript.
+
+
+### Closed Caption Settings
+
+When the CC button is visible, the settings dialog gains a **Closed Caption** tab where the user can pick the caption language and enable or disable the on-screen captions. The gear icon on the captions overlay opens the dialog directly on that tab.
+
+## Caption Language
+
+**Property:** `captionLanguage`
+
+Sets the language used for transcription and captions.
+
+```javascript
+const callSettings = {
+ captionLanguage: "en-US",
+ // ... other settings
+};
+```
+
+**Default:** `en-US`
+
+
+| Code | Language |
+|------|----------|
+| `en-US` | English (United States) |
+| `de-DE` | German (Germany) |
+| `en-GB` | English (United Kingdom) |
+| `es-ES` | Spanish (Spain) |
+| `fr-FR` | French (France) |
+| `hi-IN` | Hindi (India) |
+| `hu-HU` | Hungarian (Hungary) |
+| `it-IT` | Italian (Italy) |
+| `ja-JP` | Japanese (Japan) |
+| `ko-KR` | Korean (South Korea) |
+| `lt-LT` | Lithuanian (Lithuania) |
+| `ms-MY` | Malay (Malaysia) |
+| `nl-NL` | Dutch (Netherlands) |
+| `pt-PT` | Portuguese (Portugal) |
+| `ru-RU` | Russian (Russia) |
+| `sv-SE` | Swedish (Sweden) |
+| `tr-TR` | Turkish (Turkey) |
+| `zh` | Chinese Mandarin (Simplified, China) |
+| `zh-TW` | Chinese Mandarin (Traditional, Taiwan) |
+
+
+## Retrieving Transcripts
+
+After a call, use `TranscriptRequestBuilder` to list the transcript artifacts for a session. Each record is a **pointer to a downloadable transcript file**, not the transcript text itself.
+
+
+The SDK must be initialized with `CometChatCalls.init()` and a user must be logged in. The auth token is read from the logged-in user at fetch time, so it automatically tracks re-logins.
+
+
+### Building a Request
+
+```javascript
+const request = new CometChatCalls.TranscriptRequestBuilder()
+ .setSessionId("v1.us.2547167fe69871fd.alice") // required
+ .setLimit(10) // optional
+ .build();
+
+const transcripts = await request.fetchNext();
+```
+
+| Method | Required | Description |
+|--------|----------|-------------|
+| `setSessionId(sessionId: string)` | Yes | The session ID whose transcripts to fetch. An empty value throws `SESSION_ID_REQUIRED` at `build()`. |
+| `setLimit(limit: number)` | No | Page size. Defaults to `30` and is clamped to the range `1`–`1000`. |
+| `build()` | Yes | Returns a `TranscriptRequest`. Throws synchronously for pre-flight errors. |
+
+### Paginating
+
+A `TranscriptRequest` is a stateful cursor. Create one per session ID and drive it with `fetchNext()` and `fetchPrevious()`:
+
+```javascript
+const request = new CometChatCalls.TranscriptRequestBuilder()
+ .setSessionId(sessionId)
+ .setLimit(10)
+ .build();
+
+let page = await request.fetchNext(); // page 1
+
+while (page.length > 0) {
+ page.forEach((transcript) => console.log(transcript.transcriptUrl));
+ page = await request.fetchNext(); // page 2, 3, ... then [] at the end
+}
+```
+
+- `fetchNext()` resolves the next page, or `[]` when there are no more pages. A session with no transcripts resolves `[]`.
+- `fetchPrevious()` resolves the previous page, or `[]` when already on the first page. It never requests a page below `1`.
+- Only one fetch may be in flight at a time. Calling `fetchNext()` or `fetchPrevious()` while another request is pending rejects with `REQUEST_IN_PROGRESS`; the original call is unaffected and still completes.
+
+### Transcript Properties
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `tid` | String | Transcript ID |
+| `mid` | String | Meeting ID |
+| `roomName` | String | Room name of the meeting |
+| `startTime` | Number | Meeting start time, in epoch **seconds** |
+| `endTime` | Number | Meeting end time, in epoch **seconds** |
+| `url` | String | Meeting URL |
+| `transcriptDate` | String | Transcript date |
+| `transcriptUrl` | String | URL of the downloadable transcript JSON |
+| `metaData` | Object | Arbitrary metadata |
+
+
+Every property is optional. The server omits keys whose value is empty, so sparse records are normal and should not be treated as an error.
+
+
+### Reading the Transcript Content
+
+`transcriptUrl` points at the transcript file. Fetch it yourself to read the actual utterances:
+
+```javascript
+const [transcript] = await request.fetchNext();
+
+if (transcript?.transcriptUrl) {
+ const response = await fetch(transcript.transcriptUrl);
+ const content = await response.json();
+ console.log(content);
+}
+```
+
+### Error Handling
+
+`build()` throws synchronously; `fetchNext()` and `fetchPrevious()` reject. Both surface a `CometChatCallsException` carrying a `code`:
+
+| Condition | Code | Raised by |
+|-----------|------|-----------|
+| `init()` was not called | `NOT_INITIALIZED` | `build()` |
+| `sessionId` missing or empty | `SESSION_ID_REQUIRED` | `build()` |
+| No logged-in user | `NOT_LOGGED_IN` | `fetchNext()` / `fetchPrevious()` |
+| A fetch is already in flight | `REQUEST_IN_PROGRESS` | `fetchNext()` / `fetchPrevious()` |
+| Network failure | `NETWORK_ERROR` | `fetchNext()` / `fetchPrevious()` |
+| Missing or malformed response | `BAD_RESPONSE` | `fetchNext()` / `fetchPrevious()` |
+| Server-side API error | The server's own code (e.g. `AUTH_ERR_EMPTY_APPID`) | `fetchNext()` / `fetchPrevious()` |
+
+```javascript
+try {
+ const transcripts = await request.fetchNext();
+} catch (error) {
+ console.error(error.code, error.message);
+}
+```
+
+## Transcripts in Call Logs
+
+Call logs can be filtered to transcribed calls, which also attaches each call's transcripts to the log:
+
+```javascript
+const callLogRequest = new CometChatCalls.CallLogRequestBuilder()
+ .setLimit(30)
+ .setHasTranscriptions(true)
+ .build();
+
+const callLogs = await callLogRequest.fetchNext();
+
+callLogs.forEach((callLog) => {
+ callLog.getTranscriptions().forEach((transcription) => {
+ console.log(transcription.getTid(), transcription.getTranscriptURL());
+ });
+});
+```
+
+`getTranscriptions()` returns an empty array when the server omitted transcripts, so it never needs a null check.
+
+
+| Method | Returns | Description |
+|--------|---------|-------------|
+| `getTid()` | String | The transcript ID |
+| `getMid()` | String | The meeting ID |
+| `getRoomName()` | String | The room name of the meeting |
+| `getStartTime()` | Number | Meeting start time, in epoch seconds |
+| `getEndTime()` | Number | Meeting end time, in epoch seconds |
+| `getTranscriptDate()` | String | The transcript date |
+| `getTranscriptURL()` | String | URL of the downloadable transcript JSON |
+
+
+## Complete Example
+
+```javascript
+// 1. Join a session with transcription enabled
+const callSettings = {
+ sessionType: "VIDEO",
+ autoStartTranscription: true,
+ hideTranscriptionButton: false,
+ hideClosedCaptionButton: false,
+ captionLanguage: "en-US",
+};
+
+await CometChatCalls.joinSession(callToken, callSettings, container);
+
+// 2. Control transcription during the call
+CometChatCalls.startTranscription();
+CometChatCalls.stopTranscription();
+
+// 3. Retrieve transcripts after the call
+const request = new CometChatCalls.TranscriptRequestBuilder()
+ .setSessionId(sessionId)
+ .setLimit(10)
+ .build();
+
+const transcripts = await request.fetchNext();
+```
+
+## Related Documentation
+
+- [Session Settings](/calls/javascript/session-settings)
+- [Actions](/calls/javascript/actions)
+- [Call Logs](/calls/javascript/call-logs)
diff --git a/calls/react-native/actions.mdx b/calls/react-native/actions.mdx
index 69bbb3759..8852ae5fd 100644
--- a/calls/react-native/actions.mdx
+++ b/calls/react-native/actions.mdx
@@ -65,6 +65,34 @@ CometChatCalls.leaveSession();
The `leaveSession()` method ends the call for the local user. Other participants will remain in the call unless they also leave.
+
+`leaveSession()` takes no session ID. `CometChatCalls` is a singleton that tracks one active session at a time, so this leaves whichever session is currently in progress. The same applies to every action on this page. See [Single Active Session](/calls/react-native/overview#single-active-session).
+
+
+## Transcription
+
+*Available since v5.0.5*
+
+### Start Transcription
+
+Begin server-side transcription of the call. This also enables the live closed captions overlay:
+
+```tsx
+CometChatCalls.startTranscription();
+```
+
+
+Transcription requires the feature to be enabled for your CometChat app.
+
+
+### Stop Transcription
+
+Stop the current transcription and clear any captions currently on screen. The transcript is saved and can be retrieved with [`TranscriptRequestBuilder`](/calls/react-native/transcription#retrieving-transcripts):
+
+```tsx
+CometChatCalls.stopTranscription();
+```
+
## Raise Hand
### Raise Hand
@@ -315,3 +343,4 @@ These methods are deprecated but still available for backward compatibility:
- [Events](/calls/react-native/events) - Listen for call events
- [Participant Management](/calls/react-native/participant-management) - Manage participants
- [Call Layouts](/calls/react-native/call-layouts) - Layout options
+- [Transcription](/calls/react-native/transcription) - Transcription and closed captions
diff --git a/calls/react-native/call-logs.mdx b/calls/react-native/call-logs.mdx
index 406565f2f..81ab5b337 100644
--- a/calls/react-native/call-logs.mdx
+++ b/calls/react-native/call-logs.mdx
@@ -81,6 +81,7 @@ Each call log contains:
| `duration` | number | Call duration in seconds |
| `participants` | array | List of participants |
| `recordings` | array | List of recordings (if any) |
+| `transcriptions` | array | List of transcripts. Only attached when the request opts in with `setHasTranscriptions(true)` |
## Filter by User
@@ -141,6 +142,31 @@ const page1 = await manager.fetchNextPage();
const page2 = await manager.fetchNextPage();
```
+## Transcripts
+
+*Available since v5.0.5*
+
+Use the Calls SDK's `CallLogRequestBuilder` to fetch only calls that were transcribed. Opting in also makes the server attach each call's transcripts to the log:
+
+```tsx
+import { CometChatCalls } from '@cometchat/calls-sdk-react-native';
+
+const callLogRequest = new CometChatCalls.CallLogRequestBuilder()
+ .setLimit(30)
+ .setHasTranscriptions(true)
+ .build();
+
+const callLogs = await callLogRequest.fetchNext();
+
+callLogs.forEach((callLog) => {
+ callLog.getTranscriptions().forEach((transcription) => {
+ console.log(transcription.getTid(), transcription.getTranscriptURL());
+ });
+});
+```
+
+`getTranscriptions()` returns an empty array when the server omitted transcripts, so it never needs a null check. To page through a session's transcripts directly, use [`TranscriptRequestBuilder`](/calls/react-native/transcription#retrieving-transcripts).
+
## Complete Example
```tsx
@@ -301,4 +327,5 @@ export default CallLogsScreen;
## Related Documentation
- [Recording](/calls/react-native/recording) - Access call recordings
+- [Transcription](/calls/react-native/transcription) - Retrieve call transcripts
- [Ringing](/calls/react-native/ringing) - Implement call notifications
diff --git a/calls/react-native/join-session.mdx b/calls/react-native/join-session.mdx
index 1161b8290..fb346dbee 100644
--- a/calls/react-native/join-session.mdx
+++ b/calls/react-native/join-session.mdx
@@ -282,6 +282,21 @@ const styles = StyleSheet.create({
export default CallScreen;
```
+## One Session at a Time
+
+`CometChatCalls` is a singleton that holds a single active session. Render one `CometChatCalls.Component` at a time - joining a second session while one is in progress is not supported. Leave the current session first, wait for `onSessionLeft`, then join the next one.
+
+```tsx
+CometChatCalls.addEventListener('onSessionLeft', async () => {
+ const { token } = await CometChatCalls.generateToken(nextSessionId);
+ setCallToken(token); // Re-renders the component with the new session
+}, { signal });
+
+CometChatCalls.leaveSession();
+```
+
+This is also why `leaveSession()` and the other [actions](/calls/react-native/actions) take no session ID - they always apply to the session that is currently active. See [Single Active Session](/calls/react-native/overview#single-active-session) for the full behavior.
+
## Error Handling
Common errors when joining a session:
diff --git a/calls/react-native/migration-guide-v5.mdx b/calls/react-native/migration-guide-v5.mdx
index 40ddc74e6..174176d61 100644
--- a/calls/react-native/migration-guide-v5.mdx
+++ b/calls/react-native/migration-guide-v5.mdx
@@ -212,6 +212,20 @@ const { token } = await CometChatCalls.generateToken(sessionId);
Static methods remain on `CometChatCalls` but some have been renamed.
+
+**Boolean toggles are now paired methods.** In v4, `muteAudio()` took a boolean: `muteAudio(true)` muted the mic and `muteAudio(false)` unmuted it. In v5 these are two separate, argument-free methods — replace `muteAudio(true)` with `muteAudio()` and `muteAudio(false)` with `unmuteAudio()`. The same split applies to `pauseVideo(true)` / `pauseVideo(false)`, which become `pauseVideo()` and `resumeVideo()`.
+
+
+```javascript
+// v4
+CometChatCalls.muteAudio(true); // mute
+CometChatCalls.muteAudio(false); // unmute
+
+// v5
+CometChatCalls.muteAudio(); // mute
+CometChatCalls.unmuteAudio(); // unmute
+```
+
| v4 Method | v5 Method |
|---|---|
| `CometChatCalls.endSession()` | `CometChatCalls.leaveSession()` |
@@ -290,6 +304,24 @@ CometChatCalls.addEventListener("onPictureInPictureLayoutDisabled", () => { });
`addEventListener()` returns an unsubscribe function. Call it to remove the listener.
+### New Events Require `addEventListener()`
+
+The events introduced in v5 are only delivered through `addEventListener()`. Neither of the v4 registration paths — `setCallEventListener()` on `CallSettingsBuilder` and `CometChatCalls.addCallEventListener(name, listener)` — will ever fire them, because `OngoingCallListener` only carries the legacy v4 event set. Events such as `onSessionJoined`, `onConnectionLost`, `onLeaveSessionButtonClicked`, and `onCallLayoutChanged` are unreachable from a v4 listener no matter how it is registered.
+
+You don't have to migrate everything at once. `addEventListener()` works alongside an existing v4 listener without breaking it — keep your current `OngoingCallListener` for the legacy events and add `addEventListener()` subscriptions for the new ones.
+
+```javascript
+// Existing v4 listener keeps working
+CometChatCalls.addCallEventListener("MY_LISTENER", new CometChatCalls.OngoingCallListener({
+ onCallEnded: () => { },
+ onUserJoined: (user) => { },
+}));
+
+// Add v5-only events alongside it
+CometChatCalls.addEventListener("onConnectionLost", () => { });
+CometChatCalls.addEventListener("onCallLayoutChanged", (layoutType) => { });
+```
+
### Event Mapping
| v4 Event | v5 Event |
diff --git a/calls/react-native/overview.mdx b/calls/react-native/overview.mdx
index 5c0f04415..12370565e 100644
--- a/calls/react-native/overview.mdx
+++ b/calls/react-native/overview.mdx
@@ -14,7 +14,7 @@ Before integrating the Calls SDK, ensure you have:
1. **CometChat Account**: [Sign up](https://app.cometchat.com/signup) and create an app to get your App ID, Region, and API Key
2. **CometChat Users**: Users must exist in CometChat to use calling features. For testing, create users via the [Dashboard](https://app.cometchat.com) or [REST API](/rest-api/users/create). Authentication is handled by the Calls SDK - see [Authentication](/calls/react-native/authentication)
3. **React Native Requirements**:
- - React Native 0.71 or later
+ - React Native 0.71 or later (works with both the old architecture and the [New Architecture](/calls/react-native/setup#react-native-new-architecture), including bridgeless mode)
- Node.js 18 or later
- iOS: Minimum iOS 13.0, Xcode 14.0+
- Android: Minimum SDK API Level 24 (Android 7.0)
@@ -57,6 +57,10 @@ sequenceDiagram
Record call sessions for later playback
+
+ Transcribe calls, show live closed captions, and retrieve transcripts
+
+
Retrieve call history and details
@@ -95,6 +99,34 @@ The SDK is organized around these core components:
| `CometChatCalls.Component` | React component that renders the call UI |
| `CometChatCalls.addEventListener` | Subscribe to call events; returns an unsubscribe function |
+## Single Active Session
+
+`CometChatCalls` is a singleton, and it tracks one active call session at a time. Every session method acts on that current session, which is why methods like `leaveSession()` take no session ID:
+
+```tsx
+// Acts on whatever session is currently active - no session ID needed
+CometChatCalls.leaveSession();
+```
+
+| Behavior | Detail |
+|----------|--------|
+| One session at a time | The SDK does not support being connected to two sessions simultaneously. Leave the current session before joining another. |
+| One `Component` at a time | Render a single `CometChatCalls.Component` for the active call. Mounting two at once is not supported. |
+| Session methods take no session ID | `leaveSession()` and the other [actions](/calls/react-native/actions) apply to the active session only. |
+| Actions apply to the active session | `muteAudio()`, `pauseVideo()`, `switchCamera()`, and similar methods target the session currently in progress. |
+| Events are not session-scoped | Listeners registered with `addEventListener` fire for the active session. |
+
+To move a user from one call to another, leave the current session, wait for the `onSessionLeft` event, then generate a token for the new session and render the component with the new token:
+
+```tsx
+CometChatCalls.addEventListener('onSessionLeft', async () => {
+ const { token } = await CometChatCalls.generateToken(nextSessionId);
+ setCallToken(token); // Re-renders CometChatCalls.Component with the new session
+}, { signal });
+
+CometChatCalls.leaveSession();
+```
+
## Related Documentation
- [Setup](/calls/react-native/setup) - Install and configure the SDK
diff --git a/calls/react-native/recording.mdx b/calls/react-native/recording.mdx
index ac29f9aa4..ce0210b3d 100644
--- a/calls/react-native/recording.mdx
+++ b/calls/react-native/recording.mdx
@@ -46,6 +46,13 @@ Stop an active recording:
CometChatCalls.stopRecording();
```
+
+**A recording runs until it is stopped.** Whether it was started manually or automatically through `autoStartRecording`, it keeps running until someone stops it. If nobody does, it stops on its own when either:
+
+- Everyone leaves the session — the recording ends about a minute later.
+- Everyone in the session stays muted for 10 minutes.
+
+
## Recording Events
Listen for recording state changes:
diff --git a/calls/react-native/session-settings.mdx b/calls/react-native/session-settings.mdx
index 2f28d0825..3990b5250 100644
--- a/calls/react-native/session-settings.mdx
+++ b/calls/react-native/session-settings.mdx
@@ -27,6 +27,10 @@ const sessionSettings = {
audioMode: 'SPEAKER',
hideRecordingButton: true,
autoStartRecording: false,
+ hideTranscriptionButton: true,
+ hideClosedCaptionButton: true,
+ autoStartTranscription: false,
+ captionLanguage: 'en-US',
idleTimeoutPeriodBeforePrompt: 180000,
enableSpotlightSwap: true,
enableSpotlightDrag: true,
@@ -84,6 +88,8 @@ const sessionSettings = {
| `hideSwitchCameraButton` | boolean | `false` | Hide the switch camera button |
| `hideAudioModeButton` | boolean | `false` | Hide the audio output (route) button |
| `hideRecordingButton` | boolean | `true` | Hide the recording button |
+| `hideTranscriptionButton` | boolean | `true` | Hide the transcription item in the **More** menu |
+| `hideClosedCaptionButton` | boolean | `true` | Hide the closed-captions item in the **More** menu |
| `hideChangeLayoutButton` | boolean | `false` | Hide the layout-switcher button |
| `hideParticipantListButton` | boolean | `false` | Hide the button that opens the participant list |
| `hideChatButton` | boolean | `true` | Hide the in-call chat button |
@@ -141,6 +147,28 @@ const sessionSettings = {
};
```
+### Transcription & Closed Captions
+
+*Available since v5.0.5*
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `hideTranscriptionButton` | boolean | `true` | Hide the **Start / Stop Transcription** item in the **More** menu |
+| `hideClosedCaptionButton` | boolean | `true` | Hide the **Show / Hide Captions** item in the **More** menu. Even when `false`, the item only appears while transcription is running |
+| `autoStartTranscription` | boolean | `false` | Auto-start transcription when the call begins |
+| `captionLanguage` | string | `'en-US'` | Language code used for transcription and captions |
+
+```tsx
+const sessionSettings = {
+ hideTranscriptionButton: false,
+ hideClosedCaptionButton: false,
+ autoStartTranscription: true,
+ captionLanguage: 'en-US',
+};
+```
+
+See [Transcription](/calls/react-native/transcription) for the supported language codes and transcript retrieval.
+
### Idle Timeout
| Property | Type | Default | Description |
@@ -297,6 +325,10 @@ function createSessionSettings(isAudioOnly: boolean = false) {
startVideoPaused: false,
audioMode: 'SPEAKER',
hideRecordingButton: false,
+ hideTranscriptionButton: false,
+ hideClosedCaptionButton: false,
+ autoStartTranscription: false,
+ captionLanguage: 'en-US',
idleTimeoutPeriodBeforePrompt: 180000,
};
}
diff --git a/calls/react-native/setup.mdx b/calls/react-native/setup.mdx
index 239782e85..723680be9 100644
--- a/calls/react-native/setup.mdx
+++ b/calls/react-native/setup.mdx
@@ -25,13 +25,13 @@ This guide walks you through installing the CometChat Calls SDK and configuring
### Using npm
```bash
-npm install @cometchat/calls-sdk-react-native
+npm install @cometchat/calls-sdk-react-native@5.0.5
```
### Using Yarn
```bash
-yarn add @cometchat/calls-sdk-react-native
+yarn add @cometchat/calls-sdk-react-native@5.0.5
```
## Install Required Dependencies
@@ -59,6 +59,14 @@ npm install @react-native-async-storage/async-storage@^2.1.2 react-native-backgr
yarn add @react-native-async-storage/async-storage@^2.1.2 react-native-background-timer@^2.4.1 react-native-performance@^5.1.2 react-native-svg@^15.12.0 react-native-url-polyfill@2.0.0 react-native-webrtc@124.0.7
```
+## React Native New Architecture
+
+The Calls SDK works with both the old architecture and the New Architecture, including bridgeless mode. No extra setup is needed — leave `newArchEnabled` set to whatever your app requires and follow the same integration steps either way.
+
+
+The SDK ships as a legacy native module rather than a TurboModule/Fabric component. On the New Architecture it runs through React Native's built-in interop layer, so the API and behavior are identical on both architectures.
+
+
## iOS Configuration
### Install CocoaPods Dependencies
diff --git a/calls/react-native/transcription.mdx b/calls/react-native/transcription.mdx
new file mode 100644
index 000000000..7deb7ee51
--- /dev/null
+++ b/calls/react-native/transcription.mdx
@@ -0,0 +1,372 @@
+---
+title: "Transcription & Closed Captions"
+sidebarTitle: "Transcription"
+sdk_version: "5.x"
+description: "Use CometChat Calls SDK v5 transcription on React Native to transcribe calls, show live closed captions, and retrieve transcripts after the call."
+---
+
+
+**Available since v5.0.5** — transcription and closed captions require CometChat Calls SDK v5.0.5 or later for React Native. See [Setup](/calls/react-native/setup) to install or upgrade.
+
+
+Transcribe call sessions in real time and display live closed captions on screen. Transcripts are stored server-side and can be retrieved after the call using `TranscriptRequestBuilder`.
+
+
+Transcription must be enabled for your CometChat app. Contact support if you need to enable this feature.
+
+
+## How It Works
+
+Transcription and closed captions are two related but separate things:
+
+| Concept | What it does |
+|---------|--------------|
+| **Transcription** | Server-side speech-to-text for the session. Starting it brings a transcriber into the call, which produces the transcript that is stored for later retrieval. |
+| **Closed captions** | The on-screen overlay that renders the live transcript as it arrives. Captions are produced from the running transcription, so they only appear while transcription is active. |
+
+Starting transcription is a prerequisite for captions — toggling captions on without an active transcription shows nothing.
+
+## Starting Transcription
+
+### Auto-Start Transcription
+
+Configure transcription to start automatically when the session begins:
+
+```tsx
+import { CometChatCalls } from '@cometchat/calls-sdk-react-native';
+
+const sessionSettings = {
+ autoStartTranscription: true,
+ // ... other settings
+};
+
+
+```
+
+**Default:** `false`
+
+### Manual Transcription Control
+
+#### Start Transcription
+
+Begin transcribing during an active call:
+
+```tsx
+CometChatCalls.startTranscription();
+```
+
+#### Stop Transcription
+
+Stop the current transcription. Any captions currently on screen are cleared:
+
+```tsx
+CometChatCalls.stopTranscription();
+```
+
+## Built-in UI Controls
+
+Both transcription controls live in the control panel's **More** menu and are hidden by default.
+
+### Transcription Menu Item
+
+Show the **Start Transcription** / **Stop Transcription** item:
+
+```tsx
+const sessionSettings = {
+ hideTranscriptionButton: false,
+ // ... other settings
+};
+```
+
+**Default:** `true`
+
+The item toggles between **Start Transcription** and **Stop Transcription** based on the current state.
+
+
+Even with `hideTranscriptionButton: false`, the item is hidden while another participant is running transcription for the session. Only the participant who started transcription can stop it from the menu.
+
+
+### Closed Captions Menu Item
+
+Show the **Show Captions** / **Hide Captions** item, which toggles the on-screen captions overlay:
+
+```tsx
+const sessionSettings = {
+ hideClosedCaptionButton: false,
+ // ... other settings
+};
+```
+
+**Default:** `true`
+
+
+Even with `hideClosedCaptionButton: false`, the item only appears once transcription is running for the session, because captions are generated from the live transcript.
+
+
+### Captions Overlay
+
+When captions are shown, the SDK renders a captions panel between the call stage and the control panel. Each caption shows the speaker's avatar, name, and text. The panel keeps the most recent captions, auto-scrolls to the newest one, and shows a jump-to-latest button when the user has scrolled up to read earlier captions. Until someone speaks, it displays *Waiting for people to speak…*.
+
+## Caption Language
+
+**Property:** `captionLanguage`
+
+Sets the language used for transcription and captions.
+
+```tsx
+const sessionSettings = {
+ captionLanguage: 'en-US',
+ // ... other settings
+};
+```
+
+**Default:** `en-US`
+
+
+On React Native there is no in-call language picker. Set `captionLanguage` in the session settings before joining.
+
+
+
+| Code | Language |
+|------|----------|
+| `en-US` | English (United States) |
+| `de-DE` | German (Germany) |
+| `en-GB` | English (United Kingdom) |
+| `es-ES` | Spanish (Spain) |
+| `fr-FR` | French (France) |
+| `hi-IN` | Hindi (India) |
+| `hu-HU` | Hungarian (Hungary) |
+| `it-IT` | Italian (Italy) |
+| `ja-JP` | Japanese (Japan) |
+| `ko-KR` | Korean (South Korea) |
+| `lt-LT` | Lithuanian (Lithuania) |
+| `ms-MY` | Malay (Malaysia) |
+| `nl-NL` | Dutch (Netherlands) |
+| `pt-PT` | Portuguese (Portugal) |
+| `ru-RU` | Russian (Russia) |
+| `sv-SE` | Swedish (Sweden) |
+| `tr-TR` | Turkish (Turkey) |
+| `zh` | Chinese Mandarin (Simplified, China) |
+| `zh-TW` | Chinese Mandarin (Traditional, Taiwan) |
+
+
+## Retrieving Transcripts
+
+After a call, use `TranscriptRequestBuilder` to list the transcript artifacts for a session. Each record is a **pointer to a downloadable transcript file**, not the transcript text itself.
+
+
+The SDK must be initialized with `CometChatCalls.init()` and a user must be logged in. The auth token is read from the logged-in user at fetch time, so it automatically tracks re-logins.
+
+
+### Building a Request
+
+```tsx
+import { CometChatCalls } from '@cometchat/calls-sdk-react-native';
+
+const request = new CometChatCalls.TranscriptRequestBuilder()
+ .setSessionId('v1.us.2547167fe69871fd.alice') // required
+ .setLimit(10) // optional
+ .build();
+
+const transcripts = await request.fetchNext();
+```
+
+| Method | Required | Description |
+|--------|----------|-------------|
+| `setSessionId(sessionId: string)` | Yes | The session ID whose transcripts to fetch. An empty value throws `SESSION_ID_REQUIRED` at `build()`. |
+| `setLimit(limit: number)` | No | Page size. Defaults to `30` and is clamped to the range `1`–`1000`. |
+| `build()` | Yes | Returns a `TranscriptRequest`. Throws synchronously for pre-flight errors. |
+
+### Paginating
+
+A `TranscriptRequest` is a stateful cursor. Create one per session ID and drive it with `fetchNext()` and `fetchPrevious()`:
+
+```tsx
+const request = new CometChatCalls.TranscriptRequestBuilder()
+ .setSessionId(sessionId)
+ .setLimit(10)
+ .build();
+
+let page = await request.fetchNext(); // page 1
+
+while (page.length > 0) {
+ page.forEach((transcript) => console.log(transcript.transcriptUrl));
+ page = await request.fetchNext(); // page 2, 3, ... then [] at the end
+}
+```
+
+- `fetchNext()` resolves the next page, or `[]` when there are no more pages. A session with no transcripts resolves `[]`.
+- `fetchPrevious()` resolves the previous page, or `[]` when already on the first page. It never requests a page below `1`.
+- Only one fetch may be in flight at a time. Calling `fetchNext()` or `fetchPrevious()` while another request is pending rejects with `REQUEST_IN_PROGRESS`; the original call is unaffected and still completes.
+
+### Transcript Properties
+
+The `Transcript` type is exported from the package for TypeScript consumers:
+
+```tsx
+import type { Transcript } from '@cometchat/calls-sdk-react-native';
+```
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `tid` | string | Transcript ID |
+| `mid` | string | Meeting ID |
+| `roomName` | string | Room name of the meeting |
+| `startTime` | number | Meeting start time, in epoch **seconds** |
+| `endTime` | number | Meeting end time, in epoch **seconds** |
+| `url` | string | Meeting URL |
+| `transcriptDate` | string | Transcript date |
+| `transcriptUrl` | string | URL of the downloadable transcript JSON |
+| `metaData` | object | Arbitrary metadata |
+
+
+Every property is optional. The server omits keys whose value is empty, so sparse records are normal and should not be treated as an error.
+
+
+### Reading the Transcript Content
+
+`transcriptUrl` points at the transcript file. Fetch it yourself to read the actual utterances:
+
+```tsx
+const [transcript] = await request.fetchNext();
+
+if (transcript?.transcriptUrl) {
+ const response = await fetch(transcript.transcriptUrl);
+ const content = await response.json();
+ console.log(content);
+}
+```
+
+### Error Handling
+
+`build()` throws synchronously; `fetchNext()` and `fetchPrevious()` reject. Both surface a `CometChatCallsException` carrying a `code`:
+
+| Condition | Code | Raised by |
+|-----------|------|-----------|
+| `init()` was not called | `NOT_INITIALIZED` | `build()` |
+| `sessionId` missing or empty | `SESSION_ID_REQUIRED` | `build()` |
+| No logged-in user | `NOT_LOGGED_IN` | `fetchNext()` / `fetchPrevious()` |
+| A fetch is already in flight | `REQUEST_IN_PROGRESS` | `fetchNext()` / `fetchPrevious()` |
+| Network failure | `NETWORK_ERROR` | `fetchNext()` / `fetchPrevious()` |
+| Missing or malformed response | `BAD_RESPONSE` | `fetchNext()` / `fetchPrevious()` |
+| Server-side API error | The server's own code (e.g. `AUTH_ERR_EMPTY_APPID`) | `fetchNext()` / `fetchPrevious()` |
+
+```tsx
+try {
+ const transcripts = await request.fetchNext();
+} catch (error) {
+ console.error(error.code, error.message);
+}
+```
+
+## Transcripts in Call Logs
+
+Call logs can be filtered to transcribed calls, which also attaches each call's transcripts to the log:
+
+```tsx
+const callLogRequest = new CometChatCalls.CallLogRequestBuilder()
+ .setLimit(30)
+ .setHasTranscriptions(true)
+ .build();
+
+const callLogs = await callLogRequest.fetchNext();
+
+callLogs.forEach((callLog) => {
+ callLog.getTranscriptions().forEach((transcription) => {
+ console.log(transcription.getTid(), transcription.getTranscriptURL());
+ });
+});
+```
+
+`getTranscriptions()` returns an empty array when the server omitted transcripts, so it never needs a null check.
+
+
+| Method | Returns | Description |
+|--------|---------|-------------|
+| `getTid()` | string | The transcript ID |
+| `getMid()` | string | The meeting ID |
+| `getRoomName()` | string | The room name of the meeting |
+| `getStartTime()` | number | Meeting start time, in epoch seconds |
+| `getEndTime()` | number | Meeting end time, in epoch seconds |
+| `getTranscriptDate()` | string | The transcript date |
+| `getTranscriptURL()` | string | URL of the downloadable transcript JSON |
+
+
+## Complete Example
+
+```tsx
+import React, { useEffect, useState } from 'react';
+import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
+import { CometChatCalls } from '@cometchat/calls-sdk-react-native';
+import type { Transcript } from '@cometchat/calls-sdk-react-native';
+
+// 1. Join a session with transcription enabled
+const sessionSettings = {
+ sessionType: 'VIDEO',
+ autoStartTranscription: true,
+ hideTranscriptionButton: false,
+ hideClosedCaptionButton: false,
+ captionLanguage: 'en-US',
+};
+
+function CallScreen({ callToken }: { callToken: string }) {
+ const [isTranscribing, setIsTranscribing] = useState(true);
+
+ // 2. Control transcription during the call
+ const toggleTranscription = () => {
+ if (isTranscribing) {
+ CometChatCalls.stopTranscription();
+ } else {
+ CometChatCalls.startTranscription();
+ }
+ setIsTranscribing(!isTranscribing);
+ };
+
+ return (
+
+
+
+
+ {isTranscribing ? 'Stop Transcription' : 'Start Transcription'}
+
+
+
+ );
+}
+
+// 3. Retrieve transcripts after the call
+async function fetchTranscripts(sessionId: string): Promise {
+ const request = new CometChatCalls.TranscriptRequestBuilder()
+ .setSessionId(sessionId)
+ .setLimit(10)
+ .build();
+
+ return request.fetchNext();
+}
+
+const styles = StyleSheet.create({
+ container: { flex: 1 },
+ button: {
+ backgroundColor: '#333',
+ paddingHorizontal: 16,
+ paddingVertical: 12,
+ borderRadius: 8,
+ alignSelf: 'center',
+ },
+ buttonText: { color: '#fff', fontSize: 14, fontWeight: '600' },
+});
+
+export default CallScreen;
+```
+
+## Related Documentation
+
+- [Session Settings](/calls/react-native/session-settings) - Configure transcription options
+- [Actions](/calls/react-native/actions) - Start and stop transcription programmatically
+- [Call Logs](/calls/react-native/call-logs) - Transcripts attached to call history
diff --git a/calls/v4/android/call-logs.mdx b/calls/v4/android/call-logs.mdx
index 0f83de500..981a680fa 100644
--- a/calls/v4/android/call-logs.mdx
+++ b/calls/v4/android/call-logs.mdx
@@ -2,9 +2,17 @@
title: "Call Logs"
sidebarTitle: "Call Logs"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Logs for Android"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Logs for Android"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
+
## Overview
CometChat's Android Call SDK provides a comprehensive way to integrate call logs into your application, enhancing your user experience by allowing users to effortlessly keep track of their communication history. Call logs provide crucial information such as call duration, participants, and more.
diff --git a/calls/v4/android/call-session.mdx b/calls/v4/android/call-session.mdx
index 7eeaa060b..48d9c4c21 100644
--- a/calls/v4/android/call-session.mdx
+++ b/calls/v4/android/call-session.mdx
@@ -2,9 +2,17 @@
title: "Call Session"
sidebarTitle: "Call Session"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Session for Android"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Session for Android"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
+
## Overview
This section demonstrates how to start a call session in an Android application. Previously known as **Direct Calling**.
diff --git a/calls/v4/android/overview.mdx b/calls/v4/android/overview.mdx
index 19eb74b4f..412250b0e 100644
--- a/calls/v4/android/overview.mdx
+++ b/calls/v4/android/overview.mdx
@@ -2,12 +2,16 @@
title: "Calling SDK"
sidebarTitle: "Overview"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Overview for Android. This is the stable v4 documentation. Most users should use this version."
+description: "CometChat Calling SDK v4 - Legacy Release - Overview for Android. This is the legacy v4 documentation. Most users should use v5, the current stable version."
---
-
-🚀 **v5 Beta Available** — The Calling SDK v5 is now available in beta with significant improvements. [Check out the v5 docs →](/calls/android/overview)
-
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
## Overview
diff --git a/calls/v4/android/presenter-mode.mdx b/calls/v4/android/presenter-mode.mdx
index d0e41e0a2..f038afe5f 100644
--- a/calls/v4/android/presenter-mode.mdx
+++ b/calls/v4/android/presenter-mode.mdx
@@ -2,9 +2,17 @@
title: "Presenter Mode"
sidebarTitle: "Presenter Mode"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Presenter Mode for Android"
+description: "CometChat Calling SDK v4 - Legacy Release - Presenter Mode for Android"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
+
## Overview
The Presenter Mode feature allows developers to create a calling service experience in which:
diff --git a/calls/v4/android/recording.mdx b/calls/v4/android/recording.mdx
index 394361e31..60f943733 100644
--- a/calls/v4/android/recording.mdx
+++ b/calls/v4/android/recording.mdx
@@ -2,9 +2,17 @@
title: "Recording"
sidebarTitle: "Recording"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Recording for Android"
+description: "CometChat Calling SDK v4 - Legacy Release - Recording for Android"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
+
This section will guide you to implement call recording feature for the voice and video calls.
## Implementation
diff --git a/calls/v4/android/ringing.mdx b/calls/v4/android/ringing.mdx
index 7e584b258..6d892ba90 100644
--- a/calls/v4/android/ringing.mdx
+++ b/calls/v4/android/ringing.mdx
@@ -2,9 +2,17 @@
title: "Ringing"
sidebarTitle: "Ringing"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Ringing for Android"
+description: "CometChat Calling SDK v4 - Legacy Release - Ringing for Android"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
+
## Overview
This section explains how to implement a complete calling workflow with ringing functionality, including incoming/outgoing call UI, call acceptance, rejection, and cancellation. Previously known as **Default Calling**.
diff --git a/calls/v4/android/session-timeout.mdx b/calls/v4/android/session-timeout.mdx
index af96acde0..28dbe86b3 100644
--- a/calls/v4/android/session-timeout.mdx
+++ b/calls/v4/android/session-timeout.mdx
@@ -2,9 +2,17 @@
title: "Session Timeout Flow"
sidebarTitle: "Session Timeout"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Session Timeout for Android"
+description: "CometChat Calling SDK v4 - Legacy Release - Session Timeout for Android"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
+
Available since v4.1.0
## Overview
diff --git a/calls/v4/android/setup.mdx b/calls/v4/android/setup.mdx
index 362c473f6..c0a4de383 100644
--- a/calls/v4/android/setup.mdx
+++ b/calls/v4/android/setup.mdx
@@ -2,9 +2,17 @@
title: "Setup"
sidebarTitle: "Setup"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Setup for Android"
+description: "CometChat Calling SDK v4 - Legacy Release - Setup for Android"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
+
## Get your Application Keys
[Signup for CometChat](https://app.cometchat.com/signup) and then:
diff --git a/calls/v4/android/standalone-calling.mdx b/calls/v4/android/standalone-calling.mdx
index 77c4cc81f..72cde0219 100644
--- a/calls/v4/android/standalone-calling.mdx
+++ b/calls/v4/android/standalone-calling.mdx
@@ -2,9 +2,17 @@
title: "Standalone Calling"
sidebarTitle: "Standalone Calling"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Standalone Calling for Android"
+description: "CometChat Calling SDK v4 - Legacy Release - Standalone Calling for Android"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
+
## Overview
This section demonstrates how to implement calling functionality using only the CometChat Calls SDK, without requiring the Chat SDK. This is ideal for applications that need video/audio calling capabilities without the full chat infrastructure.
diff --git a/calls/v4/android/video-view-customisation.mdx b/calls/v4/android/video-view-customisation.mdx
index b0b6db7a4..c703721b3 100644
--- a/calls/v4/android/video-view-customisation.mdx
+++ b/calls/v4/android/video-view-customisation.mdx
@@ -2,9 +2,17 @@
title: "Video View Customisation"
sidebarTitle: "Video View Customisation"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Video View Customisation for Android"
+description: "CometChat Calling SDK v4 - Legacy Release - Video View Customisation for Android"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Android).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/android/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/android/migration-guide-v5).
+
+
This section will guide you to customise the main video container.
## Implementation
diff --git a/calls/v4/flutter/call-logs.mdx b/calls/v4/flutter/call-logs.mdx
index bff32890e..d0da7b464 100644
--- a/calls/v4/flutter/call-logs.mdx
+++ b/calls/v4/flutter/call-logs.mdx
@@ -2,9 +2,17 @@
title: "Call Logs"
sidebarTitle: "Call Logs"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Logs for Flutter"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Logs for Flutter"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
+
## Overview
CometChat's Flutter Call SDK provides a comprehensive way to integrate call logs into your application, enhancing your user experience by allowing users to effortlessly keep track of their communication history. Call logs provide crucial information such as call duration, participants, and more.
diff --git a/calls/v4/flutter/call-session.mdx b/calls/v4/flutter/call-session.mdx
index 3c0a83b44..352add123 100644
--- a/calls/v4/flutter/call-session.mdx
+++ b/calls/v4/flutter/call-session.mdx
@@ -2,9 +2,17 @@
title: "Call Session"
sidebarTitle: "Call Session"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Session for Flutter"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Session for Flutter"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
+
## Overview
This section demonstrates how to start a call session in a Flutter application. Previously known as **Direct Calling**.
diff --git a/calls/v4/flutter/overview.mdx b/calls/v4/flutter/overview.mdx
index e8966fbd2..340654d65 100644
--- a/calls/v4/flutter/overview.mdx
+++ b/calls/v4/flutter/overview.mdx
@@ -2,12 +2,16 @@
title: "Calling SDK"
sidebarTitle: "Overview"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Overview for Flutter. This is the stable v4 documentation. Most users should use this version."
+description: "CometChat Calling SDK v4 - Legacy Release - Overview for Flutter. This is the legacy v4 documentation. Most users should use v5, the current stable version."
---
-
-🚀 **v5 Beta Available** — The Calling SDK v5 is now available in beta with significant improvements. [Check out the v5 docs →](/calls/flutter/overview)
-
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
## Overview
diff --git a/calls/v4/flutter/presenter-mode.mdx b/calls/v4/flutter/presenter-mode.mdx
index 01b42193d..7a81174f4 100644
--- a/calls/v4/flutter/presenter-mode.mdx
+++ b/calls/v4/flutter/presenter-mode.mdx
@@ -2,9 +2,17 @@
title: "Presenter Mode"
sidebarTitle: "Presenter Mode"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Presenter Mode for Flutter"
+description: "CometChat Calling SDK v4 - Legacy Release - Presenter Mode for Flutter"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
+
## Overview
The Presenter Mode feature allows developers to create a calling service experience in which:
diff --git a/calls/v4/flutter/recording.mdx b/calls/v4/flutter/recording.mdx
index 853dce7db..ae6675374 100644
--- a/calls/v4/flutter/recording.mdx
+++ b/calls/v4/flutter/recording.mdx
@@ -2,9 +2,17 @@
title: "Recording"
sidebarTitle: "Recording"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Recording for Flutter"
+description: "CometChat Calling SDK v4 - Legacy Release - Recording for Flutter"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
+
This section will guide you to implement call recording feature for the voice and video calls.
## Implementation
diff --git a/calls/v4/flutter/ringing.mdx b/calls/v4/flutter/ringing.mdx
index b03a2a628..837125ba3 100644
--- a/calls/v4/flutter/ringing.mdx
+++ b/calls/v4/flutter/ringing.mdx
@@ -2,9 +2,17 @@
title: "Ringing"
sidebarTitle: "Ringing"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Ringing for Flutter"
+description: "CometChat Calling SDK v4 - Legacy Release - Ringing for Flutter"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
+
## Overview
This section explains how to implement a complete calling workflow with ringing functionality, including incoming/outgoing call UI, call acceptance, rejection, and cancellation. Previously known as **Default Calling**.
diff --git a/calls/v4/flutter/session-timeout.mdx b/calls/v4/flutter/session-timeout.mdx
index 8fe7c076f..9773c98b4 100644
--- a/calls/v4/flutter/session-timeout.mdx
+++ b/calls/v4/flutter/session-timeout.mdx
@@ -2,9 +2,17 @@
title: "Session Timeout Flow"
sidebarTitle: "Session Timeout"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Session Timeout for Flutter"
+description: "CometChat Calling SDK v4 - Legacy Release - Session Timeout for Flutter"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
+
Available since v4.1.0
## Overview
diff --git a/calls/v4/flutter/setup.mdx b/calls/v4/flutter/setup.mdx
index 887c179c1..c1323c195 100644
--- a/calls/v4/flutter/setup.mdx
+++ b/calls/v4/flutter/setup.mdx
@@ -2,9 +2,17 @@
title: "Setup"
sidebarTitle: "Setup"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Setup for Flutter"
+description: "CometChat Calling SDK v4 - Legacy Release - Setup for Flutter"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
+
### Get your Application Keys
[Signup for CometChat](https://app.cometchat.com) and then:
diff --git a/calls/v4/flutter/standalone-calling.mdx b/calls/v4/flutter/standalone-calling.mdx
index 7629d1071..0ce741642 100644
--- a/calls/v4/flutter/standalone-calling.mdx
+++ b/calls/v4/flutter/standalone-calling.mdx
@@ -2,9 +2,17 @@
title: "Standalone Calling"
sidebarTitle: "Standalone Calling"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Standalone Calling for Flutter"
+description: "CometChat Calling SDK v4 - Legacy Release - Standalone Calling for Flutter"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
+
## Overview
This section demonstrates how to implement calling functionality using only the CometChat Calls SDK, without requiring the Chat SDK. This is ideal for applications that need video/audio calling capabilities without the full chat infrastructure.
diff --git a/calls/v4/flutter/video-view-customisation.mdx b/calls/v4/flutter/video-view-customisation.mdx
index fa8670243..7665acc90 100644
--- a/calls/v4/flutter/video-view-customisation.mdx
+++ b/calls/v4/flutter/video-view-customisation.mdx
@@ -2,9 +2,17 @@
title: "Video View Customisation"
sidebarTitle: "Video View Customisation"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Video View Customisation for Flutter"
+description: "CometChat Calling SDK v4 - Legacy Release - Video View Customisation for Flutter"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (Flutter).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/flutter/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/flutter/migration-guide-v5).
+
+
This section will guide you to customise the main video container.
## Implementation
diff --git a/calls/v4/ios/call-logs.mdx b/calls/v4/ios/call-logs.mdx
index ee3e8d2df..7f0c205e1 100644
--- a/calls/v4/ios/call-logs.mdx
+++ b/calls/v4/ios/call-logs.mdx
@@ -2,9 +2,17 @@
title: "Call Logs"
sidebarTitle: "Call Logs"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Logs for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Logs for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
## Overview
CometChat's iOS Call SDK provides a comprehensive way to integrate call logs into your application, enhancing your user experience by allowing users to effortlessly keep track of their communication history. Call logs provide crucial information such as call duration, participants, and more.
diff --git a/calls/v4/ios/call-session.mdx b/calls/v4/ios/call-session.mdx
index c4f9d18cb..2477aa41e 100644
--- a/calls/v4/ios/call-session.mdx
+++ b/calls/v4/ios/call-session.mdx
@@ -2,9 +2,17 @@
title: "Call Session"
sidebarTitle: "Call Session"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Session for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Session for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
## Overview
This section demonstrates how to start a call session in an iOS application. Previously known as **Direct Calling**.
diff --git a/calls/v4/ios/launch-call-screen-on-tap-of-push-notification.mdx b/calls/v4/ios/launch-call-screen-on-tap-of-push-notification.mdx
index 7dfbe8032..9e7e48bbe 100644
--- a/calls/v4/ios/launch-call-screen-on-tap-of-push-notification.mdx
+++ b/calls/v4/ios/launch-call-screen-on-tap-of-push-notification.mdx
@@ -2,9 +2,17 @@
title: "Launch Call Screen On Tap Of Push Notification"
sidebarTitle: "Launch Call Screen On Push"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Launch Call Screen On Tap Of Push Notification for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Launch Call Screen On Tap Of Push Notification for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
diff --git a/calls/v4/ios/overview.mdx b/calls/v4/ios/overview.mdx
index 8291c0589..1358216fe 100644
--- a/calls/v4/ios/overview.mdx
+++ b/calls/v4/ios/overview.mdx
@@ -2,12 +2,16 @@
title: "Calling SDK"
sidebarTitle: "Overview"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Overview for iOS. This is the stable v4 documentation. Most users should use this version."
+description: "CometChat Calling SDK v4 - Legacy Release - Overview for iOS. This is the legacy v4 documentation. Most users should use v5, the current stable version."
---
-
-🚀 **v5 Beta Available** — The Calling SDK v5 is now available in beta with significant improvements. [Check out the v5 docs →](/calls/ios/overview)
-
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
## Overview
diff --git a/calls/v4/ios/presenter-mode.mdx b/calls/v4/ios/presenter-mode.mdx
index a0c267205..00c676df1 100644
--- a/calls/v4/ios/presenter-mode.mdx
+++ b/calls/v4/ios/presenter-mode.mdx
@@ -2,9 +2,17 @@
title: "Presenter Mode"
sidebarTitle: "Presenter Mode"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Presenter Mode for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Presenter Mode for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
## Overview
The Presenter Mode feature allows developers to create a calling service experience in which:
diff --git a/calls/v4/ios/recording.mdx b/calls/v4/ios/recording.mdx
index 91568f617..98c606931 100644
--- a/calls/v4/ios/recording.mdx
+++ b/calls/v4/ios/recording.mdx
@@ -2,9 +2,17 @@
title: "Recording"
sidebarTitle: "Recording"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Recording for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Recording for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
This section will guide you to implement call recording feature for the voice and video calls.
## Implementation
diff --git a/calls/v4/ios/ringing.mdx b/calls/v4/ios/ringing.mdx
index 2e7db467c..ba1f80f26 100644
--- a/calls/v4/ios/ringing.mdx
+++ b/calls/v4/ios/ringing.mdx
@@ -2,9 +2,17 @@
title: "Ringing"
sidebarTitle: "Ringing"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Ringing for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Ringing for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
## Overview
This section explains how to implement a complete calling workflow with ringing functionality, including incoming/outgoing call UI, call acceptance, rejection, and cancellation. Previously known as **Default Calling**.
diff --git a/calls/v4/ios/session-timeout.mdx b/calls/v4/ios/session-timeout.mdx
index 65ebb77fb..360ce3e8d 100644
--- a/calls/v4/ios/session-timeout.mdx
+++ b/calls/v4/ios/session-timeout.mdx
@@ -2,9 +2,17 @@
title: "Session Timeout Flow"
sidebarTitle: "Session Timeout"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Session Timeout for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Session Timeout for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
Available since v4.1.1
## Overview
diff --git a/calls/v4/ios/setup.mdx b/calls/v4/ios/setup.mdx
index 90e145264..7bfbb4e5d 100644
--- a/calls/v4/ios/setup.mdx
+++ b/calls/v4/ios/setup.mdx
@@ -2,9 +2,17 @@
title: "Setup"
sidebarTitle: "Setup"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Setup for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Setup for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
The **CometChatCalls** is developed to keep developers in mind and aims to reduce development efforts significantly. Let's start to integrate Calls Kit into your project.
***
diff --git a/calls/v4/ios/standalone-calling.mdx b/calls/v4/ios/standalone-calling.mdx
index 850bcde2f..bd26679f2 100644
--- a/calls/v4/ios/standalone-calling.mdx
+++ b/calls/v4/ios/standalone-calling.mdx
@@ -2,9 +2,17 @@
title: "Standalone Calling"
sidebarTitle: "Standalone Calling"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Standalone Calling for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Standalone Calling for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
## Overview
This section demonstrates how to implement calling functionality using only the CometChat Calls SDK, without requiring the Chat SDK. This is ideal for applications that need video/audio calling capabilities without the full chat infrastructure.
diff --git a/calls/v4/ios/video-view-customisation.mdx b/calls/v4/ios/video-view-customisation.mdx
index 252a526e7..4aac7cd14 100644
--- a/calls/v4/ios/video-view-customisation.mdx
+++ b/calls/v4/ios/video-view-customisation.mdx
@@ -2,9 +2,17 @@
title: "Video View Customisation"
sidebarTitle: "Video View Customisation"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Video View Customisation for iOS"
+description: "CometChat Calling SDK v4 - Legacy Release - Video View Customisation for iOS"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (iOS).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/ios/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/ios/migration-guide-v5).
+
+
This section will guide you to customise the main video container.
## Implementation
diff --git a/calls/v4/javascript/call-logs.mdx b/calls/v4/javascript/call-logs.mdx
index bbc39110a..278842747 100644
--- a/calls/v4/javascript/call-logs.mdx
+++ b/calls/v4/javascript/call-logs.mdx
@@ -2,9 +2,17 @@
title: "Call Logs"
sidebarTitle: "Call Logs"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Logs for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Logs for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
## Overview
CometChat's Web Call SDK provides a comprehensive way to integrate call logs into your application, enhancing your user experience by allowing users to effortlessly keep track of their communication history. Call logs provide crucial information such as call duration, participants, and more.
diff --git a/calls/v4/javascript/call-session.mdx b/calls/v4/javascript/call-session.mdx
index 55647d145..6c7f2aa92 100644
--- a/calls/v4/javascript/call-session.mdx
+++ b/calls/v4/javascript/call-session.mdx
@@ -2,9 +2,17 @@
title: "Call Session"
sidebarTitle: "Call Session"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Session for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Session for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
## Overview
This section demonstrates how to start a call session in a web application. Previously known as **Direct Calling**.
diff --git a/calls/v4/javascript/custom-css.mdx b/calls/v4/javascript/custom-css.mdx
index d8958297b..da7219915 100644
--- a/calls/v4/javascript/custom-css.mdx
+++ b/calls/v4/javascript/custom-css.mdx
@@ -2,9 +2,17 @@
title: "Custom CSS"
sidebarTitle: "Custom CSS"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Custom CSS for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Custom CSS for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
Passing custom CSS allows you to personalize and enhance the user interface of the call screen.
## Common CSS Classes
diff --git a/calls/v4/javascript/overview.mdx b/calls/v4/javascript/overview.mdx
index 63b8612df..71bfaab9a 100644
--- a/calls/v4/javascript/overview.mdx
+++ b/calls/v4/javascript/overview.mdx
@@ -2,12 +2,16 @@
title: "Calling SDK"
sidebarTitle: "Overview"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Overview for JavaScript. This is the stable v4 documentation. Most users should use this version."
+description: "CometChat Calling SDK v4 - Legacy Release - Overview for JavaScript. This is the legacy v4 documentation. Most users should use v5, the current stable version."
---
-
-🚀 **v5 Beta Available** — The Calling SDK v5 is now available in beta with significant improvements. [Check out the v5 docs →](/calls/javascript/overview)
-
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
## Overview
diff --git a/calls/v4/javascript/presenter-mode.mdx b/calls/v4/javascript/presenter-mode.mdx
index 8f292c6bb..5a26abbfe 100644
--- a/calls/v4/javascript/presenter-mode.mdx
+++ b/calls/v4/javascript/presenter-mode.mdx
@@ -2,9 +2,17 @@
title: "Presenter Mode"
sidebarTitle: "Presenter Mode"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Presenter Mode for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Presenter Mode for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
## Overview
The Presenter Mode feature allows developers to create a calling service experience in which:
diff --git a/calls/v4/javascript/recording.mdx b/calls/v4/javascript/recording.mdx
index 1b1d5e407..ec0ceed25 100644
--- a/calls/v4/javascript/recording.mdx
+++ b/calls/v4/javascript/recording.mdx
@@ -2,9 +2,17 @@
title: "Recording (Beta)"
sidebarTitle: "Recording"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Recording for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Recording for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
This section will guide you to implement call recording feature for the voice and video calls.
## Implementation
diff --git a/calls/v4/javascript/ringing.mdx b/calls/v4/javascript/ringing.mdx
index b0bacf63e..b77bd02e5 100644
--- a/calls/v4/javascript/ringing.mdx
+++ b/calls/v4/javascript/ringing.mdx
@@ -2,9 +2,17 @@
title: "Ringing"
sidebarTitle: "Ringing"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Ringing for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Ringing for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
## Overview
This section explains how to implement a complete calling workflow with ringing functionality, including incoming/outgoing call UI, call acceptance, rejection, and cancellation. Previously known as **Default Calling**.
diff --git a/calls/v4/javascript/session-timeout.mdx b/calls/v4/javascript/session-timeout.mdx
index 05fd7d6c8..cf55ba0c7 100644
--- a/calls/v4/javascript/session-timeout.mdx
+++ b/calls/v4/javascript/session-timeout.mdx
@@ -2,9 +2,17 @@
title: "Session Timeout Flow"
sidebarTitle: "Session Timeout"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Session Timeout for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Session Timeout for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
Available since v4.1.0
## Overview
diff --git a/calls/v4/javascript/setup.mdx b/calls/v4/javascript/setup.mdx
index 62909887e..dfcf5fc2e 100644
--- a/calls/v4/javascript/setup.mdx
+++ b/calls/v4/javascript/setup.mdx
@@ -2,9 +2,17 @@
title: "Setup"
sidebarTitle: "Setup"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Setup for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Setup for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
## Get your Application Keys
[Signup for CometChat](https://app.cometchat.com) and then:
diff --git a/calls/v4/javascript/standalone-calling.mdx b/calls/v4/javascript/standalone-calling.mdx
index 9682483be..78806d197 100644
--- a/calls/v4/javascript/standalone-calling.mdx
+++ b/calls/v4/javascript/standalone-calling.mdx
@@ -2,9 +2,17 @@
title: "Standalone Calling"
sidebarTitle: "Standalone Calling"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Standalone Calling for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Standalone Calling for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
## Overview
This section demonstrates how to implement calling functionality using only the CometChat Calls SDK, without requiring the Chat SDK. This is ideal for applications that need video/audio calling capabilities without the full chat infrastructure.
diff --git a/calls/v4/javascript/video-view-customisation.mdx b/calls/v4/javascript/video-view-customisation.mdx
index e63938916..6e5585929 100644
--- a/calls/v4/javascript/video-view-customisation.mdx
+++ b/calls/v4/javascript/video-view-customisation.mdx
@@ -2,9 +2,17 @@
title: "Video View Customisation"
sidebarTitle: "Video View Customisation"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Video View Customisation for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Video View Customisation for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
**Quick Reference**
- **Class:** `CometChat.MainVideoContainerSetting`
diff --git a/calls/v4/javascript/virtual-background.mdx b/calls/v4/javascript/virtual-background.mdx
index 5ff75ae60..4fbf9b4b2 100644
--- a/calls/v4/javascript/virtual-background.mdx
+++ b/calls/v4/javascript/virtual-background.mdx
@@ -2,9 +2,17 @@
title: "Virtual Background"
sidebarTitle: "Virtual Background"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Virtual Background for JavaScript"
+description: "CometChat Calling SDK v4 - Legacy Release - Virtual Background for JavaScript"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (JavaScript).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/javascript/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/javascript/migration-guide-v5).
+
+
**Quick Reference**
- **Settings class:** `CometChat.VirtualBackground`
diff --git a/calls/v4/react-native/call-logs.mdx b/calls/v4/react-native/call-logs.mdx
index 4c41c61f0..45b3db5a5 100644
--- a/calls/v4/react-native/call-logs.mdx
+++ b/calls/v4/react-native/call-logs.mdx
@@ -2,9 +2,17 @@
title: "Call Logs"
sidebarTitle: "Call Logs"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Logs for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Logs for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
**Quick Reference** - Fetch call logs:
diff --git a/calls/v4/react-native/call-session.mdx b/calls/v4/react-native/call-session.mdx
index 4f35a092b..82eb73dc4 100644
--- a/calls/v4/react-native/call-session.mdx
+++ b/calls/v4/react-native/call-session.mdx
@@ -2,9 +2,17 @@
title: "Call Session"
sidebarTitle: "Call Session"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Call Session for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Call Session for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
**Quick Reference** - Generate token and start a call session:
diff --git a/calls/v4/react-native/expo-integration-guide.mdx b/calls/v4/react-native/expo-integration-guide.mdx
index 9ff3f1fae..24e34a468 100644
--- a/calls/v4/react-native/expo-integration-guide.mdx
+++ b/calls/v4/react-native/expo-integration-guide.mdx
@@ -2,9 +2,17 @@
title: "Expo Integration"
sidebarTitle: "Expo Integration"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Expo Integration Guide for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Expo Integration Guide for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
**Quick Reference** - Key Expo setup steps:
diff --git a/calls/v4/react-native/overview.mdx b/calls/v4/react-native/overview.mdx
index 5d8af9ad8..9c6f72264 100644
--- a/calls/v4/react-native/overview.mdx
+++ b/calls/v4/react-native/overview.mdx
@@ -2,12 +2,16 @@
title: "Calling SDK"
sidebarTitle: "Overview"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Overview for React Native. This is the stable v4 documentation. Most users should use this version."
+description: "CometChat Calling SDK v4 - Legacy Release - Overview for React Native. This is the legacy v4 documentation. Most users should use v5, the current stable version."
---
-
-🚀 **v5 Beta Available** — The Calling SDK v5 is now available in beta with significant improvements. [Check out the v5 docs →](/calls/react-native/overview)
-
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
## Overview
diff --git a/calls/v4/react-native/presenter-mode.mdx b/calls/v4/react-native/presenter-mode.mdx
index 52f4bf3ac..81f01371e 100644
--- a/calls/v4/react-native/presenter-mode.mdx
+++ b/calls/v4/react-native/presenter-mode.mdx
@@ -2,9 +2,17 @@
title: "Presenter Mode"
sidebarTitle: "Presenter Mode"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Presenter Mode for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Presenter Mode for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
**Quick Reference** - Start a presentation session:
diff --git a/calls/v4/react-native/recording.mdx b/calls/v4/react-native/recording.mdx
index ba3529039..4742dd317 100644
--- a/calls/v4/react-native/recording.mdx
+++ b/calls/v4/react-native/recording.mdx
@@ -2,9 +2,17 @@
title: "Recording (Beta)"
sidebarTitle: "Recording"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Recording for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Recording for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
**Quick Reference** - Start and stop call recording:
diff --git a/calls/v4/react-native/ringing.mdx b/calls/v4/react-native/ringing.mdx
index 814271dc6..7149d63f1 100644
--- a/calls/v4/react-native/ringing.mdx
+++ b/calls/v4/react-native/ringing.mdx
@@ -2,9 +2,17 @@
title: "Ringing"
sidebarTitle: "Ringing"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Ringing for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Ringing for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
**Quick Reference** - Initiate a call and listen for events:
diff --git a/calls/v4/react-native/session-timeout.mdx b/calls/v4/react-native/session-timeout.mdx
index 12658f378..98381712c 100644
--- a/calls/v4/react-native/session-timeout.mdx
+++ b/calls/v4/react-native/session-timeout.mdx
@@ -2,9 +2,17 @@
title: "Session Timeout Flow"
sidebarTitle: "Session Timeout"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Session Timeout for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Session Timeout for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
Available since v4.2.0
## Overview
diff --git a/calls/v4/react-native/setup.mdx b/calls/v4/react-native/setup.mdx
index 6a6b7e07f..4116d0ca4 100644
--- a/calls/v4/react-native/setup.mdx
+++ b/calls/v4/react-native/setup.mdx
@@ -2,9 +2,17 @@
title: "Setup"
sidebarTitle: "Setup"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Setup for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Setup for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
**Quick Reference** - Install and initialize the Calls SDK:
diff --git a/calls/v4/react-native/standalone-calling.mdx b/calls/v4/react-native/standalone-calling.mdx
index 90070d7b1..710c688ed 100644
--- a/calls/v4/react-native/standalone-calling.mdx
+++ b/calls/v4/react-native/standalone-calling.mdx
@@ -2,9 +2,17 @@
title: "Standalone Calling"
sidebarTitle: "Standalone Calling"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Standalone Calling for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Standalone Calling for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
**Quick Reference** - Generate token and start a standalone call session:
diff --git a/calls/v4/react-native/video-view-customisation.mdx b/calls/v4/react-native/video-view-customisation.mdx
index 17a072dcc..53287158c 100644
--- a/calls/v4/react-native/video-view-customisation.mdx
+++ b/calls/v4/react-native/video-view-customisation.mdx
@@ -2,9 +2,17 @@
title: "Video View Customisation"
sidebarTitle: "Video View Customisation"
sdk_version: "4.x"
-description: "CometChat Calling SDK v4 - Stable Release - Video View Customisation for React Native"
+description: "CometChat Calling SDK v4 - Legacy Release - Video View Customisation for React Native"
---
+
+**You're reading the legacy documentation for Calling SDK v4 (React Native).**
+
+Follow this page only if your app is on **v4.x**. If you're using v5, see the [**Calling SDK v5 documentation**](/calls/react-native/overview) instead.
+
+Ready to move off v4? Follow the [v4 → v5 migration guide](/calls/react-native/migration-guide-v5).
+
+
**Quick Reference** - Customize the main video container:
diff --git a/docs.json b/docs.json
index cb43da08b..502f47077 100644
--- a/docs.json
+++ b/docs.json
@@ -1185,7 +1185,8 @@
"ui-kit/react-native/call-buttons",
"ui-kit/react-native/call-logs",
"ui-kit/react-native/ai-assistant-chat-history",
- "ui-kit/react-native/notification-feed"
+ "ui-kit/react-native/notification-feed",
+ "ui-kit/react-native/search"
]
},
{
@@ -5395,6 +5396,7 @@
"calls/javascript/ringing",
"calls/javascript/call-layouts",
"calls/javascript/recording",
+ "calls/javascript/transcription",
"calls/javascript/call-logs",
"calls/javascript/participant-management",
"calls/javascript/screen-sharing",
@@ -5510,6 +5512,7 @@
"calls/react-native/call-layouts",
"calls/react-native/call-logs",
"calls/react-native/recording",
+ "calls/react-native/transcription",
"calls/react-native/participant-management",
"calls/react-native/screen-sharing",
"calls/react-native/audio-modes",
@@ -5626,6 +5629,7 @@
"calls/ios/call-layouts",
"calls/ios/audio-modes",
"calls/ios/recording",
+ "calls/ios/transcription",
"calls/ios/call-logs",
"calls/ios/participant-management",
"calls/ios/screen-sharing",
@@ -5741,6 +5745,7 @@
"calls/android/call-layouts",
"calls/android/audio-modes",
"calls/android/recording",
+ "calls/android/transcription",
"calls/android/call-logs",
"calls/android/participant-management",
"calls/android/screen-sharing",
@@ -5855,6 +5860,7 @@
"calls/flutter/call-layouts",
"calls/flutter/audio-modes",
"calls/flutter/recording",
+ "calls/flutter/transcription",
"calls/flutter/call-logs",
"calls/flutter/participant-management",
"calls/flutter/screen-sharing",
@@ -6510,6 +6516,7 @@
"group": "Getting Started",
"pages": [
"notifications/android-push-notifications",
+ "notifications/ios-push-notifications-sdk",
"notifications/ios-apns-push-notifications",
"notifications/ios-fcm-push-notifications",
"notifications/flutter-push-notifications-android",
diff --git a/notifications/ios-apns-push-notifications.mdx b/notifications/ios-apns-push-notifications.mdx
index b6b86055e..6931637ce 100644
--- a/notifications/ios-apns-push-notifications.mdx
+++ b/notifications/ios-apns-push-notifications.mdx
@@ -11,6 +11,16 @@ description: "Implement APNs push notifications with CometChat UIKit for iOS, in
Reference implementation of iOS UIKit, APNs and Push Notification Setup.
+
+ **There is a drop-in alternative to this guide.** The
+ [iOS Push Notifications SDK](/notifications/ios-push-notifications-sdk)
+ (`CometChatPushNotifications`) ships everything below — token registration, foreground
+ presentation, quick reply, badge counts, PushKit and CallKit — as a dependency, so you do not
+ copy helper files or write a `CXProviderDelegate`. It works with or without the UI Kit.
+
+ Use this page when you need direct control over the PushKit and CallKit layer.
+
+
## What this guide covers
- CometChat dashboard setup (enable push, add APNs Device + APNs VoIP providers) with screenshots.
@@ -220,11 +230,12 @@ extension AppDelegate: PKPushRegistryDelegate, CXProviderDelegate {
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
print("User toggled mute: \(action.isMuted)")
- CometChatCalls.audioMuted(action.isMuted)
+ // v5: the deprecated CometChatCalls.audioMuted(_:) maps to these.
+ action.isMuted ? CallSession.shared.muteAudio() : CallSession.shared.unmuteAudio()
action.fulfill()
}
- // MARK: - CRITICAL: Audio Session Delegates (MISSING IN YOUR CODE)
+ // MARK: - Audio Session Delegates
/// Called when CallKit activates the audio session
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
@@ -232,15 +243,11 @@ extension AppDelegate: PKPushRegistryDelegate, CXProviderDelegate {
// Configure audio session for VoIP
configureAudioSession()
-
- // Removed CometChatCalls.startAudioSession() as per instructions
}
/// Called when CallKit deactivates the audio session
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
print("Audio session deactivated")
-
- // Removed CometChatCalls.stopAudioSession() as per instructions
}
// MARK: - Audio Session Configuration
@@ -797,7 +804,7 @@ extension CometChatAPNsHelper {
}
} else {
CometChat.endCall(sessionID: CometChat.getActiveCall()?.sessionID ?? "") { call in
- CometChatCalls.endSession()
+ CallSession.shared.leaveSession()
action.fulfill()
print("CallKit: End call success")
DispatchQueue.main.async { [self] in
diff --git a/notifications/ios-push-notifications-sdk.mdx b/notifications/ios-push-notifications-sdk.mdx
new file mode 100644
index 000000000..cd5a15ba6
--- /dev/null
+++ b/notifications/ios-push-notifications-sdk.mdx
@@ -0,0 +1,275 @@
+---
+title: "iOS Push Notifications SDK"
+description: "Drop-in push notifications and VoIP calling for iOS with CometChatPushNotifications — APNs and PushKit token registration, CallKit, foreground presentation, quick reply and badge counts."
+---
+
+
+ **This is the recommended way to add push notifications and VoIP calling to an iOS app.**
+ `CometChatPushNotifications` handles APNs and PushKit token registration, foreground
+ presentation, notification taps, quick reply, campaign receipts, badge counts and the whole
+ PushKit + CallKit incoming-call flow. You do not write a `PKPushRegistry` or a
+ `CXProviderDelegate`.
+
+ It works with **or without** the UI Kit. If you would rather wire APNs, PushKit and CallKit
+ yourself, see [iOS APNs Push Notifications](/notifications/ios-apns-push-notifications).
+
+
+## Requirements
+
+| | |
+|---|---|
+| Minimum iOS | **15.1** |
+| Chat SDK | `CometChatSDK` **4.1.5+** |
+| Calls SDK | `CometChatCallsSDK` **5.0.0+** (required for the VoIP/CallKit flow) |
+| Device | **A physical device.** VoIP pushes are not delivered to the Simulator |
+
+## Install
+
+
+
+In Xcode, **File → Add Package Dependencies** and add:
+
+```
+https://github.com/cometchat/push-notifications-sdk-ios
+```
+
+Pick version **1.0.0** or later, and add the **`CometChatPushNotificationsSwift`** library to your
+app target.
+
+
+```ruby
+platform :ios, '15.1'
+
+target 'YourApp' do
+ use_frameworks!
+ pod 'CometChatPushNotifications', '1.0.0'
+end
+```
+
+Then `pod install`. The pod pulls `CometChatSDK` and `CometChatCallsSDK` automatically.
+
+
+
+
+ The module you import is **`CometChatPushNotificationsSwift`**, not `CometChatPushNotifications`.
+ The latter is the name of the class inside it.
+
+
+## Before you start
+
+1. In the **CometChat dashboard**, enable Push Notifications and add your APNs credentials. Add an
+ **APNs Device** provider and an **APNs VoIP** provider, and copy the **Provider ID**.
+2. In Xcode, add these capabilities to your app target:
+ - **Push Notifications**
+ - **Background Modes** → **Voice over IP** and **Remote notifications**
+3. Add `NSMicrophoneUsageDescription` and `NSCameraUsageDescription` to your `Info.plist` — iOS
+ terminates the app at the first call permission request without them.
+
+## 1. Initialize
+
+Call this **after** `CometChat.init(...)`:
+
+```swift
+import CometChatPushNotificationsSwift
+
+CometChatPushNotifications.shared.initialize(config: .init(
+ providerId: "YOUR_PROVIDER_ID",
+ extensionGroupID: "group.com.yourcompany.yourapp" // optional — see the extension section
+))
+CometChatPushNotifications.shared.delegate = self
+```
+
+That single call requests notification permission, registers for remote **and** VoIP pushes,
+listens for login and logout to re-register and unregister tokens, and takes over
+`UNUserNotificationCenter` handling.
+
+
+ You do **not** call `CometChatNotifications.registerPushToken(...)` yourself. The SDK does it on
+ every login, for both the APNs device token and the PushKit VoIP token.
+
+
+## 2. Forward the APNs token
+
+In your `AppDelegate`:
+
+```swift
+func application(_ application: UIApplication,
+ didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
+ CometChatPushNotifications.shared.registerDeviceToken(deviceToken)
+}
+
+// Recommended — surfaces entitlement and provisioning problems during development.
+func application(_ application: UIApplication,
+ didFailToRegisterForRemoteNotificationsWithError error: Error) {
+ CometChatPushNotifications.shared.handleRegistrationFailure(error)
+}
+
+// Optional — silent/background pushes (campaign receipts, badge updates).
+func application(_ application: UIApplication,
+ didReceiveRemoteNotification userInfo: [AnyHashable: Any],
+ fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
+ CometChatPushNotifications.shared.handleBackgroundNotification(userInfo: userInfo)
+ completionHandler(.newData)
+}
+```
+
+`registerDeviceToken` takes optional `onSuccess` / `onError` closures if you want the result.
+
+
+ The token can arrive before `initialize(config:)` runs. The SDK caches it and registers it
+ automatically once you initialize and the user logs in, so ordering is not your problem.
+
+
+That is the only `AppDelegate` code required. There is no `PKPushRegistryDelegate` and no
+`CXProviderDelegate` to write.
+
+## 3. Implement the delegate
+
+Every method has a default no-op implementation — override only what you need.
+
+```swift
+extension MyRootCoordinator: CometChatPushNotificationsDelegate {
+
+ // MARK: Navigation (notification taps)
+ func navigateToChat(for user: User) { /* push your 1-1 chat screen */ }
+ func navigateToChat(for group: Group) { /* push your group chat screen */ }
+ func navigateToDefaultScreen() { /* fallback destination */ }
+
+ // MARK: Calls
+ // The call is ALREADY accepted on the server when this fires. Present your call UI
+ // and start the session (generate a token, then join).
+ func presentCallScreen(for call: Call, sessionId: String) { }
+
+ // The SDK has already ended the call and the CallKit session — just dismiss your UI.
+ func onCallCleanupComplete() { }
+
+ // MARK: Optional observability
+ func onCallMissed(call: Call, reason: CometChatMissedCallReason) { }
+ func onCallMuteStateChanged(isMuted: Bool) { }
+ func onPushTokenRegistered(platform: CometChatPushTokenPlatform) { }
+ func onPushTokenRegistrationFailed(platform: CometChatPushTokenPlatform,
+ error: CometChatException) {
+ // Fires for automatic registrations too — the fastest way to catch a wrong Provider ID.
+ }
+}
+```
+
+| Type | Values |
+|---|---|
+| `CometChatPushTokenPlatform` | `.apns` · `.fcm` · `.voip` |
+| `CometChatMissedCallReason` | `.unanswered` · `.cancelled` |
+
+## Cold start: tell the SDK when the Calls SDK is ready
+
+A VoIP push can wake your app *before* your Calls SDK is ready. The SDK buffers the
+`presentCallScreen` callback until you tell it to release.
+
+Call `notifyCallsSDKReady()` once **login has resolved** — not merely when init succeeds:
+
+```swift
+CometChatCalls.init(callsAppSettings: settings, onSuccess: { _ in
+ CometChatCalls.login(authToken: token, onSuccess: { _ in
+ // Release the buffered call only now — joining needs an authenticated session.
+ CometChatPushNotifications.shared.notifyCallsSDKReady()
+ }, onError: { _ in })
+}, onError: { _ in })
+```
+
+
+ **Release it after login, not after init.** `notifyCallsSDKReady()` only lifts the buffer — it
+ does not check whether anyone is logged in. Calling it at init success releases the call while
+ the session is still unauthenticated, and the `joinSession` that follows `presentCallScreen`
+ fails on auth.
+
+
+
+ There is a **3-second safety timeout**: if `notifyCallsSDKReady()` has not been called by then,
+ the SDK fires the buffered `presentCallScreen` anyway so the call is never silently dropped. A
+ cold start whose login takes longer than 3 seconds will therefore reach `presentCallScreen`
+ before login completes — handle that in your call screen rather than assuming a live session.
+
+
+## Notification Service Extension
+
+Adds delivery receipts, sender avatars, campaign images and markdown-free notification text.
+
+
+
+ **File → New → Target → Notification Service Extension.**
+
+
+ Add the same App Group (e.g. `group.com.yourcompany.yourapp`) to **both** the app target and the
+ extension target, and pass it as `extensionGroupID` when you initialize.
+
+
+ Link `CometChatPushNotificationsSwift` to the extension target and replace the generated class:
+
+```swift
+import CometChatPushNotificationsSwift
+
+class NotificationService: CometChatNotificationServiceExtension {}
+```
+
+
+ Add a String entry `CometChatExtensionGroupID` set to your App Group ID in the **extension's**
+ `Info.plist`, or override the `extensionGroupID` property instead.
+
+
+
+Optional overrides: `stripsMarkdown`, `attachesMedia`, and `finalizeContent(_:)` for last-chance
+content customization.
+
+## Configuration reference
+
+```swift
+CometChatPushNotificationsConfig(
+ providerId: String, // required — from the CometChat dashboard
+ callkitIconName: String? = nil, // asset name for the CallKit icon
+ callkitRingtoneName: String? = nil, // custom ringtone file
+ enableBadgeCount: Bool = true,
+ showInAppNotifications: Bool = true, // foreground banners
+ extensionGroupID: String? = nil, // must match the Notification Service Extension
+ foregroundCallPresentation: .callKit, // .callKit | .inApp | .none
+ incomingCallStyle: CometChatIncomingCallStyle? = nil,
+ notificationContentModifier: ((UNMutableNotificationContent, CometChatNotificationInfo) -> UNMutableNotificationContent)? = nil
+)
+```
+
+`foregroundCallPresentation` decides what happens when a call arrives while the app is open:
+
+| Value | Behaviour |
+|---|---|
+| `.callKit` | Full-screen system CallKit UI (default) |
+| `.inApp` | The SDK's own `CometChatIncomingCallView`, stylable via `incomingCallStyle` |
+| `.none` | Nothing — you present your own UI from the delegate |
+
+## Other APIs
+
+| API | Use |
+|---|---|
+| `setActiveConversation(userId:)` / `setActiveConversation(groupId:)` | Suppress notifications for the chat the user is already looking at |
+| `clearActiveConversation()` | Call when that screen closes |
+| `clearBadgeCount()` | Reset the app badge |
+| `CometChatPushNotifications.parseNotificationInfo(from:)` | Read sender, receiver and body out of a raw payload |
+| `CometChatPushNotifications.isCampaignNotification(userInfo:)` | Distinguish campaign pushes from chat pushes |
+| `activeCallSessionId` | The session ID of the call currently in progress, if any |
+
+## Testing checklist
+
+- Run on a **physical device** — VoIP pushes never reach the Simulator.
+- Confirm both providers exist in the dashboard and that `providerId` matches the one you passed.
+- Watch `onPushTokenRegistrationFailed` — a wrong Provider ID shows up here first.
+- Test an incoming call in all three states: foreground, background, and app terminated
+ (cold start — this is what `notifyCallsSDKReady()` covers).
+
+## Related
+
+
+ Dashboard setup, providers and templates.
+
+
+ The hand-wired alternative, if you need full control over PushKit and CallKit.
+
+
+ The in-app 1:1 call signaling this builds on.
+
diff --git a/notifications/push-overview.mdx b/notifications/push-overview.mdx
index 6d83a545f..8616d813b 100644
--- a/notifications/push-overview.mdx
+++ b/notifications/push-overview.mdx
@@ -47,8 +47,12 @@ CometChat listens for chat and call events, assembles payloads from your templat
UI Kit implementation
-} href="/notifications/ios-apns-push-notifications">
-UI Kit implementation
+} href="/notifications/ios-push-notifications-sdk">
+Drop-in SDK — APNs, VoIP and CallKit handled for you. Works with or without the UI Kit.
+
+
+} href="/notifications/ios-apns-push-notifications">
+UI Kit implementation, hand-wired PushKit + CallKit
} href="/notifications/ios-fcm-push-notifications">
diff --git a/ui-kit/react-native/component-styling.mdx b/ui-kit/react-native/component-styling.mdx
index b4e1c350e..8e23ad010 100644
--- a/ui-kit/react-native/component-styling.mdx
+++ b/ui-kit/react-native/component-styling.mdx
@@ -1115,6 +1115,79 @@ return (
To learn more about such attributes, refer to the [theme interface](https://github.com/cometchat/cometchat-uikit-react-native/blob/v5/packages/ChatUiKit/src/theme/type.ts).
+### Search
+
+The `CometChatSearch` component allows users to search through conversations and messages. It provides a user-friendly interface for finding specific content quickly, with customizable styles for various elements such as background colors, text appearances, and section headers.
+
+
+
+
+
+```javascript
+import {
+ CometChatSearch,
+ CometChatThemeProvider,
+} from "@cometchat/chat-uikit-react-native";
+//code
+return (
+
+
+ {loggedIn && }
+
+
+);
+```
+
+To learn more about such attributes, refer to the [theme interface](https://github.com/cometchat/cometchat-uikit-react-native/blob/v5/packages/ChatUiKit/src/theme/type.ts).
+
## Base Components
### Avatar
diff --git a/ui-kit/react-native/message-bubble-styling.mdx b/ui-kit/react-native/message-bubble-styling.mdx
index 5bb4f1789..9af6489e3 100644
--- a/ui-kit/react-native/message-bubble-styling.mdx
+++ b/ui-kit/react-native/message-bubble-styling.mdx
@@ -971,6 +971,100 @@ return (
To learn more about such attributes, refer to the [theme interface](https://github.com/cometchat/cometchat-uikit-react-native/blob/v5/packages/ChatUiKit/src/theme/type.ts).
+### Quoted Reply
+
+Quoted Reply bubbles display messages that are replies to other messages, showing the original message context within the reply bubble.
+
+**Default**
+
+
+
+
+
+**Customization**
+
+**Customizing Incoming and Outgoing Bubble**
+
+```tsx
+import {
+ CometChatThemeProvider,
+} from "@cometchat/chat-uikit-react-native";
+//other code
+return (
+
+ {/*your component*/}
+
+);
+```
+
+To learn more about such attributes, refer to the [theme interface](https://github.com/cometchat/cometchat-uikit-react-native/blob/v5/packages/ChatUiKit/src/theme/type.ts).
+
---
## Next Steps
diff --git a/ui-kit/react-native/message-header.mdx b/ui-kit/react-native/message-header.mdx
index 8fe58d75c..370ba21ba 100644
--- a/ui-kit/react-native/message-header.mdx
+++ b/ui-kit/react-native/message-header.mdx
@@ -372,6 +372,83 @@ TrailingView?: ({ user, group }) => JSX.Element
+***
+
+#### Options
+
+A function that returns custom menu items to **replace** the default menu items entirely. This allows you to define your own set of options for the header menu.
+
+Use Cases:
+
+* Create a custom options menu with specific actions for your use case.
+* Add info and search options with custom navigation.
+* Show different menu options based on user type or permissions.
+* Hide menu options for specific user types (like agentic users).
+
+
+
+```tsx
+import { CometChatMessageHeader } from "@cometchat/chat-uikit-react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+import { useMemo } from "react";
+//code
+const options = useMemo(() => {
+ return ({ user, group }: { user?: CometChat.User; group?: CometChat.Group }) => {
+ // For agentic users, don't show any options menu
+ if (agentic) {
+ return [];
+ }
+
+ const menuOptions = [];
+
+ // Add info option first
+ if (group && loggedInUser) {
+ menuOptions.push({
+ text: 'Group Info',
+ onPress: () => {
+ navigation.navigate('GroupInfo', { group });
+ },
+ icon: ,
+ });
+ } else if (user && !user.getBlockedByMe()) {
+ menuOptions.push({
+ text: 'User Info',
+ onPress: () => {
+ navigation.navigate('UserInfo', { user });
+ },
+ icon: ,
+ });
+ }
+
+ // Then add search option
+ menuOptions.push({
+ text: 'Search',
+ onPress: () => {
+ if (group) {
+ navigation.navigate('SearchMessages', { group });
+ } else if (user) {
+ navigation.navigate('SearchMessages', { user });
+ }
+ },
+ icon: ,
+ });
+
+ return menuOptions;
+ };
+}, [navigation, group, user, theme, agentic, loggedInUser]);
+
+return (
+
+);
+```
+
+
+
+
+
```tsx lines
import { CometChatMessageHeader } from "@cometchat/chat-uikit-react-native";
import { CometChat } from "@cometchat/chat-sdk-react-native";
diff --git a/ui-kit/react-native/search.mdx b/ui-kit/react-native/search.mdx
new file mode 100644
index 000000000..4c5ccbff8
--- /dev/null
+++ b/ui-kit/react-native/search.mdx
@@ -0,0 +1,937 @@
+---
+title: "Search"
+---
+
+## Overview
+
+The `CometChatSearch` component is a powerful and customizable search interface that allows users to search across conversations and messages in real time. It supports a wide variety of filters, scopes, and customization options. `CometChatSearch` helps users find messages, conversations, media, and more through an intuitive and filterable search experience. It can be embedded in multiple contexts — as part of the conversation list, message header, or as a full-screen search experience.
+
+
+
+
+
+***
+
+## Usage
+
+### Integration
+
+
+
+```tsx
+import React from "react";
+import { View } from "react-native";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+function SearchDemo() {
+ return (
+
+
+
+ );
+}
+
+export default SearchDemo;
+```
+
+
+
+
+```tsx
+import React from "react";
+import { View } from "react-native";
+import { SearchDemo } from "./SearchDemo";
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+
+
+
+
+***
+
+### Actions
+
+[Actions](/ui-kit/react-native/components-overview#actions) dictate how a component functions. They are divided into two types: Predefined and User-defined. You can override either type, allowing you to tailor the behavior of the component to fit your specific needs.
+
+#### 1. onConversationClicked
+
+`onConversationClicked` is triggered when you click on a Conversation from the search result. The `onConversationClicked` action doesn't have a predefined behavior. You can override this action using the following code snippet.
+
+
+
+```tsx
+import React from "react";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+
+const openConversation = (conversation: CometChat.Conversation) => {
+ console.log("Conversation Selected:", conversation);
+};
+
+;
+```
+
+
+
+
+
+***
+
+#### 2. onMessageClicked
+
+`onMessageClicked` is triggered when you click on a Message from the search result. The `onMessageClicked` action doesn't have a predefined behavior. You can override this action using the following code snippet.
+
+
+
+```tsx
+import React from "react";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+
+const goToMessage = (message: CometChat.BaseMessage) => {
+ console.log("Message Selected:", message);
+};
+
+;
+```
+
+
+
+
+
+***
+
+#### 3. OnBack
+
+`OnBack` is triggered when you click on the back button of the Message Header component. You can override this action using the following code snippet.
+
+
+
+```tsx
+import React from "react";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+const onBack = () => {
+ console.log("Back button pressed");
+};
+
+;
+```
+
+
+
+
+
+***
+
+#### 4. onError
+
+This action doesn't change the behavior of the component but rather listens for any errors that occur in the Conversations component.
+
+
+
+```tsx
+import React from "react";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+
+const handleOnError = (error: CometChat.CometChatException) => {
+ // Your exception handling code
+};
+
+;
+```
+
+
+
+
+
+***
+
+### Filters
+
+#### 1. ConversationsRequestBuilder
+
+You can set the `ConversationsRequestBuilder` in the Search Component to filter the search result. You can modify the builder as per your specific requirements with multiple options available to know more refer to [ConversationRequestBuilder](/sdk/react-native/retrieve-conversations).
+
+
+
+```tsx
+import React from "react";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+
+;
+```
+
+
+
+
+
+***
+
+#### 2. MessagesRequestBuilder
+
+You can set the `MessagesRequestBuilder` in the Search Component to filter the search result. You can modify the builder as per your specific requirements with multiple options available to know more refer to [MessagesRequestBuilder](/sdk/react-native/additional-message-filtering).
+
+
+
+```tsx
+import React from "react";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+
+;
+```
+
+
+
+
+
+***
+
+### Events
+
+[Events](/ui-kit/react-native/components-overview#events) are emitted by a `Component`. By using event you can extend existing functionality. Being global events, they can be applied in multiple locations and are capable of being added or removed.
+
+The `CometChatSearch` component does not produce any events.
+
+***
+
+## Customization
+
+To fit your app's design requirements, you can customize the appearance of the `CometChatSearch` component. We provide exposed methods that allow you to modify the experience and behavior according to your specific needs.
+
+### Style
+
+To customize the appearance, you can pass a custom `style` prop to `CometChatSearch`. The style prop accepts a `DeepPartial` object that allows you to override various style properties.
+
+**Example**
+
+
+
+
+
+
+
+```tsx
+import React from "react";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+import { SearchStyle } from "@cometchat/chat-uikit-react-native";
+
+const customSearchStyle: Partial = {
+ containerStyle: {
+ backgroundColor: "#E8EAF6",
+ },
+ headerStyle: {
+ backgroundColor: "#5F35AE",
+ paddingVertical: 16,
+ },
+ backButtonIconStyle: {
+ tintColor: "#FFFFFF",
+ },
+ searchInputContainerStyle: {
+ backgroundColor: "#FFFFFF",
+ borderRadius: 8,
+ },
+ searchInputStyle: {
+ color: "#000000",
+ fontSize: 16,
+ },
+ filterButtonStyle: {
+ backgroundColor: "#FFFFFF",
+ borderRadius: 20,
+ paddingHorizontal: 16,
+ paddingVertical: 8,
+ },
+ filterButtonActiveStyle: {
+ backgroundColor: "#5F35AE",
+ },
+ filterButtonTextStyle: {
+ color: "#5F35AE",
+ fontSize: 14,
+ },
+ filterButtonTextActiveStyle: {
+ color: "#FFFFFF",
+ },
+ sectionTitleStyle: {
+ color: "#000000",
+ fontSize: 18,
+ fontWeight: "700",
+ },
+ conversationItemStyle: {
+ containerStyle: {
+ backgroundColor: "#E8EAF6",
+ paddingVertical: 12,
+ },
+ titleStyle: {
+ color: "#000000",
+ fontSize: 16,
+ fontWeight: "600",
+ },
+ subtitleStyle: {
+ color: "#666666",
+ fontSize: 14,
+ },
+ },
+ messageItemStyle: {
+ containerStyle: {
+ backgroundColor: "#FFFFFF",
+ paddingVertical: 12,
+ },
+ titleStyle: {
+ color: "#000000",
+ fontSize: 16,
+ fontWeight: "600",
+ },
+ subtitleStyle: {
+ color: "#666666",
+ fontSize: 14,
+ },
+ },
+};
+
+;
+```
+
+
+
+
+
+### Functionality
+
+These are a set of small functional customizations that allow you to fine-tune the overall experience of the component. With these, you can change text, set custom icons, and toggle the visibility of UI elements.
+
+Here is a code snippet demonstrating how you can customize the functionality of the Search component.
+
+
+
+```tsx
+import React from "react";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+;
+```
+
+
+
+
+
+Following is a list of customizations along with their corresponding code snippets:
+
+| Property | Description | Code |
+| ------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| **uid** | A string representing the user ID in whose conversation the search will be performed. | `uid="user123"` |
+| **guid** | A string representing the group ID in whose conversation the search will be performed. | `guid="group456"` |
+| **Hide Back Button** | Hides the back button in the Search component. | `hideBackButton={true}` |
+| **Search Filters** | List of filters to be rendered in the Search component. | `searchFilters={[CometChatSearchFilter.Messages, CometChatSearchFilter.Photos, CometChatSearchFilter.Audio, CometChatSearchFilter.Documents]}` |
+| **Initial Search Filter** | The filter which will be active by default on load. | `initialSearchFilter={CometChatSearchFilter.Messages}` |
+| **Search In** | List of entities in which the search should be performed. | `searchIn={[CometChatSearchScope.Conversations]}` |
+| **Search Placeholder** | Custom placeholder text for the search input. | `searchPlaceholder="Search messages..."` |
+| **Loading View** | A custom component to display during the loading state. | `loadingView={() => }` |
+| **Empty View** | A custom component to display when there are no results available. | `emptyView={() => }` |
+| **Error View** | A custom component to display when an error occurs. | `errorView={() => }` |
+
+### Advanced
+
+For advanced-level customization, you can set custom views to the component. This lets you tailor each aspect of the component to fit your exact needs and application aesthetics. You can create and define your views, layouts, and UI elements and then incorporate those into the component.
+
+***
+
+#### ConversationItemView
+
+With this function, you can assign a custom list item view to a conversation in the search result.
+
+**Example**
+
+
+
+```tsx
+import React from "react";
+import { View, Text } from "react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+const getConversationItemView = (
+ conversation: CometChat.Conversation,
+ searchKeyword?: string
+) => {
+ const conversationWith = conversation.getConversationWith();
+ const lastMessage = conversation.getLastMessage() as CometChat.TextMessage;
+
+ return (
+
+
+
+ {conversationWith.getName()}
+
+
+ {lastMessage?.getText?.() || "No messages yet"}
+
+
+
+
+ {conversation.getUnreadMessageCount() > 0
+ ? conversation.getUnreadMessageCount()
+ : ""}
+
+
+
+ );
+};
+
+;
+```
+
+
+
+
+
+***
+
+#### TextMessageItemView
+
+With this function, you can assign a custom view for text messages in the search result.
+
+**Example**
+
+
+
+```tsx
+import React from "react";
+import { View, Text } from "react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+const getTextMessageItemView = (
+ message: CometChat.BaseMessage,
+ searchKeyword?: string
+) => {
+ const textMessage = message as CometChat.TextMessage;
+
+ return (
+
+
+ {message.getSender().getName()}:
+
+
+ {textMessage.getText()}
+
+
+ );
+};
+
+;
+```
+
+
+
+
+
+It should look like this in the app:
+
+
+
+
+
+***
+
+#### ImageMessageItemView
+
+With this function, you can assign a custom view for image messages in the search result.
+
+**Example**
+
+
+
+```tsx
+import React from "react";
+import { View, Text, Image } from "react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+const getImageMessageItemView = (
+ message: CometChat.BaseMessage,
+ searchKeyword?: string
+) => {
+ const imageMessage = message as CometChat.MediaMessage;
+ const attachment = imageMessage.getAttachment();
+ const imageUrl = attachment?.url;
+
+ return (
+
+
+
+ {message.getSender().getName()}
+
+
+ 📷 Photo
+
+
+ {imageUrl && (
+
+ )}
+
+ );
+};
+
+;
+```
+
+
+
+
+
+***
+
+#### VideoMessageItemView
+
+With this function, you can assign a custom view for video messages in the search result.
+
+**Example**
+
+
+
+```tsx
+import React from "react";
+import { View, Text, Image } from "react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+const getVideoMessageItemView = (
+ message: CometChat.BaseMessage,
+ searchKeyword?: string
+) => {
+ const videoMessage = message as CometChat.MediaMessage;
+ const attachment = videoMessage.getAttachment();
+ const thumbnailUrl = attachment?.thumbnail || attachment?.url;
+
+ return (
+
+
+
+ {message.getSender().getName()}
+
+
+ 🎥 Video
+
+
+ {thumbnailUrl && (
+
+
+
+
+ ▶
+
+
+
+ )}
+
+ );
+};
+
+;
+```
+
+
+
+
+
+***
+
+#### AudioMessageItemView
+
+With this function, you can assign a custom view for audio messages in the search result.
+
+**Example**
+
+
+
+```tsx
+import React from "react";
+import { View, Text } from "react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+const getAudioMessageItemView = (
+ message: CometChat.BaseMessage,
+ searchKeyword?: string
+) => {
+ const audioMessage = message as CometChat.MediaMessage;
+ const attachment = audioMessage.getAttachment();
+ const fileName = attachment?.name || "Audio Message";
+
+ return (
+
+
+
+ 🎵
+
+
+
+
+ {message.getSender().getName()}
+
+
+ {fileName}
+
+
+
+ );
+};
+
+;
+```
+
+
+
+
+
+***
+
+#### DocumentMessageItemView
+
+With this function, you can assign a custom view for document/file messages in the search result.
+
+**Example**
+
+
+
+```tsx
+import React from "react";
+import { View, Text } from "react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+const getDocumentMessageItemView = (
+ message: CometChat.BaseMessage,
+ searchKeyword?: string
+) => {
+ const fileMessage = message as CometChat.MediaMessage;
+ const attachment = fileMessage.getAttachment();
+ const fileName = attachment?.name || "Document";
+ const fileExtension = fileName.split('.').pop()?.toUpperCase() || "FILE";
+
+ return (
+
+
+
+ {fileExtension}
+
+
+
+
+ {message.getSender().getName()}
+
+
+ {fileName}
+
+
+
+ );
+};
+
+;
+```
+
+
+
+
+
+***
+
+#### LinkMessageItemView
+
+With this function, you can assign a custom view for link messages (text messages with link previews) in the search result.
+
+**Example**
+
+
+
+```tsx
+import React from "react";
+import { View, Text, Image } from "react-native";
+import { CometChat } from "@cometchat/chat-sdk-react-native";
+import { CometChatSearch } from "@cometchat/chat-uikit-react-native";
+
+const getLinkMessageItemView = (
+ message: CometChat.BaseMessage,
+ searchKeyword?: string
+) => {
+ const textMessage = message as CometChat.TextMessage;
+ const metadata = textMessage.getMetadata();
+ const linkPreview = metadata?.['@injected']?.extensions?.['link-preview'];
+ const firstLink = linkPreview?.links?.[0];
+ const thumbnailUrl = firstLink?.image || firstLink?.favicon;
+
+ return (
+
+ {thumbnailUrl && (
+
+ )}
+
+
+ {message.getSender().getName()}
+
+
+ {textMessage.getText()}
+
+ {firstLink?.title && (
+
+ 🔗 {firstLink.title}
+
+ )}
+
+
+ );
+};
+
+;
+```
+
+
+
+
+
+***
+
+Below is a summary table of all message item view customization functions:
+
+| Function | Message Type | Description |
+| ---------------------------- | ----------------------------- | -------------------------------------------------------------- |
+| **textMessageItemView** | Text Message | Custom view for text messages |
+| **imageMessageItemView** | Image Message | Custom view for image messages |
+| **videoMessageItemView** | Video Message | Custom view for video messages |
+| **audioMessageItemView** | Audio Message | Custom view for audio messages |
+| **documentMessageItemView** | Document/File Message | Custom view for document and file messages |
+| **linkMessageItemView** | Link Message | Custom view for text messages with link previews |
+
+
+
+***
+
+Assigns the list of text formatters. If the provided list is not null, it sets the list. Otherwise, it assigns the default text formatters retrieved from the data source.