--- description: Build scalable real-time applications with Cloudflare Realtime products including RealtimeKit, SFU, and TURN. title: Cloudflare Realtime image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Cloudflare Realtime Last updated Jul 28, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) Cloudflare Realtime is a comprehensive suite of products designed to help you build powerful, scalable real-time applications. ### RealtimeKit [RealtimeKit](https://developers.cloudflare.com/realtime/realtimekit/) is a set of SDKs and APIs that lets you add customizable live video and voice to web or mobile applications. It is fully customisable and lets you set up in just a few lines of code. It sits on top of the Realtime SFU, abstracting away the heavy lifting of media routing, peer management, and other complex WebRTC operations. ### Realtime SFU The [Realtime SFU (Selective Forwarding Unit)](https://developers.cloudflare.com/realtime/sfu/) is a powerful media server that efficiently routes video and audio. The Realtime SFU runs on [Cloudflare's global cloud network ↗](https://www.cloudflare.com/network/) in hundreds of cities worldwide. For developers with WebRTC expertise, the SFU can be used independently to build highly custom applications that require full control over media streams. This is recommended only for those who want to leverage Cloudflare's network with their own WebRTC logic. ### TURN Service The [TURN service](https://developers.cloudflare.com/realtime/turn/) is a managed service that acts as a relay for WebRTC traffic. It ensures connectivity for users behind restrictive firewalls or NATs by providing a public relay point for media streams. ## Choose the right Realtime product Use this comparison table to quickly find the right Realtime product for your needs: | | **RealtimeKit** | **Realtime SFU** | **TURN Service** | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | | **What is it** | High-level SDKs and APIs with pre-built UI components for video/voice integration. Built on top of Realtime SFU. | Low-level WebRTC media server (Selective Forwarding Unit) that routes audio/video/data streams between participants. | Managed relay service for WebRTC traffic that ensures connectivity through restrictive firewalls and NATs. | | **Who is it for** | Developers who want to quickly add video/voice features without handling WebRTC complexities. | Developers with WebRTC expertise who need full control over media streams and want to build highly custom applications. | Any WebRTC application needing reliable connectivity in restrictive network environments. | | **Effort to get started** | Low - Just a few lines of code with UI Kit and Core SDK. | High - Requires deep WebRTC knowledge. No SDK provided (unopinionated). You manage sessions, tracks, and presence protocol. Works with every WebRTC library. | Low - Automatically used by WebRTC libraries (browser WebRTC, Pion, libwebrtc). No additional code needed. | | **WebRTC expertise required** | None - Abstracts away WebRTC complexities. | Expert - You handle all WebRTC logic yourself. | None - Used transparently by WebRTC libraries. | | **Primitives** | Meetings, Sessions, Participants, Presets (roles), Stage, Waiting Room | Sessions (PeerConnections), Tracks (MediaStreamTracks), pub/sub model - no rooms concept | TURN allocations, relayed transport addresses, protocols (UDP/TCP/TLS) | | **Key use cases** | Team meetings, virtual classrooms, webinars, live streaming with interactive features, social video chat | Highly custom real-time apps, unique WebRTC architectures that don't fit standard patterns, leveraging Cloudflare's network with custom logic | Ensuring connectivity for all users regardless of firewall/NAT configuration, used alongside SFU or peer-to-peer WebRTC | | **Key features** | Pre-built UI components, automatic track management, recording, chat, polls, breakout rooms, virtual backgrounds, transcription | Unopinionated architecture, no lock-in, globally scalable, full control over media routing, programmable "switchboard" | Anycast routing to nearest location, multiple protocol options | | **Pricing** | Pricing by minute [view details ↗](https://workers.cloudflare.com/pricing#media) | $0.05/GB egress | Free when used with Realtime SFU, otherwise $0.05/GB egress | | **Free tier** | None | First 1,000 GB free each month | First 1,000 GB free each month | ## Related products [Workers AI](https://developers.cloudflare.com/workers-ai/) Run machine learning models, powered by serverless GPUs, on Cloudflare’s global network. [Stream](https://developers.cloudflare.com/stream/) Cloudflare Stream lets you or your end users upload, store, encode, and deliver live and on-demand video with one API, without configuring or maintaining infrastructure. ## More resources ### [Developer Discord](https://discord.cloudflare.com) Connect with the Realtime community on Discord to ask questions, show what you are building, and discuss the platform with other developers. ### [Use cases](https://developers.cloudflare.com/realtime/realtimekit/#build-with-realtimekit) Learn how you can build and deploy ambitious Realtime applications to Cloudflare's global network. ### [@CloudflareDev](https://x.com/cloudflaredev) Follow @CloudflareDev on Twitter to learn about product announcements, and what is new in Cloudflare Realtime. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/#page","headline":"Overview · Cloudflare Realtime docs","description":"Build scalable real-time applications with Cloudflare Realtime products including RealtimeKit, SFU, and TURN.","url":"https://developers.cloudflare.com/realtime/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-28","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Add live video and voice to web or mobile apps with RealtimeKit SDKs and APIs. title: RealtimeKit image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RealtimeKit Last updated Jun 12, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) Add live video and voice to your web or mobile apps in minutes — customizable SDKs, Integrate in just a few lines of code. With RealtimeKit, you can expect: * **Fast, simple integration:** Add live video and voice calling to any platform using our SDKs in minutes. * **Customizable:**Tailor the experience to your needs. * **Powered by WebRTC:**Built on top of modern, battle-tested WebRTC technology. RealtimeKit sits on top of [Realtime SFU](https://developers.cloudflare.com/realtime/sfu/) handling media track management, peer management, and other complicated tasks for you. Experience the product: [Try A Demo Meeting](https://examples.realtime.cloudflare.com/)[Build using Examples](https://github.com/cloudflare/realtimekit-web-examples)[RealtimeKit Dashboard](https://dash.cloudflare.com/?to=/:account/realtime/kit) ## Build with RealtimeKit RealtimeKit powers a wide range of usecases — here are the most common ones #### Group Calls Experience team meetings, virtual classrooms with interactive plugins, and seamless private or group video chats — all within your platform. #### Webinars Host large, interactive one-to-many events with virtual stage management, and engagement tools like plugins, chat, and polls — ideal for product demos, company all-hands, and live workshops #### Audio Only Calls Host audio-only calls — perfect for team discussions, support lines, and community hangouts— low bandwidth usage and features like mute controls, hand-raise, and role management. ## Product Suite * [**UI Kit**](https://developers.cloudflare.com/realtime/realtimekit/ui-kit) Recommended UI library of pre-built, customizable components for rapid development — sits on top of the Core SDK. * [**Core SDK**](https://developers.cloudflare.com/realtime/realtimekit/core) Client SDK built on top of Realtime SFU that provides a full set of APIs for managing video calls, from joining and leaving sessions to muting, unmuting, and toggling audio and video. * [**Realtime SFU**](https://developers.cloudflare.com/realtime/sfu) efficiently routes media with low latency—all running on Cloudflare’s global network for reliability and scale. The **Backend Infrastructure** Powering the SDKs is a robust layer that includes REST APIs for managing meetings, participants, recordings and more, along with webhooks for server-side events. A dedicated signalling server coordinates real-time updates. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/#page","headline":"Overview · Cloudflare Realtime docs","description":"Add live video and voice to web or mobile apps with RealtimeKit SDKs and APIs.","url":"https://developers.cloudflare.com/realtime/realtimekit/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-06-12","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Add AI-powered transcription and summarization to RealtimeKit meetings. title: AI image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # AI Last updated Jun 18, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/ai/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) RealtimeKit provides AI-powered features using Cloudflare's AI infrastructure to enhance your meetings with transcription and summarization capabilities. * [Transcription](https://developers.cloudflare.com/realtime/realtimekit/ai/transcription/) * [Summary](https://developers.cloudflare.com/realtime/realtimekit/ai/summary/) ## Available features | Feature | Description | | ----------------------------------------------------------------------------------------- | ----------------------------------------- | | [Transcription](https://developers.cloudflare.com/realtime/realtimekit/ai/transcription/) | Real-time and post-meeting speech-to-text | | [Summary](https://developers.cloudflare.com/realtime/realtimekit/ai/summary/) | AI-generated meeting summaries | ## Quick start Turn on post-meeting transcription and automatic summaries when creating a meeting: ```json { "title": "Team Standup", "transcribe_on_end": true, "summarize_on_end": true, "ai_config": { "transcription": { "language": "en" }, "summarization": { "word_limit": 500, "text_format": "markdown", "summary_type": "team_meeting" } } } ``` Use `transcribe_on_end` for post-meeting transcripts. Use `summarize_on_end` for AI-generated summaries. For real-time transcription, make sure participants have `transcription_enabled: true` in their [preset](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/). ## Storage and retention * Transcripts and summaries are stored for **7 days** after the meeting ends * Files are stored in R2 with presigned URLs for secure access * Delivered via [webhooks](https://developers.cloudflare.com/realtime/realtimekit/webhooks/) or REST API Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/ai/#page","headline":"AI · Cloudflare Realtime docs","description":"Add AI-powered transcription and summarization to RealtimeKit meetings.","url":"https://developers.cloudflare.com/realtime/realtimekit/ai/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-06-18","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Generate AI-powered meeting summaries from transcript data in RealtimeKit. title: Summary image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Summary Last updated Jun 8, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/ai/summary/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) RealtimeKit generates AI-powered meeting summaries from transcript data. Note Summarization requires a transcript. To generate summaries automatically when a meeting ends, turn on [real-time transcription](https://developers.cloudflare.com/realtime/realtimekit/ai/transcription/#real-time-transcription) or [post-meeting transcription](https://developers.cloudflare.com/realtime/realtimekit/ai/transcription/#post-meeting-transcription), and set `summarize_on_end: true`. With post-meeting transcription, RealtimeKit generates the summary after the transcript is available. ## Turn on summarization Set `summarize_on_end: true` when [creating a meeting](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/create/). For post-meeting summaries, also set `transcribe_on_end: true` so RealtimeKit generates the summary automatically after the transcript is available: ```bash curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/meetings" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Product Review", "transcribe_on_end": true, "summarize_on_end": true, "ai_config": { "transcription": { "language": "en-US" }, "summarization": { "word_limit": 500, "text_format": "markdown", "summary_type": "team_meeting" } } }' ``` ## Configuration | Option | Type | Default | Description | | ------------- | ------ | -------- | -------------------------------------- | | word\_limit | number | 500 | Summary length (150-1000 words) | | text\_format | string | markdown | Output format: plain\_text or markdown | | summary\_type | string | general | Meeting context for tailored summaries | ### Summary types Choose a type that matches your meeting for better results: | Type | Best for | | --------------------- | ---------------------------- | | general | Any meeting (default) | | team\_meeting | Regular team syncs | | daily\_standup | Agile standups | | one\_on\_one\_meeting | 1:1 meetings | | sales\_call | Customer sales conversations | | client\_check\_in | Client status updates | | interview | Job interviews | | lecture | Educational content | | code\_review | Technical code reviews | ## Consume summaries ### Webhook Configure the `meeting.summary` event in [RealtimeKit webhooks](https://developers.cloudflare.com/realtime/realtimekit/webhooks/#meetingsummary) to receive the summary download URL when it is ready: ```json { "event": "meeting.summary", "meeting": { "id": "bbb8940e-1b97-402a-97d6-2708b7feca41", "sessionId": "05e57591-d89e-45c9-ae44-08dc1eaad0e0", "organizedBy": { "id": "c94c437b-592a-4a39-b9e2-47ef1451e43b", "name": "Example organization" } }, "summaryDownloadUrl": "https://example.com/summary.txt", "summaryDownloadUrlExpiry": "2026-06-10T10:30:00.000Z" } ``` ### REST API #### Fetch summary Refer to [Fetch summary of transcripts for a session](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/sessions/methods/get%5Fsession%5Fsummary/). ```bash curl -X GET "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/sessions/$SESSION_ID/summary" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" ``` #### Trigger manually Use the [Generate summary of transcripts for the session](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/sessions/methods/generate%5Fsummary%5Fof%5Ftranscripts/) API if `summarize_on_end` was not set and you want to generate a summary manually after the transcript is available. ```bash curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/sessions/$SESSION_ID/summary" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" ``` ## Example output With `text_format: "markdown"` and `summary_type: "team_meeting"`: ```markdown ## Meeting Summary ### Key Discussion Points - Reviewed Q4 roadmap priorities - Discussed deployment timeline for v2.0 - Identified blockers for the auth migration ### Action Items - @alice: Update design specs by Friday - @bob: Schedule security review - @charlie: Create migration runbook ### Decisions Made - Approved moving forward with Kubernetes migration - Delayed analytics dashboard to next sprint ``` ## Retention Summaries are stored for **7 days** after the meeting ends. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/ai/summary/#page","headline":"Summary · Cloudflare Realtime docs","description":"Generate AI-powered meeting summaries from transcript data in RealtimeKit.","url":"https://developers.cloudflare.com/realtime/realtimekit/ai/summary/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-06-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Turn on real-time and post-meeting speech-to-text transcription in RealtimeKit. title: Transcription image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Transcription Last updated Jun 8, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/ai/transcription/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) RealtimeKit provides two transcription modes powered by [Cloudflare Workers AI](https://developers.cloudflare.com/workers-ai/): | Mode | Model | Processing time | Use case | | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------- | | [**Real-time**](#real-time-transcription) | [Deepgram Nova-3](https://developers.cloudflare.com/workers-ai/models/nova-3/) | During the meeting | Live captions for attendees | | [**Post-meeting**](#post-meeting-transcription) | [Whisper Large v3 Turbo](https://developers.cloudflare.com/workers-ai/models/whisper-large-v3-turbo/) | After the meeting ends | [Transcript files](#output-formats) and [webhooks](https://developers.cloudflare.com/realtime/realtimekit/webhooks/) | RealtimeKit processes each participant audio stream separately. This helps identify each speaker in the final transcript. We recommend upgrading to the Workers Paid plan to avoid Workers AI processing limits on the Free plan. Learn more in [Billing and Free plan limits](#billing-and-free-plan-limits). ## Real-time transcription Real-time transcription streams participant audio to [Deepgram Nova-3](https://developers.cloudflare.com/workers-ai/models/nova-3/) on Workers AI and sends [transcript events](#consume-real-time-transcripts) to meeting participants during the meeting. ### Turn on real-time transcription You can turn on real-time transcription for participants by setting `permissions.transcription_enabled: true` in the participant's [preset](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/). This lets you decide which participant audio is transcribed. For example, you can transcribe speaker audio without transcribing audience audio. To update an existing preset, use the [Update a preset API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/presets/methods/update/): ```bash curl -X PATCH "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/presets/$PRESET_ID" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "permissions": { "transcription_enabled": true } }' ``` To create a preset, refer to the [Create a preset API reference](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/presets/methods/create/). RealtimeKit transcribes audio only for participants who join with a preset that has `permissions.transcription_enabled: true`. During the meeting, RealtimeKit streams transcript updates to the client SDK. To access existing transcripts from `meeting.ai.transcripts` or listen for new transcript events with `meeting.ai.on("transcript", ...)`, refer to [Consume real-time transcripts](#consume-real-time-transcripts). ### Configure transcription settings The preset controls whose audio is transcribed. The meeting configuration controls how RealtimeKit transcribes that audio. Use `ai_config.transcription` to set the [spoken language](#real-time-supported-languages), boost recognition for custom terms, and control profanity filtering for a specific meeting. ```bash curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/meetings" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Weekly product review", "ai_config": { "transcription": { "language": "en-US", "keywords": ["RealtimeKit", "Cloudflare"], "profanity_filter": false } } }' ``` | Option | Type | Default | Description | | ----------------- | ---------- | ------- | ------------------------------------------ | | language | string | en-US | Language code for transcription | | keywords | string\[\] | \[\] | Terms to boost recognition (names, jargon) | | profanity\_filter | boolean | false | Filter offensive language | ### Real-time supported languages Real-time transcription is powered by [Deepgram Nova-3](https://developers.cloudflare.com/workers-ai/models/nova-3/) on Workers AI. Nova-3 on Workers AI supports the following languages for transcription: | Language | Code(s) | | ---------- | ------------------------------------- | | English | en, en-US, en-AU, en-GB, en-IN, en-NZ | | Spanish | es, es-419 | | French | fr, fr-CA | | German | de, de-CH | | Hindi | hi | | Russian | ru | | Portuguese | pt, pt-BR, pt-PT | | Japanese | ja | | Italian | it | | Dutch | nl | Use `multi` for automatic multilingual detection across all of the languages listed above. If no language is specified, the model defaults to `en-US`. For best accuracy, explicitly set the language code matching your audio. ### Consume real-time transcripts Real-time transcription sends interim and final transcript updates to the client SDK. Use interim updates for live captions, and use final updates for transcript history or saved UI state. #### Client SDK ```js // Get transcript entries already received by the client. const transcripts = meeting.ai.transcripts; // Listen for transcript updates during the meeting. meeting.ai.on("transcript", (transcript) => { if (transcript.isPartialTranscript) { updateLiveCaption(transcript.peerId, transcript.transcript); return; } appendFinalTranscript(transcript); }); ``` #### Transcript payload ```json { "id": "1a2b3c4d-5678-90ab-cdef-1234567890ab", "name": "Alice", "peerId": "4f5g6h7i-8j9k-0lmn-opqr-1234567890st", "userId": "uvwxyz-1234-5678-90ab-cdefghijklmn", "customParticipantId": "abc123xyz", "transcript": "Hello everyone", "isPartialTranscript": false, "timestamp": 1716700000000 } ``` | Field | Description | | ------------------- | ------------------------------------------------------------- | | id | Unique transcript entry ID | | name | Display name of the participant who spoke | | peerId | Peer ID of the participant who spoke. Changes if they rejoin. | | userId | Persistent participant ID | | customParticipantId | Participant identifier set when the participant was added | | transcript | Transcribed text | | isPartialTranscript | true for interim updates, false for final updates | | timestamp | Unix epoch timestamp in milliseconds | --- ## Post-meeting transcription Post-meeting transcription generates a transcript after the meeting ends using [Whisper Large v3 Turbo](https://developers.cloudflare.com/workers-ai/models/whisper-large-v3-turbo/) on Workers AI and delivers it through a [webhook](https://developers.cloudflare.com/realtime/realtimekit/webhooks/) or [REST API](#rest-api). To identify speakers, RealtimeKit processes each participant's audio separately before creating the [final transcript](#output-formats). ### Turn on post-meeting transcription You can turn on post-meeting transcription when you create a meeting. Set `transcribe_on_end: true` to generate a transcript after the meeting ends. To also generate a [summary](https://developers.cloudflare.com/realtime/realtimekit/ai/summary/) automatically after the transcript is available, set `summarize_on_end: true`. ```bash curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/meetings" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Weekly product review", "transcribe_on_end": true, "summarize_on_end": true, "ai_config": { "transcription": { "language": "en" } } }' ``` Use `ai_config.transcription.language` to set the transcript language. For supported values, refer to [Post-meeting supported languages](#post-meeting-supported-languages). If `transcribe_on_end` is not set, RealtimeKit does not generate a post-meeting transcript. ### Post-meeting supported languages Post-meeting transcription supports [Whisper Large v3 Turbo](https://developers.cloudflare.com/workers-ai/models/whisper-large-v3-turbo/) language codes. Omit `ai_config.transcription.language` to let Whisper detect the spoken language. Common language codes include: | Language | Code | Language | Code | Language | Code | | -------- | ---- | -------- | ---- | ---------- | ---- | | English | en | Spanish | es | French | fr | | German | de | Hindi | hi | Portuguese | pt | | Japanese | ja | Italian | it | Dutch | nl | | Russian | ru | Chinese | zh | Cantonese | yue | Additional post-meeting language codes | Language | Code | | ----------------- | ---- | | Afrikaans | af | | Albanian | sq | | Amharic | am | | Arabic | ar | | Assamese | as | | Azerbaijani | az | | Bashkir | ba | | Basque | eu | | Belarusian | be | | Bengali | bn | | Bosnian | bs | | Breton | br | | Bulgarian | bg | | Catalan | ca | | Croatian | hr | | Czech | cs | | Danish | da | | Estonian | et | | Faroese | fo | | Finnish | fi | | Galician | gl | | Georgian | ka | | Greek | el | | Gujarati | gu | | Haitian Creole | ht | | Hausa | ha | | Hawaiian | haw | | Hebrew | he | | Hungarian | hu | | Icelandic | is | | Indonesian | id | | Javanese | jw | | Kannada | kn | | Kazakh | kk | | Khmer | km | | Korean | ko | | Lao | lo | | Latin | la | | Latvian | lv | | Lingala | ln | | Lithuanian | lt | | Luxembourgish | lb | | Macedonian | mk | | Malagasy | mg | | Malay | ms | | Malayalam | ml | | Maltese | mt | | Maori | mi | | Marathi | mr | | Mongolian | mn | | Myanmar | my | | Nepali | ne | | Norwegian | no | | Norwegian Nynorsk | nn | | Occitan | oc | | Pashto | ps | | Persian | fa | | Polish | pl | | Punjabi | pa | | Romanian | ro | | Sanskrit | sa | | Serbian | sr | | Shona | sn | | Sindhi | sd | | Sinhala | si | | Slovak | sk | | Slovenian | sl | | Somali | so | | Sundanese | su | | Swahili | sw | | Swedish | sv | | Tagalog | tl | | Tajik | tg | | Tamil | ta | | Tatar | tt | | Telugu | te | | Thai | th | | Tibetan | bo | | Turkmen | tk | | Turkish | tr | | Ukrainian | uk | | Urdu | ur | | Uzbek | uz | | Vietnamese | vi | | Welsh | cy | | Yiddish | yi | | Yoruba | yo | ### Output formats Post-meeting transcripts are available in multiple formats. Use CSV or JSON for application workflows, and use SRT or VTT when you need subtitle files. | Format | Use case | | -------- | ------------------------------------ | | **CSV** | Spreadsheets and data analysis | | **JSON** | Programmatic access | | **SRT** | Video subtitle files | | **VTT** | Web video captions ( element) | #### Examples ```csv "1000","peer-123","user-456","cust-789","Alice","Hello everyone" "3000","peer-234","user-567","cust-890","Bob","Hi Alice" ``` CSV rows use the following field order: start time in milliseconds, peer ID, user ID, custom participant ID, participant name, and transcript text. ```json [ { "startTime": 1000, "endTime": 2500, "sentence": "Hello everyone", "peerData": { "id": "peer-123", "userId": "user-456", "displayName": "Alice", "cpi": "cust-789", "joinedAt": "2024-08-07T10:15:29.000Z", "leftAt": "" } } ] ``` ```txt 1 00:00:01,000 --> 00:00:02,500 Alice: Hello everyone 2 00:00:03,000 --> 00:00:04,500 Bob: Hi Alice ``` ### Consume post-meeting transcripts After RealtimeKit finishes processing a post-meeting transcript, you can receive the transcript download URL through a webhook or fetch it with the REST API. Use webhooks for asynchronous backend workflows, and use the REST API when you need to retrieve the transcript for a specific session. #### Webhook Configure the `meeting.transcript` event in [RealtimeKit webhooks](https://developers.cloudflare.com/realtime/realtimekit/webhooks/#meetingtranscript): ```json { "event": "meeting.transcript", "meeting": { "id": "bbb8940e-1b97-402a-97d6-2708b7feca41", "title": "Weekly sync", "endedAt": "2026-06-03T10:30:00.000Z", "createdAt": "2026-06-03T10:00:00.000Z", "sessionId": "05e57591-d89e-45c9-ae44-08dc1eaad0e0", "startedAt": "2026-06-03T10:00:00.000Z", "status": "LIVE", "organizedBy": { "id": "c94c437b-592a-4a39-b9e2-47ef1451e43b", "name": "Example organization" } }, "transcriptDownloadUrl": "https://example.com/transcript.csv", "transcriptDownloadUrlExpiry": "2026-06-10T10:30:00.000Z" } ``` #### REST API Refer to [Fetch the complete transcript for a session](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/sessions/methods/get%5Fsession%5Ftranscripts/). ```bash curl -X GET "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/sessions/$SESSION_ID/transcript" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" ``` #### Transcript availability Transcripts are available for **7 days** after the meeting ends. Download or copy transcript files before the URL expiry time returned by the webhook or REST API. ## Billing and Free plan limits RealtimeKit's default transcription records each participant's audio track and processes it with Workers AI. Workers AI usage is billed to your Cloudflare account using [audio model pricing](https://developers.cloudflare.com/workers-ai/platform/pricing/#audio-model-pricing), which scales by participant audio minutes, not meeting duration. On the Workers Free plan, Workers AI includes 10,000 Neurons per day. To use more than 10,000 Neurons per day, upgrade to the [Workers Paid plan](https://developers.cloudflare.com/workers/platform/pricing/#workers). Workers Paid includes the same 10,000 daily free Neurons, then bills additional usage at `$0.011` per 1,000 Neurons. You can upgrade to the Workers Paid plan in the Cloudflare dashboard under **Manage account**. RealtimeKit transcription uses these Workers AI audio model rates: | Transcription mode | Workers AI model | Neurons per audio minute | | ------------------ | --------------------------------- | ------------------------ | | Post-meeting | @cf/openai/whisper-large-v3-turbo | 46.63 | | Real-time | @cf/deepgram/nova-3 WebSocket | 836.36 | ## Data processing and storage RealtimeKit transcription is a managed transcription workflow. When transcription is turned on, RealtimeKit processes participant audio with Workers AI and stores [transcript outputs](#output-formats) in RealtimeKit-managed storage. For real-time transcription, RealtimeKit streams audio from participants with transcription turned on to Workers AI and sends transcript updates to meeting participants during the meeting. For post-meeting transcription, RealtimeKit processes each participant's audio separately after the meeting ends, creates the final transcript files, and makes them available through a webhook or REST API. Storage for sensitive workloads Post-meeting transcription uses RealtimeKit-managed storage for intermediate participant audio tracks and transcript outputs. If you configured external storage for regular recordings, that storage configuration is not used for transcription processing. For workloads with stricter storage, retention, or processing requirements, use [RealtimeKit recording with custom cloud storage](https://developers.cloudflare.com/realtime/realtimekit/recording-guide/custom-cloud-storage/) instead of managed transcription. You can then run your own transcription pipeline with your preferred infrastructure, access controls, and retention policy. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/ai/transcription/#page","headline":"Transcription · Cloudflare Realtime docs","description":"Turn on real-time and post-meeting speech-to-text transcription in RealtimeKit.","url":"https://developers.cloudflare.com/realtime/realtimekit/ai/transcription/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-06-08","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Build audio-only experiences like voice rooms and support lines with RealtimeKit. title: Audio Only Calls image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Audio Only Calls Last updated Jun 18, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/audio-calls/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) RealtimeKit supports voice calls, allowing you to build audio-only experiences such as audio rooms, support lines, or community hangouts. In these meetings, participants use their microphones and hear others, but cannot use their camera. Voice meetings reduce bandwidth requirements and focus on audio communication. ## How Audio Calls Work A participant’s meeting experience is determined by the **Preset** applied to that participant. To run a voice meeting, ensure all participants join with a Preset that has meeting type set to `Voice`. For details on Presets and how to configure them, refer to [Preset](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/). ## Pricing When a participant joins with a `Voice` meeting type Preset, they are considered an **Audio-Only Participant** for billing. This is different from the billing for Audio/Video Participants. For detailed pricing information, refer to [Pricing](https://developers.cloudflare.com/realtime/realtimekit/pricing/). ## Building Audio Experiences You can build voice meeting experiences using either the UI Kit or the Core SDK. ### UI Kit UI Kit provides a pre-built meeting experience with customization options. When participants join with a `Voice` meeting type Preset, UI Kit automatically renders a voice-only interface. You can use the default meeting UI or build your own UI using UI Kit components. To get started, refer to [Build using UI Kit](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/). ### Core SDK Core SDK provides full control to build custom audio-only interfaces. Video-related APIs are non-functional for participants with `Voice` type Presets. To get started, refer to [Build using Core SDK](https://developers.cloudflare.com/realtime/realtimekit/core/). Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/audio-calls/#page","headline":"Audio Only Calls · Cloudflare Realtime docs","description":"Build audio-only experiences like voice rooms and support lines with RealtimeKit.","url":"https://developers.cloudflare.com/realtime/realtimekit/audio-calls/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-06-18","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Choose the right video resolution, simulcast settings, and frame rate for your RealtimeKit use case to optimize quality and bandwidth. title: Video resolution and simulcast image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Video resolution and simulcast Last updated Jun 12, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/best-practices/video-and-simulcast/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) Configure video resolution, simulcast, and frame rate in RealtimeKit to balance quality against bandwidth. The right settings depend on your grid layout, participant count, and use case. Note Bitrate estimates on this page are approximate ranges for typical WebRTC video. The lower end of each range reflects low-motion content such as a talking head. The upper end reflects high-motion content such as gaming, virtual property tours, or fitness classes. Actual bitrate varies with codec, scene complexity, and encoder settings. ## Choose the right resolution for your layout Video resolution directly affects bandwidth and CPU usage. Sending higher resolution than the display tile requires wastes upload bandwidth and decoding resources on every receiver. Configure the video resolution in your [preset](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/) based on the layout each participant type will see. ### 1:1 calls (two participants) Each participant occupies a large portion of the screen. Use `1280×720` (720p) as the default. For use cases where visual detail matters — coaching sessions, design reviews, or remote consultations — use `1920×1080` (1080p). Turn off simulcast for 1:1 calls. With only one receiver, encoding multiple layers adds CPU cost with no benefit. In a 1:1 call, each participant uploads one stream and downloads one stream. Upload and download bandwidth are equal. The following table shows per-participant bandwidth and hourly data transfer at 24 FPS for a 1:1 call: | Resolution | Bitrate (upload = download) | Upload per hour | Download per hour | | ----------------- | --------------------------- | --------------- | ----------------- | | 1280×720 (720p) | 1.5–6 Mbps | 675 MB–2.7 GB | 675 MB–2.7 GB | | 1920×1080 (1080p) | 3.5–14 Mbps | 1.6–6.3 GB | 1.6–6.3 GB | ### Podcast or broadcast (one to two speakers) When one or two speakers are the primary focus, push resolution higher. Use `1920×1080` (1080p) as the baseline. Use `3840×2160` (4K) when the host has sufficient upload bandwidth and the content benefits from high fidelity — product showcases, studio interviews, or live art demonstrations. Turn on simulcast so that viewers on constrained networks automatically receive a lower layer. The following table shows per-sender upload bandwidth and hourly data transfer at 24 FPS: | Resolution | Sender upload bitrate | Sender upload per hour | | ----------------- | --------------------- | ---------------------- | | 1920×1080 (1080p) | 3.5–14 Mbps | 1.6–6.3 GB | | 3840×2160 (4K) | 14–56 Mbps | 6.3–25.2 GB | Each viewer downloads one stream. The download bitrate matches the sender upload bitrate, or a lower simulcast layer if the viewer's network is constrained. Caution 4K requires 14–56 Mbps upload bandwidth per sender depending on scene complexity. Verify that the host network supports this before selecting it in the preset. ### Small group grid (3×3, nine participants) In a 3×3 grid, each tile is roughly one-third of the screen width. Turn on simulcast and set the sending resolution to `640×480` (480p). Each participant uploads one stream and downloads eight streams (one from each other participant). The following table shows per-participant bandwidth and hourly data transfer at 24 FPS: | Resolution | Upload bitrate (one stream) | Upload per hour | Download bitrate (8 streams) | Download per hour | | -------------- | --------------------------- | --------------- | ---------------------------- | ----------------- | | 640×480 (480p) | 500 Kbps–2 Mbps | 225–900 MB | 4–16 Mbps | 1.8–7.2 GB | ### Large grids (4×4, 5×5, 6×4, or more) With 16 or more visible tiles, each tile is small. Set the sending resolution to `320×240` (240p) and turn on simulcast. Each participant uploads one stream and downloads one stream from every other participant. The following table shows per-participant bandwidth for a 4×4 grid (16 participants, 15 incoming streams) at 24 FPS: | Resolution | Upload bitrate (one stream) | Upload per hour | Download bitrate (15 streams) | Download per hour | | ----------------- | --------------------------- | --------------- | ----------------------------- | ----------------- | | 320×240 (240p) | 130–520 Kbps | 58–234 MB | 1.9–7.8 Mbps | 870 MB–3.5 GB | | 640×480 (480p) | 500 Kbps–2 Mbps | 225–900 MB | 7.5–30 Mbps | 3.4–13.5 GB | | 1280×720 (720p) | 1.5–6 Mbps | 675 MB–2.7 GB | 22.5–90 Mbps | 10.1–40.5 GB | | 1920×1080 (1080p) | 3.5–14 Mbps | 1.6–6.3 GB | 52.5–210 Mbps | 23.6–94.5 GB | At `320×240`, each participant needs 2–8 Mbps download. The lower end is practical even on mobile networks. At `1920×1080`, the same grid requires 52–210 Mbps download, which exceeds most residential connections. Tip For grids with 16 or more participants, `320×240` with simulcast keeps the session usable across a wide range of networks. The visual difference on small tiles is negligible. ### Pinned speaker with gallery (all participants send video) For layouts where all participants send video but one speaker is pinned at a larger size — town halls, classrooms, or team standups — use different presets for each role. Set the speaker preset resolution to `1280×720` or `1920×1080` because the speaker tile is large and benefits from higher quality. Set the participant preset resolution to `320×240` because participant tiles are small thumbnails. The following table shows bandwidth for a town hall with one speaker at 720p and 15 participants at 240p, all at 24 FPS. Every participant sends video: | Role | Upload bitrate | Upload per hour | Download bitrate | Download per hour | | ---------------------------------------------------------------------------------- | -------------- | --------------- | ---------------- | ----------------- | | Speaker (one stream at 720p, receives 15 streams at 240p) | 1.5–6 Mbps | 675 MB–2.7 GB | 1.9–7.8 Mbps | 870 MB–3.5 GB | | Participant (one stream at 240p, receives one stream at 720p + 14 streams at 240p) | 130–520 Kbps | 58–234 MB | 3.3–13.3 Mbps | 1.5–6 GB | ### Webinar (audience is view-only) In a webinar, audience members do not send audio or video unless they are invited on stage. Only the speaker uploads a stream. Use a high-resolution preset for the speaker and a view-only preset for the audience. The following example shows a webinar with one speaker at 720p and 15 view-only audience members, all at 24 FPS: | Role | Upload bitrate | Upload per hour | Download bitrate | Download per hour | | ------------------------------------------------------------ | -------------- | --------------- | ---------------- | ----------------- | | Speaker (one stream at 720p, no other on-stage participants) | 1.5–6 Mbps | 675 MB–2.7 GB | 0 | 0 | | Audience member (view-only, receives one stream at 720p) | 0 | 0 | 1.5–6 Mbps | 675 MB–2.7 GB | ## Turn on simulcast for multi-participant sessions Simulcast encodes the sender's video at multiple resolutions simultaneously (for example, 720p, 360p, and 180p). The SFU selects the best layer for each receiver based on available bandwidth and tile size. **Turn on simulcast when:** * The session has three or more participants * Participants have varying network conditions (mobile and desktop mix) * The session is a webinar or event with audience on constrained networks **Turn off simulcast** for 1:1 calls (one receiver, no benefit from multiple layers) and for audio-only sessions. Simulcast increases CPU usage on the sender because the device encodes multiple layers. If your use case targets low-powered devices in remote regions with limited bandwidth, consider turning off simulcast and setting a single lower resolution (such as `320×240`) for all participants instead. This avoids the extra encoding cost while still keeping bandwidth low. For sessions where participants have a mix of device capabilities and network conditions, simulcast provides the best experience because the SFU delivers the appropriate layer to each receiver. ## Choose the right frame rate Frame rate (FPS) directly multiplies bandwidth. Doubling the frame rate roughly doubles the required bitrate at the same resolution. Choose the lowest frame rate that meets your use case. | FPS | Bandwidth impact | Best for | | ------ | ------------------------ | ------------------------------------------------------------------------ | | 15 FPS | Lowest | Slide presentations, screen shares of static content, surveillance feeds | | 24 FPS | Moderate | Standard video calls, webinars, online classrooms, telemedicine | | 30 FPS | Higher | Interactive workshops, live product demos, fitness classes | | 60 FPS | Highest (\~2x of 30 FPS) | Live sports broadcasting, real-time music performances, esports | ### Industry examples * **Education**: Use 15 FPS for lecture screen shares and 24 FPS for the instructor camera. Students on mobile networks benefit from lower bandwidth. * **Healthcare**: Use 24 FPS for telemedicine consultations. Higher frame rates provide minimal benefit for conversation-style video. * **Fitness and wellness**: Use 30 FPS for live workout classes. Participants need smooth motion to follow physical movements. * **Media and entertainment**: Use 60 FPS for live music performances or sports watch parties where motion clarity matters. * **Corporate meetings**: Use 24 FPS for all-hands and standups. Talking-head video does not benefit from higher frame rates. * **Customer support**: Use 15 FPS for support sessions that involve mostly screen sharing. Lower bandwidth reduces dropped frames on the customer end. ### Combine resolution and frame rate Resolution and frame rate compound. The following table shows per-participant bandwidth for a 4×4 grid (15 incoming streams) at different combinations: | Configuration | Upload bitrate | Upload per hour | Download bitrate (15 streams) | Download per hour | | -------------- | --------------- | --------------- | ----------------------------- | ----------------- | | 720p at 30 FPS | 1.9–8 Mbps | 855 MB–3.6 GB | 29–116 Mbps | 13–52.2 GB | | 480p at 24 FPS | 500 Kbps–2 Mbps | 225–900 MB | 7.5–30 Mbps | 3.4–13.5 GB | | 240p at 15 FPS | 80–320 Kbps | 36–144 MB | 1.2–4.8 Mbps | 540 MB–2.2 GB | For large-grid use cases like virtual classrooms or all-hands meetings, `320×240` at 15 FPS with simulcast keeps total download bandwidth under 5 Mbps per participant even for high-motion content. ## Summary | Use case | Resolution | Simulcast | FPS | Notes | | ------------------------ | ------------------------------------------ | --------- | ----- | ---------------------------------------------------------- | | 1:1 call | 720p–1080p | Off | 24–30 | Upload and download are equal (one stream each) | | Podcast or broadcast | 1080p–4K | On | 24–30 | Verify host upload supports 4K (14–56 Mbps) | | 3×3 group call | 480p | On | 24 | 500 Kbps–2 Mbps upload, 4–16 Mbps download per participant | | 4×4+ large grid | 240p | On | 15–24 | 130–520 Kbps upload, 2–8 Mbps download per participant | | Pinned speaker + gallery | 720p–1080p (speaker), 240p (participants) | On | 24 | All participants send video, use separate presets per role | | Webinar | 720p–1080p (speaker), view-only (audience) | On | 24 | Audience does not upload unless invited on stage | | Screen sharing | Source resolution | Off | 15 | Low FPS is sufficient for static content | For more information on configuring presets, refer to [Presets](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/). Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/best-practices/video-and-simulcast/#page","headline":"Video resolution and simulcast · Cloudflare Realtime docs","description":"Choose the right video resolution, simulcast settings, and frame rate for your RealtimeKit use case to optimize quality and bandwidth.","url":"https://developers.cloudflare.com/realtime/realtimekit/best-practices/video-and-simulcast/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-06-12","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Send custom broadcast messages to all participants in a RealtimeKit meeting. title: Message Broadcast APIs image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Message Broadcast APIs Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/broadcast-apis/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The broadcast APIs allow a user to send custom messages to all other users in a meeting. WebMobile ReactWeb ComponentsAngular ### Broadcasting a Message The Participants module on the meeting object allows you to broadcast messages to all other users in a meeting (or to other meetings in case of connected meetings) over the signaling channel. | Param | Type | Description | Required | | ------- | ---------------------------- | ------------------------------------------------------------------------------------ | -------- | | type | Exclude | Message type identifier used to distinguish different kinds of broadcasts. | Yes | | payload | BroadcastMessagePayload | Data sent with the message. Keys map to boolean, number, string, Date, or ActiveTab. | Yes | | target | BroadcastMessageTarget | Optional target filter for which participants or meetings receive the message. | No | * If target is omitted, the message is broadcast to all participants in the current meeting, including the local participant. * If `target.participantIds` is provided, the message is sent only to those participants in the current meeting. * If `target.presetNames` is provided, the message is sent to all participants whose preset name is in the list. * If `target.meetingIds` is provided, the message is broadcast to all specified meetings (multi‑meeting broadcast). ```ts const participants = useRealtimeKitSelector((m) => m.participants); participants.broadcastMessage( type: Exclude, payload: BroadcastMessagePayload, target?: BroadcastMessageTarget, ): Promise ``` ```ts type BroadcastMessagePayload = { [key: string]: boolean | number | string | Date | ActiveTab; }; type BroadcastMessageTarget = | { participantIds: string[] } | { presetNames: string[] } | { meetingIds: string[] }; ``` | Param | Type | Description | Required | | ------- | ---------------------------- | ------------------------------------------------------------------------------------ | -------- | | type | Exclude | Message type identifier used to distinguish different kinds of broadcasts. | Yes | | payload | BroadcastMessagePayload | Data sent with the message. Keys map to boolean, number, string, Date, or ActiveTab. | Yes | | target | BroadcastMessageTarget | Optional target filter for which participants or meetings receive the message. | No | * If target is omitted, the message is broadcast to all participants in the current meeting, including the local participant. * If `target.participantIds` is provided, the message is sent only to those participants in the current meeting. * If `target.presetNames` is provided, the message is sent to all participants whose preset name is in the list. * If `target.meetingIds` is provided, the message is broadcast to all specified meetings (multi‑meeting broadcast). ```ts meeting.participants.broadcastMessage( type: Exclude, payload: BroadcastMessagePayload, target?: BroadcastMessageTarget, ): Promise ``` ```ts type BroadcastMessagePayload = { [key: string]: boolean | number | string | Date | ActiveTab; }; type BroadcastMessageTarget = | { participantIds: string[] } | { presetNames: string[] } | { meetingIds: string[] }; ``` | Param | Type | Description | Required | | ------- | ---------------------------- | ------------------------------------------------------------------------------------ | -------- | | type | Exclude | Message type identifier used to distinguish different kinds of broadcasts. | Yes | | payload | BroadcastMessagePayload | Data sent with the message. Keys map to boolean, number, string, Date, or ActiveTab. | Yes | | target | BroadcastMessageTarget | Optional target filter for which participants or meetings receive the message. | No | * If target is omitted, the message is broadcast to all participants in the current meeting, including the local participant. * If `target.participantIds` is provided, the message is sent only to those participants in the current meeting. * If `target.presetNames` is provided, the message is sent to all participants whose preset name is in the list. * If `target.meetingIds` is provided, the message is broadcast to all specified meetings (multi‑meeting broadcast). ```ts meeting.participants.broadcastMessage( type: Exclude, payload: BroadcastMessagePayload, target?: BroadcastMessageTarget, ): Promise ``` ```ts type BroadcastMessagePayload = { [key: string]: boolean | number | string | Date | ActiveTab; }; type BroadcastMessageTarget = | { participantIds: string[] } | { presetNames: string[] } | { meetingIds: string[] }; ``` ### Subscribe to Messages Use the `broadcastedMessage` event to listen for messages sent via `broadcastMessage` and handle them in your application. ```ts const participants = useRealtimeKitSelector((m) => m.participants); participants.on("broadcastedMessage", ({ type, payload, timestamp }) => { // handle message }); ``` ```ts meeting.participants.on( "broadcastedMessage", ({ type, payload, timestamp }) => { // handle message }, ); ``` ```ts meeting.participants.on( "broadcastedMessage", ({ type, payload, timestamp }) => { // handle message }, ); ``` ### Rate Limiting & Constraints * The method is rate‑limited (server‑side + client‑side) to prevent abuse. * Default client‑side config in the deprecated module: maxInvocations = 5 per period = 1s. * The Participants module exposes a `rateLimitConfig` and `updateRateLimits(maxInvocations, period)` for tuning on the client, but server‑side limits may still apply. * The event type cannot be `spotlight`. This is reserved for internal use by the SDK. ### Examples #### Broadcast to everyone in the meeting ```ts const participants = useRealtimeKitSelector((m) => m.participants); await participants.broadcastMessage("HAND_RAISE", { raised: true, userId: meeting.self.userId, sentAt: new Date(), }); participants.on( "broadcastedMessage", ({ type, payload, timestamp }) => { if (type === "HAND_RAISE") { // payload.raised, payload.userId, payload.sentAt } }, ); ``` ```ts await meeting.participants.broadcastMessage("HAND_RAISE", { raised: true, userId: meeting.self.userId, sentAt: new Date(), }); meeting.participants.on( "broadcastedMessage", ({ type, payload, timestamp }) => { if (type === "HAND_RAISE") { // payload.raised, payload.userId, payload.sentAt } }, ); ``` ```ts await meeting.participants.broadcastMessage("HAND_RAISE", { raised: true, userId: meeting.self.userId, sentAt: new Date(), }); meeting.participants.on( "broadcastedMessage", ({ type, payload, timestamp }) => { if (type === "HAND_RAISE") { // payload.raised, payload.userId, payload.sentAt } }, ); ``` #### Broadcast to a specific set of participants. Only the participants with those participantIds receive the message. ```ts const participants = useRealtimeKitSelector((m) => m.participants); await participants.broadcastMessage( "PRIVATE_NOTE", { message: "You are on stage in 30 seconds" }, { participantIds: ["peer-id-1", "peer-id-2"], }, ); ``` ```ts await meeting.participants.broadcastMessage( "PRIVATE_NOTE", { message: "You are on stage in 30 seconds" }, { participantIds: ["peer-id-1", "peer-id-2"], }, ); ``` ```ts await meeting.participants.broadcastMessage( "PRIVATE_NOTE", { message: "You are on stage in 30 seconds" }, { participantIds: ["peer-id-1", "peer-id-2"], }, ); ``` #### Broadcast to a preset All participants whose preset name is `speaker` receive the message. ```ts const participants = useRealtimeKitSelector((m) => m.participants); await participants.broadcastMessage( "STAGE_INSTRUCTION", { text: "Prepare for Q&A" }, { presetNames: ["speaker"], }, ); ``` ```ts await meeting.participants.broadcastMessage( "STAGE_INSTRUCTION", { text: "Prepare for Q&A" }, { presetNames: ["speaker"], }, ); ``` ```ts await meeting.participants.broadcastMessage( "STAGE_INSTRUCTION", { text: "Prepare for Q&A" }, { presetNames: ["speaker"], }, ); ``` #### Broadcast across multiple meetings All participants in the specified meetings receive the message. ```ts const participants = useRealtimeKitSelector((m) => m.participants); await participants.broadcastMessage( "GLOBAL_ANNOUNCEMENT", { text: "The event will end in 5 minutes." }, { meetingIds: ["meeting-1", "meeting-2"], }, ); ``` ```ts await meeting.participants.broadcastMessage( "GLOBAL_ANNOUNCEMENT", { text: "The event will end in 5 minutes." }, { meetingIds: ["meeting-1", "meeting-2"], }, ); ``` ```ts await meeting.participants.broadcastMessage( "GLOBAL_ANNOUNCEMENT", { text: "The event will end in 5 minutes." }, { meetingIds: ["meeting-1", "meeting-2"], }, ); ``` Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/broadcast-apis/#page","headline":"Message Broadcast APIs · Cloudflare Realtime docs","description":"Send custom broadcast messages to all participants in a RealtimeKit meeting.","url":"https://developers.cloudflare.com/realtime/realtimekit/broadcast-apis/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Create real-time key-value stores and broadcast messages to participants in RealtimeKit. title: Storage and Broadcast image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Storage and Broadcast Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/collaborative-stores/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RealtimeKit Stores API allows you to create multiple key-value pair realtime stores. Users can subscribe to changes in a store and receive real-time updates. Data is stored until a [session](https://developers.cloudflare.com/realtime/realtimekit/concepts/meeting/#session) is active. This page is not available for the **Flutter**platform. WebMobile ReactWeb ComponentsAngular ### Create a Store You can create a realtime store (changes are synced with other users): | Param | Type | Description | Required | | ----- | ------ | ----------------- | -------- | | name | string | Name of the store | true | To create a store: ```ts const stores = useRealtimeKitSelector((m) => m.stores); const store = stores.create('myStore'); ``` ```ts const store = meeting.stores.create('myStore'); ``` ```ts const store = meeting.stores.create('myStore'); ``` ```kotlin val meeting = RealtimeKitMeetingBuilder.build(activity) val store = meeting.stores.create("myStore") ``` ```swift let meeting = RealtimeKitiOSClientBuilder().build() let store = meeting.stores.create(name: "myStore") ``` This feature is not currently supported in the Flutter SDK Note This method must be executed for every user. ### Update a Store You can add, update or delete entries in a store: | Param | Type | Description | Required | | ----- | ---------- | ----------------------------------------------------------- | -------- | | key | string | Unique identifier used to store/update a value in the store | Yes | | value | StoreValue | Value that can be stored against a key | Yes | ```ts type StoreValue = string | number | object | array; ``` ```ts const stores = useRealtimeKitSelector((m) => m.stores.stores); const store = stores.get("myStore"); await store.set("user", { name: "John Doe" }); await store.update("user", { age: 34 }); // { name: 'John Doe', age: 34 } await store.delete("user"); ``` ```ts type StoreValue = string | number | object | array; ``` ```ts const { stores } = meeting.stores; const store = stores.get("myStore"); await store.set("user", { name: "John Doe" }); await store.update("user", { age: 34 }); // { name: 'John Doe', age: 34 } await store.delete("user"); ``` ```ts type StoreValue = string | number | object | array; ``` ```ts const { stores } = meeting.stores; const store = stores.get("myStore"); await store.set("user", { name: "John Doe" }); await store.update("user", { age: 34 }); // { name: 'John Doe', age: 34 } await store.delete("user"); ``` ```kotlin val store = meeting.stores.get("myStore") store.set("user", mapOf("name" to "John Doe")) ``` ```swift let store = meeting.stores.get(name: "myStore") store.set("user", ["name": "John Doe"]) ``` Note The `set` method overwrites the existing value, while the `update` method updates the existing value. For example, if the stored value is `['a', 'b']` and you call `update` with `['c']`, the final value will be `['a', 'b', 'c']`. ### Subscribe to a Store You can attach event listeners on a store's key, which fire when the value changes. ```ts const stores = useRealtimeKitSelector((m) => m.stores.stores); const store = stores.get('myStore'); store.subscribe('key', (data) => { console.log(data); }); // subscribe to all keys of a store store.subscribe('\*', (data) => { console.log(data); }); store.unsubscribe('key'); ``` ```ts const { stores } = meeting.stores; const store = stores.get('myStore'); store.subscribe('key', (data) => { console.log(data); }); // subscribe to all keys of a store store.subscribe('\*', (data) => { console.log(data); }); store.unsubscribe('key'); ``` ```ts const { stores } = meeting.stores; const store = stores.get('myStore'); store.subscribe('key', (data) => { console.log(data); }); // subscribe to all keys of a store store.subscribe('\*', (data) => { console.log(data); }); store.unsubscribe('key'); ``` ```kotlin val store = meeting.stores.create("myStore") val keyChangeCallback = { key: String, value: Any? -> println(value) } store.subscribe("key", keyChangeCallback) // Subscribe to all keys store.subscribe(RtkStore.WILDCARD_KEY) { key, value -> println(value) } store.unsubscribe("key", keyChangeCallback) ``` ```swift let store = meeting.stores.create(name: "myStore") let keyChangeCallback: ((String, (Any?)) -> Void) = { key, value in print(value ?? "null") } store.subscribe(key: "key", onChange: keyChangeCallback) // Subscribe to all keys store.subscribe(key: RtkStore.Companion().WILDCARD_KEY) { key, value in print(value ?? "null") } store.unsubscribe(key: "key", onChange: keyChangeCallback) ``` ### Fetch Store Data You can fetch the data stored in the store: ```ts const stores = useRealtimeKitSelector((m) => m.stores.stores); const store = stores.get('myStore'); // fetch value for a specific key const data = store.get('key'); // fetch all the data in the store const data = store.getAll(); ``` ```ts const { stores } = meeting.stores; const store = stores.get('myStore'); // fetch value for a specific key const data = store.get('key'); // fetch all the data in the store const data = store.getAll(); ``` ```ts const { stores } = meeting.stores; const store = stores.get('myStore'); // fetch value for a specific key const data = store.get('key'); // fetch all the data in the store const data = store.getAll(); ``` ```kotlin val store = meeting.stores.create("myStore") // fetch value for a specific key val data = store.get("key") // fetch all the data in the store val data = store.getAll() ``` ```swift let store = meeting.stores.create(name: "myStore") // fetch value for a specific key store.get(key: "key") // fetch all the data in the store store.getAll() ``` Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/collaborative-stores/#page","headline":"Storage and Broadcast · Cloudflare Realtime docs","description":"Create real-time key-value stores and broadcast messages to participants in RealtimeKit.","url":"https://developers.cloudflare.com/realtime/realtimekit/collaborative-stores/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Core concepts and terminology for RealtimeKit including apps, meetings, participants, and presets. title: Concepts image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Concepts Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/concepts/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This page outlines the core concepts and key terminology used throughout RealtimeKit. ### App An App represents a **workspace** within RealtimeKit. It groups together your meetings, participants, presets, recordings, and other configuration under an isolated namespace. Treat each App like an environment-specific container—most teams create one App for staging and another for production to avoid mixing data. ### Meeting A Meeting is a **re-usable virtual room** that you can join anytime. Every time participants join a meeting, a new [session](https://developers.cloudflare.com/realtime/realtimekit/concepts#session) is created. A session is marked `ENDED` shortly after the last participant leaves. A meeting can have only **one active session** at any given time. For more information about meetings, refer to [Meetings](https://developers.cloudflare.com/realtime/realtimekit/concepts#meeting). ### Session A Session is the **live instance of a meeting**. It is created when the first participant joins a meeting and ends shortly after the last participant leaves. Each session is independent, with its own participants, chat, and recordings. It also inherits the configurations set while creating the meeting - `record on start`, `persist_chat`, and more. Example - A recurring “Weekly Standup” **meeting will generate a new session** every time participants join. ### Participant A **Participant** is created when you add a user to a meeting via the [Add participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/). This API call returns a unique `authToken` that the client-side SDK uses to join the session and authenticate the user. > **Note:** Please do not re-use auth tokens for participants. For more information about participants, refer to [Participants](https://developers.cloudflare.com/realtime/realtimekit/concepts/participant/). ### Preset A Preset is a reusable set of permissions that defines the experience and the UI’s look and feel for a participant. Created at the App level, it can be applied to any participant across any meeting in that App. It also defines the meeting type a user joins—video call, audio call, or webinar. Participants in the same meeting can use different presets to create flexible roles. Example: In a large ed-tech class: * **Teacher** will join with a `webinar-host` preset, allowing them to share their media and providing host controls. * **Students** will join with a `webinar-participant` preset, which restricts them from sharing media but allows them to use features like chat. * **Teaching assistant** will join with a `group-call-host` preset, enabling them to share their media but not have full control. It also lets you customize the UI’s look and feel, including colors and themes, so the experience matches your application's branding. For more information about presets, refer to [Presets](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/). Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/concepts/#page","headline":"Concepts · Cloudflare Realtime docs","description":"Core concepts and terminology for RealtimeKit including apps, meetings, participants, and presets.","url":"https://developers.cloudflare.com/realtime/realtimekit/concepts/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: RealtimeKit meetings are reusable virtual rooms for real-time audio and video interaction. title: Meeting image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Meeting Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/concepts/meeting/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) Meeting is a **re-usable virtual room** that you can join and interact in, in real-time. You can assign a title and feature configuration to it, then add participants who are authorised to join. The Meeting itself doesn't "start" or "end"; it just exists. Because Meetings do not have a specific date or time, you can create them well in advance or create them just-in-time, right when users need to join. The following diagram shows the blueprint of a Meeting: --- title: Meeting --- flowchart TB accTitle: Meeting blueprint accDescr: Diagram showing blueprint of a Meeting subgraph details [ ] direction LR subgraph feat ["Features"] feat-content["Chat
...
Recording
...
Transcriptions"] end subgraph config ["Configuration"] config-content["record_on_start
...
persist_chat
...
ai_config"] end end subgraph participants ["Participants"] direction LR subgraph participants-row2 [ ] direction TB P3["
Participant 3
"] P4["
Participant 4
"] end subgraph participants-row1 [ ] direction TB P1["
Participant 1
"] P2["
Participant 2
"] end end style participants-row1 fill:none,stroke:none style participants-row2 fill:none,stroke:none style details fill:none,stroke:none ### Session A **Session** is a live instance of a Meeting. It starts automatically when the first participant joins the meeting and ends shortly after the last participant leaves. A Session inherits all settings (like features and title) from its parent Meeting. Because the Meeting is persistent, it can have many different Sessions over time. Example - **Think of a Meeting as a recurring weekly standup event.** The Meeting is the permanent “standup event” that exists in your system. Each week, when participants join for that week’s standup, a **new Session** is created — this Session represents that week’s actual live standup. > **Note**: This distinction is important for billing. You are charged on a per-participant basis only for the duration of an active Session, not for an idle Meeting. You can get the details of your sessions from the [RealtimeKit Dashboard ↗](https://dash.cloudflare.com/?to=/:account/realtime/kit) or using the [Sessions API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/sessions/) endpoints. ![Sessions in RealtimeKit Dashboard](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=3024,height=1616,format=webp/_astro/dashboard-sessions.dvN7oDwZ.png) ### Session Terminologies #### Waiting Room A **waiting room** lets participants join a meeting without immediately entering the live session. This gives hosts full control over **who gets in**, **when they enter**, and **how the meeting flow is managed**. Hosts can also configure specific behaviours for how users move from the waiting room into the meeting. * **Join when accepted by someone**Participants stay in the waiting room until a host or another authorized user explicitly admits them. Ideal for highly controlled or private meetings. * **Join when a privileged user joins**Participants remain in the waiting room initially, but are automatically admitted once a host or other privileged user enters the meeting. Useful for scheduled events where attendees should only join after the moderator is present. * **Accept users into waiting room**Hosts can see the list of waiting users, admit them individually or in bulk, or remove them. This mode provides maximum visibility and control over incoming participants. These options allow you to tailor how access is managed—whether you need strict admission control, a smoother flow once a host arrives, or a combination of both. #### Stage Meetings can be configured with a **virtual stage**, which helps you manage who actively participates with audio and video during high-attendance sessions. When a participant is **on the stage**, they are visible in the grid and they can publish their audio and video to everyone in the meeting. Participants who are **off the stage** cannot publish media, but they can still fully engage through features like **chat**, **polls**, **Q&A**, and **plugins**—making the experience interactive without overwhelming the live video layout. Participants can also **request to join the stage**, allowing them to signal when they want to speak or present. Hosts retain full control at all times: they can **approve or deny requests**, or directly **invite or remove participants from the stage** as needed. This setup is ideal for webinars, town halls, AMAs, and other structured events where only a subset of users should broadcast while everyone else participates from the audience. #### Connected Meetings Connected Meetings let you create linked meeting spaces, that participants can switch between during a session. This is useful for workshops, classrooms, parallel discussions, or any scenario where the main meeting splits into smaller groups before coming back together. You can control how participants move between these connected spaces using the following permissions: * **Full Access:** Allows participants to create, update, and delete connected meetings. * **Switch Connected Meeting:** Lets participants move freely between the available connected (child) meetings. * **Switch to Parent Meeting:** Allows participants to return to the main (parent) meeting at any time. ### Create a meeting You create and manage RealtimeKit meetings, typically from your backend, using the [Meetings API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/). To create a meeting, send a `POST` request to the [Create Meeting](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/create/) endpoint. API Prerequisites Make sure you have the following values for this API request: * Your Cloudflare `ACCOUNT_ID` * RealtimeKit `APP_ID` * Your `CLOUDFLARE_API_TOKEN` (with Realtime permissions) If you do not have them yet, refer to the [Getting Started](https://developers.cloudflare.com/realtime/realtimekit/quickstart/) guide. ```bash curl https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/realtime/kit/{APP_ID}/meetings \ --request POST \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --data '{ "title": "My First Cloudflare RealtimeKit meeting" }' ``` A successful response includes a unique `id` for the created meeting. Save this ID, as it is required for all future operations on this specific meeting, such as adding participants or disabling it. For a complete list of all available configuration parameters, refer to the [Create Meeting API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/create/). ### Where to Go Next After learning about Meetings and Sessions, you can explore the following next steps: * Configure [Presets](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/) for your App – Set up default permissions, media settings, and behavior for all Sessions created from a Meeting. * Add [Participants](https://developers.cloudflare.com/realtime/realtimekit/concepts/participant/) to a Meeting – Manage who can join, their roles, and the access controls they inherit. * Get started with [RealtimeKit SDKs](https://developers.cloudflare.com/realtime/realtimekit/quickstart/) – Integrate RealtimeKit into your web or mobile app with just a few lines of code. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/concepts/meeting/#page","headline":"Meeting · Cloudflare Realtime docs","description":"RealtimeKit meetings are reusable virtual rooms for real-time audio and video interaction.","url":"https://developers.cloudflare.com/realtime/realtimekit/concepts/meeting/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Add and manage participants in RealtimeKit meetings with tokens and presets. title: Participant image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Participant Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/concepts/participant/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) Before a user can join a meeting through the RealtimeKit SDK, your backend must add that user as a participant to that meeting using the [Add Participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/). In RealtimeKit, a **participant** represents a user who is allowed to join a specific meeting. You can think of this as enrolling a student into a classroom. The meeting is the classroom, and adding a participant is how you register a user so that they are allowed to attend. When you add a participant, you also choose which [preset](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/) to apply. The preset defines the role, permissions, and meeting experience of that participant. ### Participant tokens When you add a participant to a meeting using the [Add Participant](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/) API endpoint, it returns: * A participant `id` that identifies this participant within the meeting. * An authentication `token` for that participant. Your backend should make it available to your frontend application. When the user chooses to join the meeting, the frontend passes the token to the RealtimeKit SDK. RealtimeKit uses the token to authenticate the participant and determine which meeting and which participant is joining. Without a valid authentication token, the SDK cannot join the meeting on behalf of that participant. As long as a participant has a valid authentication token, that participant can join multiple live sessions of the same meeting over time. ### Token validity and refresh Participant authentication tokens are time bound and eventually expire. When a token expires, you do not need to create a new participant for the same user in the meeting. Instead, your backend can call the [Refresh Participant Token](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/refresh%5Fparticipant%5Ftoken/) API endpoint. This endpoint issues a new authentication token for the existing participant record, keeping details such as the participant `id` and preset the same. ### Custom participant identifier When adding the participant, you can optionally provide a custom participant identifier, referred to as `custom_participant_id`. This value is purely for your use. RealtimeKit stores it and returns it in APIs, but does not use it to control access. It allows you to map your application's user to RealtimeKit participant and to correlate RealtimeKit session data, events or analytics with user information in your system. Note **Do not** use personal data such as email address, phone number, or any other personally identifiable information as `custom_participant_id`. Use a stable internal identifier from your own system, such as a numeric user id or UUID. ### Where to Go Next After understanding participants, you can explore the following topics: * Learn how [Presets](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset) define roles and permissions for participants * [Get started with RealtimeKit SDKs](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/) Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/concepts/participant/#page","headline":"Participant · Cloudflare Realtime docs","description":"Add and manage participants in RealtimeKit meetings with tokens and presets.","url":"https://developers.cloudflare.com/realtime/realtimekit/concepts/participant/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Configure participant roles, permissions, and meeting experience with RealtimeKit presets. title: Preset image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Preset Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) A Preset is a **re-usable configuration** that defines a participant’s experience in a Meeting. It determines: * The meeting type they join (Video, Audio, or Webinar) * Actions they can perform (permissions and controls) * The UI’s look and feel, including colors and themes, so the experience matches your application's branding. Presets belong to an App, and they are applied to participants — not to meetings. You can assign the same Preset to multiple participants when creating them through the Add Participant API. Participants in the same Meeting can have different Presets, allowing each user to have a distinct role and experience. Example: Large Ed-Tech Classroom * **Teacher** uses the `webinar-host` preset — they can share media and access host controls. * **Students** use the `webinar-participant` preset — they cannot share media but can use features like chat. * **Teaching** assistant uses the `group-call-host` preset — they can share media but don’t have full host privileges. ### Create a Preset A set of default presets are created for you, when you create an app via the [Cloudflare dashboard ↗](https://dash.cloudflare.com/?to=/:account/realtime/kit). You can also create a preset using the [dashboard ↗](https://dash.cloudflare.com/?to=/:account/realtime/kit) or the [Create Preset API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/presets/methods/create/). ```bash curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/presets \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -d '{ "config": { ... }' ``` ### Preset Editor We provide a UI-based editor to create and manage the presets in the [Cloudflare dashboard ↗](https://dash.cloudflare.com/?to=/:account/realtime/kit). ![Preset Editor](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=3452,height=1898,format=webp/_astro/preset-editor.CoEtzs7E.png) The permissions are divided into the following categories: * **Host Controls:** These permissions allow the user to control the meeting, manage participants, and perform administrative actions like kicking users, muting video/audio for others and more. * **Stage Management:** Large meetings can be configured with a virtual stage. Participants on the stage can share their audio and video, while participants off the stage can view this media and still use features like chat, polls, and plugins. Users can request to join the stage, host can add/remove users from the stage at any point. Read more about stage management in [Meetings](https://developers.cloudflare.com/realtime/realtimekit/concepts/meeting#stage). * **Chat:** RealtimeKit allows users to send and receive messages in real time. You can also send private messages (visible only to a specific user). You can configure who has access to send & messages and receive various kinds of messages. * **Polls:** Allows user to configure who can create, view and interact with polls in the meeting. * **Plugins:** Plugins are interactive real-time applications that run inside the meeting to make collaboration easier. RealtimeKit lets you build your own plugins and also offers built-in options like Whiteboard and Document Sharing. You can control which plugins a participant is allowed to view, open, or close. * **Waiting Room:** A waiting room allows participants to join a meeting before they’re admitted, giving hosts control over who enters and when. It helps manage access, reduce interruptions, and ensure the meeting starts smoothly. Hosts can admit or remove participants at any time, and you can configure who should bypass the waiting room automatically. Read more about waiting rooms in [Meetings](https://developers.cloudflare.com/realtime/realtimekit/concepts/meeting#waiting-room). * **Connected Meetings:** Beta Connected Meetings let you meeting spaces linked to a main meeting, enabling smaller group discussions or parallel sessions. Participants can be given permission to move between meetings or return to the parent meeting. Hosts can create, update, or delete these connected spaces. Read more about connected meetings in [Meetings](https://developers.cloudflare.com/realtime/realtimekit/concepts/meeting#connected-meetings). * **Miscellaneous:** Miscellaneous permissions let you fine-tune additional aspects of the participant experience that don’t fall under specific categories. These options control capabilities like - editing names, viewing the participant list, syncing tab views, enabling transcriptions, and other supplementary features that enhance how users interact within the meeting. ### Where to Go Next After learning about Meetings and Sessions, you can explore the following next steps: * Add [Participants](https://developers.cloudflare.com/realtime/realtimekit/concepts/participant) to a Meeting – Manage who can join, their roles, and the access controls they inherit. * Get started with [RealtimeKit SDKs](https://developers.cloudflare.com/realtime/realtimekit/quickstart/) – Integrate RealtimeKit into your web or mobile app with just a few lines of code. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/#page","headline":"Preset · Cloudflare Realtime docs","description":"Configure participant roles, permissions, and meeting experience with RealtimeKit presets.","url":"https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Understand the lifecycle of a peer in a RealtimeKit session from setup to disconnect. title: Session Lifecycle image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Session Lifecycle Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/concepts/session-lifecycle/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The [Session Guide](https://developers.cloudflare.com/realtime/realtimekit/concepts/meeting/#session) explains what a session is and how to initialize one. In this guide we will talk about what happens to a peer as they move through a session, when do they go to the setup screen, waitlist screen, ended screen or any other screen, and how you can hook into these events to perform custom actions. ### Lifecycle of a Peer in a Session ![Peer Lifecycle In a Session](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=545,height=326,format=svg/_astro/peer-lifecycle.ChUtQdVP.svg) Here’s how the peer lifecycle works: 1. **Initialization state**: When the SDK is initialized, the peer first sees a Setup Screen, where they can preview their audio and video before joining. 2. **Join intent**: When the peer decides to join, one of two things happens: * If waitlisting is enabled, they are moved to a Waitlist and see a Waitlist screen. * If not waitlisted, they join the session and see the main Meeting screen (Stage), where they can interact with others. 3. **During the session**: The peer can see and interact with others in the main Meeting screen (Stage). 4. **Session transitions**: * If the peer is rejected from the waitlist, they see a dedicated Rejected screen. * If the peer is kicked out, they see an Ended screen and the session ends for them. * If the peer leaves voluntarily, or if the meeting ends, they see an Ended screen, and the session ends for them. Each of these screens is built with UI Kit components, which you can fully customize to match your app’s design and requirements. The UI Kit SDKs automatically handle which notifications or screens to show at each state, so you don’t have to manage these transitions manually. In upcoming pages, we will see how to hook into these events to perform custom actions and to build your own custom meeting experience. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/concepts/session-lifecycle/#page","headline":"Session Lifecycle · Cloudflare Realtime docs","description":"Understand the lifecycle of a peer in a RealtimeKit session from setup to disconnect.","url":"https://developers.cloudflare.com/realtime/realtimekit/concepts/session-lifecycle/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Initialize and build with the RealtimeKit Core SDK for full control over video calls. title: Build using Core SDK image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Build using Core SDK Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) ### Initialize Core SDK To integrate the Core SDK, you will need to initialize it with a [participant's auth token](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/), and then use the provided SDK APIs to control the peer in the session. Initialization might differ slightly based on your tech stack. Please choose your preferred tech stack below. WebMobile ReactWeb ComponentsAngular Install the client SDK ```bash npm i @cloudflare/realtimekit-react ``` Use the `useRealtimeKitClient` hook to initialise the SDK. ```ts import { useEffect } from 'react'; import { useRealtimeKitClient } from '@cloudflare/realtimekit-react'; export default function App() { const [meeting, initMeeting] = useRealtimeKitClient(); useEffect(() => { const meetingDefaultOptions = { audio: true, video: true, }; initMeeting({ authToken: "", defaults: meetingDefaultOptions, // optional }); }, []); useEffect(() => { // next - if (meeting) meeting.join(); }, [meeting]) return
; } ``` Use the [Add participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/) to fetch the `authToken`. Install the client SDK. ```bash npm i @cloudflare/realtimekit ``` Alternatively, you can also use the CDN. ```html ``` You can initialise the SDK using `RealtimeKitClient.init`. ```js const authToken = ; const meetingDefaultOptions = { audio: true, video: true, }; RealtimeKitClient.init({ authToken, defaults: meetingDefaultOptions, // optional }).then((meeting) => { // next - meeting.join(); }); ``` Use the [Add participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/) to fetch the `authToken`. Install the client SDK. ```bash npm i @cloudflare/realtimekit-angular ``` You can initialise the SDK using `RealtimeKitClient.init`. ```ts class AppComponent { title = "MyProject"; @ViewChild("myid") meetingComponent: RtkMeeting; rtkMeeting: RealtimeKitClient; async ngAfterViewInit() { const meetingDefaultOptions = { audio: true, video: true, }; const meeting = await RealtimeKitClient.init({ authToken: "", defaults: meetingDefaultOptions, // optional }); // next - meeting.join(); } } ``` Use the [Add participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/) to fetch the `authToken`. Initialize the RealtimeKit SDK by obtaining an instance of `RealtimeKitClient` using the `RealtimeKitMeetingBuilder` helper. ```kotlin val meeting = RealtimeKitMeetingBuilder.build(activity) ``` Configure the meeting properties in the `RtkMeetingInfo` class with a valid participant `authToken` from the [Add participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/). ```kotlin val meetingInfo = RtkMeetingInfo( authToken = authToken, enableAudio = true, enableVideo = true, ) ``` Initialize the meeting by calling the `init()` method. This establishes a connection with the RealtimeKit meeting server. ```kotlin meeting.init( meetingInfo, onInitCompleted = { ... }, onInitFailed = { ... }, ) ``` Initialize the RealtimeKit SDK by creating an instance of `RealtimeKitClient`. ```swift let meeting = RealtimeKitiOSClientBuilder().build() ``` Add the required listeners to receive callbacks for meeting events. ```swift meeting.addMeetingRoomEventListener(meetingRoomEventListener: self) meeting.addParticipantsEventListener(participantsEventListener: self) meeting.addSelfEventListener(selfEventListener: self) ``` Configure the meeting properties in the `RtkMeetingInfo` class with a valid participant `authToken` from the [Add participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/). ```swift let meetingInfo = RtkMeetingInfo(authToken: authToken, enableAudio: true, enableVideo: true) ``` Initialize the meeting by calling the `doInit()` method. This establishes a connection with the RealtimeKit meeting server. ```swift meeting.doInit(meetingInfo: meetingInfo, onSuccess: {}, onFailure: {_ in}) ``` Initialize the RealtimeKit SDK by creating an instance of `RealtimeKitClient`. ```dart final meeting = RealtimeKitClient(); ``` Configure the meeting properties in the `RtkMeetingInfo` class with a valid participant `authToken` from the [Add participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/). ```dart final meetingInfo = RtkMeetingInfo( authToken: authToken, enableAudio: false, enableVideo: false, ); ``` Initialize the connection by calling the `init()` method. This establishes a connection with the RealtimeKit meeting server. ```dart meeting.init(meetingInfo); ``` Subscribe to the `RtkMeetingRoomEventListener` to receive callbacks for meeting events. ```dart meeting.addMeetingRoomEventListener(RoomStateNotifier()); ``` Initialize the RealtimeKit SDK using the `useRealtimeKitClient` hook. ```js import React from 'react'; import { View, Text } from 'react-native'; import { useRealtimeKitClient, RealtimeKitProvider } from '@cloudflare/realtimekit-react-native'; export default function App() { const [meeting, initMeeting] = useRealtimeKitClient(); React.useEffect(() => { const init = async () => { const meetingOptions = { audio: true, video: true, }; await initMeeting({ authToken: 'YourAuthToken', defaults: meetingOptions, }); }; init(); // next - if (meeting) meeting.joinRoom(); }, []); if (meeting) { return ( {/* Render your components here */} ); } else { return ( Loading... ) } } ``` Use the [Add participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/) to fetch the `authToken`. ### Advanced Options You can **optionally** configure meeting defaults, media quality, screen share preferences, simulcast settings, ice connection behavior, logging, and error handling while initializing the SDK. ```ts init({ authToken: "", defaults: { video: true, audio: true, mediaConfiguration: { // Configure custom video quality (e.g., 1080p). Disable simulcast using `simulcastConfig` override for single-layer streaming. video: { width: { ideal: 1920 }, height: { ideal: 1080 }, frameRate: { ideal: 15 }, }, screenshare: { frameRate: { ideal: 15, max: 30 }, // Default 5 displaySurface: "monitor", // Given surface is suggested to the end user }, }, }, overrides: { simulcastConfig: { // If you want to disable simulcast disable: false, // If you want to pass custom simulcast encodings encodings: [ { rid: "f", // full / highest quality scaleResolutionDownBy: 1.0, maxBitrate: 2500000, // ~2.5 Mbps }, { rid: "h", // half scaleResolutionDownBy: 2.0, maxBitrate: 900000, // ~0.9 Mbps }, { rid: "q", // quarter scaleResolutionDownBy: 4.0, maxBitrate: 250000, // ~0.25 Mbps }, ], }, forceRelay: false, // forceRelay, if true, TURN will be preferred over STUN }, modules: { devTools: { logs: true, // Prints SDK logs to console, Useful in initial integration phase }, }, onError: (error) => { console.error(error); // SDK errors, Useful in detecting common issues }, }); ``` Tip `onError` callback is used to handle SDK errors. These errors could be due to invalid auth token, network issues, media permissions, etc. Each error is thrown with a unique error code. Learn more about SDK [error codes](https://developers.cloudflare.com/realtime/realtimekit/core/error-codes/). You can pass the following options as `defaults` to alter default behavior. | Option | Description | Type | Required | | ------------------------- | ------------------------------------------------------------- | ------------------------------------ | -------- | | **video** | Should video be enabled by default | boolean | false | | **audio** | Should audio be enabled by default | boolean | false | | **mediaConfiguration** | Allows you to pass custom media quality constraints | MediaConfiguration | false | | **autoSwitchAudioDevice** | Automatically switch to a newly plugged microphone or speaker | boolean | false | | **isNonPreferredDevice** | Allows you to set specific devices as "not preferred" | (device: MediaDeviceInfo) => boolean | false | | **recording** | Allows you to configure recording settings | RecordingConfig | false | Note By default, audio and video are auto enabled, as per preset permissions. SDK uses 640x480 quality as default for group calls, which can be overridden with `mediaConfiguration`. By default, the SDK automatically switches to the best available device and marks virtual devices as not preferred. Reference for the types: ```ts interface AudioQualityConstraints { echoCancellation?: boolean; noiseSuppression?: boolean; autoGainControl?: boolean; enableStereo?: boolean; enableHighBitrate?: boolean; } interface VideoQualityConstraints { width: { ideal: number }; height: { ideal: number }; frameRate?: { ideal: number }; } interface ScreenshareQualityConstraints { width?: { max: number }; height?: { max: number }; frameRate?: { ideal: number; max: number; }; displaySurface?: "window" | "monitor" | "browser"; selfBrowserSurface?: "include" | "exclude"; } interface MediaConfiguration { video?: VideoQualityConstraints; audio?: AudioQualityConstraints; screenshare?: ScreenshareQualityConstraints; } interface RecordingConfig { fileNamePrefix?: string; videoConfig?: { height?: number; width?: number; codec?: string; }; } interface DefaultOptions { video?: boolean; audio?: boolean; mediaConfiguration?: MediaConfiguration; isNonPreferredDevice?: (device: MediaDeviceInfo) => boolean; autoSwitchAudioDevice?: boolean; recording?: RecordingConfig; } interface Overrides { simulcastConfig?: { disable?: boolean; encodings?: RTCRtpEncodingParameters[]; }; forceRelay?: boolean; } ``` You can configure meeting defaults using `enableAudio` and `enableVideo` parameters. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/#page","headline":"Build using Core SDK · Cloudflare Realtime docs","description":"Initialize and build with the RealtimeKit Core SDK for full control over video calls.","url":"https://developers.cloudflare.com/realtime/realtimekit/core/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: API reference for the RealtimeKit Core SDK methods, events, and properties. title: API Reference image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # API Reference Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/#page","headline":"API Reference · Cloudflare Realtime docs","description":"API reference for the RealtimeKit Core SDK methods, events, and properties.","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RealtimeKitClient image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RealtimeKitClient Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/realtimekitclient/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RealtimeKitClient class is the main class of the web core library. An object of the RealtimeKitClient class can be created using `await RealtimeKitClient.init({ ... })`. Typically, an object of `RealtimeKitClient` is named `meeting`. * [RealtimeKitClient](#module%5FRealtimeKitClient) * _instance_ * [.participants](#module%5FRealtimeKitClient+participants) * [.self](#module%5FRealtimeKitClient+self) * [.meta](#module%5FRealtimeKitClient+meta) * [.ai](#module%5FRealtimeKitClient+ai) * [.plugins](#module%5FRealtimeKitClient+plugins) * [.chat](#module%5FRealtimeKitClient+chat) * [.polls](#module%5FRealtimeKitClient+polls) * [.connectedMeetings](#module%5FRealtimeKitClient+connectedMeetings) * [.**internals**](#module%5FRealtimeKitClient+%5F%5Finternals%5F%5F) * [.join()](#module%5FRealtimeKitClient+join) * [.leave()](#module%5FRealtimeKitClient+leave) * _static_ * [.initMedia(\[options\], \[skipAwaits\], \[cachedUserDetails\])](#module%5FRealtimeKitClient.initMedia) * [.init(options)](#module%5FRealtimeKitClient.init) ### meeting.participants The `participants` object consists of 4 maps of participants, `waitlisted`, `joined`, `active`, `pinned`. The maps are indexed by `peerId`s, and the values are the corresponding participant objects. **Kind**: instance property of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.self The `self` object can be used to manipulate audio and video settings, and other configurations for the local participant. This exposes methods to enable and disable media tracks, share the user's screen, etc. **Kind**: instance property of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.meta The `room` object stores information about the current meeting, such as chat messages, polls, room name, etc. **Kind**: instance property of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.ai The `ai` object is used to interface with AI features. You can obtain the live meeting transcript and use other meeting AI features such as summary, and agenda using this object. **Kind**: instance property of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.plugins The `plugins` object stores information about the plugins available in the current meeting. It exposes methods to activate and deactivate them. **Kind**: instance property of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.chat The chat object stores the chat messages that were sent in the meeting. This includes text messages, images, and files. **Kind**: instance property of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.polls The polls object stores the polls that were initiated in the meeting. It exposes methods to create and vote on polls. **Kind**: instance property of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.connectedMeetings The connectedMeetings object stores the connected meetings states. It exposes methods to create/read/update/delete methods for connected meetings. **Kind**: instance property of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.\_\_internals\_\_ The **internals** object exposes the internal tools & utilities such as features and logger so that client can utilise the same to build their own feature based UI. Logger (**internals**.logger) can be used to send logs to servers to inform of issues, if any, proactively. **Kind**: instance property of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.join() The `join()` method can be used to join the meeting. A `roomJoined` event is emitted on `self` when the room is joined successfully. **Kind**: instance method of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.leave() The `leave()` method can be used to leave a meeting. **Kind**: instance method of [RealtimeKitClient](#module%5FRealtimeKitClient) ### meeting.initMedia(\[options\], \[skipAwaits\], \[cachedUserDetails\]) **Kind**: static method of [RealtimeKitClient](#module%5FRealtimeKitClient) | Param | Type | Default | | ----------------------- | ----------------- | ------- | | \[options\] | Object | | | \[options.video\] | boolean | | | \[options.audio\] | boolean | | | \[options.constraints\] | MediaConstraints | | | \[skipAwaits\] | boolean | false | | \[cachedUserDetails\] | CachedUserDetails | | ### meeting.init(options) The `init` method can be used to instantiate the RealtimeKitClient class. This returns an instance of RealtimeKitClient, which can be used to perform actions on the meeting. **Kind**: static method of [RealtimeKitClient](#module%5FRealtimeKitClient) | Param | Description | | ----------------- | ----------------------------------------------- | | options | The options object. | | options.authToken | The authorization token received using the API. | | options.baseURI | The base URL of the API. | | options.defaults | The default audio and video settings. | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/realtimekitclient/#page","headline":"RealtimeKitClient · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/realtimekitclient/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKAi image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKAi Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkai/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This module consists of the `ai` object which is used to interface with product's AI features. You can obtain the live meeting transcript and use other meeting AI features such as summary, and agenda using this object. ### meeting.ai.onTranscript(transcript) **Kind**: instance method of [RTKAi](#module%5FRTKAi) | Param | Type | Description | | ---------- | ----------------- | ------------------------------------------- | | transcript | TranscriptionData | Transcript data received for a participant. | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkai/#page","headline":"RTKAi · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkai/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKChat image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKChat Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkchat/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This is the chat module, which can be used to send and receive messages from the meeting. * [RTKChat](#module%5FRTKChat) * ~~[.messages](#module%5FRTKChat+messages)~~ * [.setMaxTextLimit(limit)](#module%5FRTKChat+setMaxTextLimit) * [.updateRateLimits(num, period)](#module%5FRTKChat+updateRateLimits) * [.sendTextMessage(message, \[peerIds\])](#module%5FRTKChat+sendTextMessage) * [.sendCustomMessage(message, \[peerIds\])](#module%5FRTKChat+sendCustomMessage) * [.sendImageMessage(image, \[peerIds\])](#module%5FRTKChat+sendImageMessage) * [.sendFileMessage(file, \[peerIds\])](#module%5FRTKChat+sendFileMessage) * [.sendMessage(message, \[participantIds\])](#module%5FRTKChat+sendMessage) * [.editTextMessage(messageId, message)](#module%5FRTKChat+editTextMessage) * [.editImageMessage(messageId, image)](#module%5FRTKChat+editImageMessage) * [.editFileMessage(messageId, file)](#module%5FRTKChat+editFileMessage) * [.editMessage(messageId, message)](#module%5FRTKChat+editMessage) * [.deleteMessage(messageId)](#module%5FRTKChat+deleteMessage) * [.pin(id)](#module%5FRTKChat+pin) * [.unpin(id)](#module%5FRTKChat+unpin) * [.fetchPublicMessages(options)](#module%5FRTKChat+fetchPublicMessages) * [.fetchPrivateMessages(options)](#module%5FRTKChat+fetchPrivateMessages) * [.fetchPinnedMessages(options)](#module%5FRTKChat+fetchPinnedMessages) ### ~~meeting.chat.messages~~ _**Deprecated**_ **Kind**: instance property of [RTKChat](#module%5FRTKChat) ### meeting.chat.setMaxTextLimit(limit) Set the max character limit of a text message **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ----- | ------ | --------------------------------------- | | limit | number | Max character limit for a text message. | ### meeting.chat.updateRateLimits(num, period) **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | | ------ | ------ | | num | number | | period | number | ### meeting.chat.sendTextMessage(message, \[peerIds\]) Sends a chat text message to the room. **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ----------- | -------------- | ------------------------------------------ | | message | string | The message that must be sent to the room. | | \[peerIds\] | Array. | Peer ids to send the message to. | ### meeting.chat.sendCustomMessage(message, \[peerIds\]) **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ----------- | -------------------- | -------------------------------- | | message | CustomMessagePayload | Custom message payload. | | \[peerIds\] | Array. | Peer ids to send the message to. | ### meeting.chat.sendImageMessage(image, \[peerIds\]) Sends an image message to the meeting. **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ----------- | ----------------------- | -------------------------------- | | image | File \| ReactNativeFile | The image that is to be sent. | | \[peerIds\] | Array. | Peer ids to send the message to. | ### meeting.chat.sendFileMessage(file, \[peerIds\]) Sends a file to the meeting. **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ----------- | ----------------------- | -------------------------------- | | file | File \| ReactNativeFile | A File object. | | \[peerIds\] | Array. | Peer ids to send the message to. | ### meeting.chat.sendMessage(message, \[participantIds\]) Sends a message to the meeting. This method can be used to send text, image, or file messages. The message type is determined by the key 'type' in `message`object. **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ------------------ | -------------- | -------------------------------------------------------- | | message | MessagePayload | An object including the type and content of the message. | | \[participantIds\] | Array. | An array including the userIds of the participants. | ### meeting.chat.editTextMessage(messageId, message) **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | --------- | ------ | -------------------------- | | messageId | string | Id of the message to edit. | | message | string | Updated text message. | ### meeting.chat.editImageMessage(messageId, image) **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | --------- | ----------------------- | -------------------------- | | messageId | string | Id of the message to edit. | | image | File \| ReactNativeFile | Updated image file. | ### meeting.chat.editFileMessage(messageId, file) **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | --------- | ----------------------- | -------------------------- | | messageId | string | Id of the message to edit. | | file | File \| ReactNativeFile | Updated file. | ### meeting.chat.editMessage(messageId, message) **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | --------- | -------------- | -------------------------- | | messageId | string | Id of the message to edit. | | message | MessagePayload | Updated message payload. | ### meeting.chat.deleteMessage(messageId) **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | --------- | ------ | ---------------------------- | | messageId | string | Id of the message to delete. | ### meeting.chat.pin(id) Pins a chat message **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ----- | ------ | ------------------------------ | | id | string | ID of the message to be pinned | ### meeting.chat.unpin(id) Unpins a chat message **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ----- | ------ | -------------------------------- | | id | string | ID of the message to be unpinned | ### meeting.chat.fetchPublicMessages(options) Fetches messages from the chat with pagination. **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ------- | ------------------- | ------------------------------------------------------------------------------------------------------ | | options | FetchMessageOptions | Configuration options for fetching messages, including timestamp, limit, and direction for pagination. | ### meeting.chat.fetchPrivateMessages(options) Fetches private messages between the current user and another participant with pagination. **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | options | FetchPrivateMessagesOptions | Configuration options for fetching private messages, including private RTKChat ID (User ID of the participant) and pagination settings. | ### meeting.chat.fetchPinnedMessages(options) Fetches pinned messages with pagination. **Kind**: instance method of [RTKChat](#module%5FRTKChat) | Param | Type | Description | | ------- | ------------------- | ---------------------------------------------------------------------------------------------- | | options | FetchMessageOptions | Configuration options for fetching pinned messages, including timestamp, limit, and direction. | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkchat/#page","headline":"RTKChat · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkchat/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKConnectedMeetings image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKConnectedMeetings Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkconnectedmeetings/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This consists of the methods to facilitate connected meetings * [RTKConnectedMeetings](#module%5FRTKConnectedMeetings) * [.getConnectedMeetings()](#module%5FRTKConnectedMeetings+getConnectedMeetings) * [.createMeetings(request)](#module%5FRTKConnectedMeetings+createMeetings) * [.updateMeetings(request)](#module%5FRTKConnectedMeetings+updateMeetings) * [.deleteMeetings(meetingIds)](#module%5FRTKConnectedMeetings+deleteMeetings) * [.moveParticipants(sourceMeetingId, destinationMeetingId, participantIds)](#module%5FRTKConnectedMeetings+moveParticipants) * [.moveParticipantsWithCustomPreset(sourceMeetingId, destinationMeetingId, participants)](#module%5FRTKConnectedMeetings+moveParticipantsWithCustomPreset) ### meeting.connectedMeetings.getConnectedMeetings() get connected meeting state **Kind**: instance method of [RTKConnectedMeetings](#module%5FRTKConnectedMeetings) ### meeting.connectedMeetings.createMeetings(request) create connected meetings **Kind**: instance method of [RTKConnectedMeetings](#module%5FRTKConnectedMeetings) | Param | Type | | ------- | ----------------------- | | request | Array.<{title: string}> | ### meeting.connectedMeetings.updateMeetings(request) update meeting title **Kind**: instance method of [RTKConnectedMeetings](#module%5FRTKConnectedMeetings) | Param | Type | | ------- | ----------------------------------- | | request | Array.<{id: string, title: string}> | ### meeting.connectedMeetings.deleteMeetings(meetingIds) delete connected meetings **Kind**: instance method of [RTKConnectedMeetings](#module%5FRTKConnectedMeetings) | Param | Type | | ---------- | -------------- | | meetingIds | Array. | ### meeting.connectedMeetings.moveParticipants(sourceMeetingId, destinationMeetingId, participantIds) Trigger event to move participants **Kind**: instance method of [RTKConnectedMeetings](#module%5FRTKConnectedMeetings) | Param | Type | Description | | -------------------- | -------------- | ------------------------------ | | sourceMeetingId | string | id of source meeting | | destinationMeetingId | string | id of destination meeting | | participantIds | Array. | list of id of the participants | ### meeting.connectedMeetings.moveParticipantsWithCustomPreset(sourceMeetingId, destinationMeetingId, participants) Trigger event to move participants with custom preset **Kind**: instance method of [RTKConnectedMeetings](#module%5FRTKConnectedMeetings) | Param | Type | Description | | -------------------- | -------------------------------------- | ------------------------- | | sourceMeetingId | string | id of source meeting | | destinationMeetingId | string | id of destination meeting | | participants | Array.<{id: string, presetId: string}> | | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkconnectedmeetings/#page","headline":"RTKConnectedMeetings · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkconnectedmeetings/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKLivestream image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKLivestream Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtklivestream/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RTKLivestream module represents the state of the current livestream, and allows to start/stop live streams. * [RTKLivestream](#module%5FRTKLivestream) * [.start(\[livestreamConfig\])](#module%5FRTKLivestream+start) * [.stop()](#module%5FRTKLivestream+stop) ### meeting.livestream.start(\[livestreamConfig\]) Starts livestreaming the meeting. **Kind**: instance method of [RTKLivestream](#module%5FRTKLivestream) | Param | Type | | -------------------- | --------------------- | | \[livestreamConfig\] | StartLivestreamConfig | ### meeting.livestream.stop() Stops livestreaming the meeting. **Kind**: instance method of [RTKLivestream](#module%5FRTKLivestream) Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtklivestream/#page","headline":"RTKLivestream · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtklivestream/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKMeta image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKMeta Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkmeta/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This consists of the metadata of the meeting, such as the room name and the title. * [RTKMeta](#module%5FRTKMeta) * [.selfActiveTab](#module%5FRTKMeta+selfActiveTab) * [.broadcastTabChanges](#module%5FRTKMeta+broadcastTabChanges) * [.viewType](#module%5FRTKMeta+viewType) * [.meetingStartedTimestamp](#module%5FRTKMeta+meetingStartedTimestamp) * [.meetingTitle](#module%5FRTKMeta+meetingTitle) * [.sessionId](#module%5FRTKMeta+sessionId) * [.meetingId](#module%5FRTKMeta+meetingId) * [.setBroadcastTabChanges(broadcastTabChanges)](#module%5FRTKMeta+setBroadcastTabChanges) * [.setSelfActiveTab(spotlightTab, tabChangeSource)](#module%5FRTKMeta+setSelfActiveTab) ### meeting.meta.selfActiveTab Represents the current active tab **Kind**: instance property of [RTKMeta](#module%5FRTKMeta) ### meeting.meta.broadcastTabChanges Represents whether current user is spotlighted **Kind**: instance property of [RTKMeta](#module%5FRTKMeta) ### meeting.meta.viewType The `viewType` tells the type of the meeting possible values are: GROUP\_CALL| LIVESTREAM | CHAT | AUDIO\_ROOM **Kind**: instance property of [RTKMeta](#module%5FRTKMeta) ### meeting.meta.meetingStartedTimestamp The timestamp of the time when the meeting started. **Kind**: instance property of [RTKMeta](#module%5FRTKMeta) ### meeting.meta.meetingTitle The title of the meeting. **Kind**: instance property of [RTKMeta](#module%5FRTKMeta) ### meeting.meta.sessionId (Experimental) The sessionId this meeting object is part of. **Kind**: instance property of [RTKMeta](#module%5FRTKMeta) ### meeting.meta.meetingId The room name of the meeting. **Kind**: instance property of [RTKMeta](#module%5FRTKMeta) ### meeting.meta.setBroadcastTabChanges(broadcastTabChanges) Sets current user as broadcasting tab changes **Kind**: instance method of [RTKMeta](#module%5FRTKMeta) | Param | Type | | ------------------- | ------- | | broadcastTabChanges | boolean | ### meeting.meta.setSelfActiveTab(spotlightTab, tabChangeSource) Sets current active tab for user **Kind**: instance method of [RTKMeta](#module%5FRTKMeta) | Param | Type | | --------------- | --------------- | | spotlightTab | ActiveTab | | tabChangeSource | TabChangeSource | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkmeta/#page","headline":"RTKMeta · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkmeta/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKParticipant image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKParticipant Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkparticipant/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This module represents a single participant in the meeting. The participant object can be accessed from one of the participant lists present in the `meeting.participants` object. For example, ```ts const participant1 = meeting.participants.active.get(participantId); const participant2 = meeting.participants.joined.get(participantId); const participant3 = meeting.participants.active.toArray()[0]; const participantsNamedJohn = meeting.participants.active.toArray() .filter((p) => p.name === 'John'); ``` * [RTKParticipant](#module%5FRTKParticipant) * [.id](#module%5FRTKParticipant+id) * [.userId](#module%5FRTKParticipant+userId) * [.name](#module%5FRTKParticipant+name) * [.picture](#module%5FRTKParticipant+picture) * [.customParticipantId](#module%5FRTKParticipant+customParticipantId) * [.device](#module%5FRTKParticipant+device) * [.videoTrack](#module%5FRTKParticipant+videoTrack) * [.audioTrack](#module%5FRTKParticipant+audioTrack) * [.screenShareTracks](#module%5FRTKParticipant+screenShareTracks) * [.videoEnabled](#module%5FRTKParticipant+videoEnabled) * [.audioEnabled](#module%5FRTKParticipant+audioEnabled) * [.screenShareEnabled](#module%5FRTKParticipant+screenShareEnabled) * [.producers](#module%5FRTKParticipant+producers) * [.manualProducerConfig](#module%5FRTKParticipant+manualProducerConfig) * [.supportsRemoteControl](#module%5FRTKParticipant+supportsRemoteControl) * [.presetName](#module%5FRTKParticipant+presetName) * [.stageStatus](#module%5FRTKParticipant+stageStatus) * [.isPinned](#module%5FRTKParticipant+isPinned) * [.pin()](#module%5FRTKParticipant+pin) * [.unpin()](#module%5FRTKParticipant+unpin) * [.disableAudio()](#module%5FRTKParticipant+disableAudio) * [.kick()](#module%5FRTKParticipant+kick) * [.disableVideo()](#module%5FRTKParticipant+disableVideo) * [.registerVideoElement(videoElem)](#module%5FRTKParticipant+registerVideoElement) * [.deregisterVideoElement(\[videoElem\])](#module%5FRTKParticipant+deregisterVideoElement) ### participant.id The peer ID of the participant. The participants are indexed by this ID in the participant map. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.userId The user ID of the participant. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.name The name of the participant. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.picture The picture of the participant. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.customParticipantId The custom id of the participant set during [https://developers.cloudflare.com/api/resources/realtime\_kit/subresources/meetings/methods/add\_participant ↗](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant) REST API **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.device The device configuration of the participant. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.videoTrack The participant's video track. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.audioTrack The participant's audio track. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.screenShareTracks The participant's screenshare video and audio track. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.videoEnabled This is true if the participant's video is enabled. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.audioEnabled This is true if the participant's audio is enabled. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.screenShareEnabled This is true if the participant is screensharing. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.producers producers created by participant **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.manualProducerConfig producer config passed during manual subscription **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.supportsRemoteControl This is true if the participant supports remote control. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.presetName The preset of the participant. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.stageStatus Denotes the participants's current stage status. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.isPinned Returns true if the participant is pinned. **Kind**: instance property of [RTKParticipant](#module%5FRTKParticipant) ### participant.pin() Returns `participant.id` if user has permission to pin participants. **Kind**: instance method of [RTKParticipant](#module%5FRTKParticipant) ### participant.unpin() Returns `participant.id` if user has permission to unpin participants. **Kind**: instance method of [RTKParticipant](#module%5FRTKParticipant) ### participant.disableAudio() Disables audio for this participant. Requires the permission to disable participant audio. **Kind**: instance method of [RTKParticipant](#module%5FRTKParticipant) ### participant.kick() Kicks this participant from the meeting. Requires the permission to kick a participant. **Kind**: instance method of [RTKParticipant](#module%5FRTKParticipant) ### participant.disableVideo() Disables video for this participant. Requires the permission to disable video for a participant. **Kind**: instance method of [RTKParticipant](#module%5FRTKParticipant) ### participant.registerVideoElement(videoElem) **Kind**: instance method of [RTKParticipant](#module%5FRTKParticipant) | Param | Type | | --------- | ---------------- | | videoElem | HTMLVideoElement | ### participant.deregisterVideoElement(\[videoElem\]) **Kind**: instance method of [RTKParticipant](#module%5FRTKParticipant) | Param | Type | | ------------- | ---------------- | | \[videoElem\] | HTMLVideoElement | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkparticipant/#page","headline":"RTKParticipant · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkparticipant/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKParticipants image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKParticipants Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkparticipants/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This module represents all the participants in the meeting (except the local user). It consists of 4 maps: * `joined`: A map of all participants that have joined the meeting. * `waitlisted`: A map of all participants that have been added to the waitlist. * `active`: A map of active participants who should be displayed in the meeting grid. * `pinned`: A map of pinned participants. * [RTKParticipants](#module%5FRTKParticipants) * [.waitlisted](#module%5FRTKParticipants+waitlisted) * [.joined](#module%5FRTKParticipants+joined) * [.active](#module%5FRTKParticipants+active) * [.videoSubscribed](#module%5FRTKParticipants+videoSubscribed) * [.audioSubscribed](#module%5FRTKParticipants+audioSubscribed) * [.pinned](#module%5FRTKParticipants+pinned) * [.all](#module%5FRTKParticipants+all) * [.pip](#module%5FRTKParticipants+pip) * [.viewMode](#module%5FRTKParticipants+viewMode) * [.currentPage](#module%5FRTKParticipants+currentPage) * [.lastActiveSpeaker](#module%5FRTKParticipants+lastActiveSpeaker) * [.selectedPeers](#module%5FRTKParticipants+selectedPeers) * [.count](#module%5FRTKParticipants+count) * [.maxActiveParticipantsCount](#module%5FRTKParticipants+maxActiveParticipantsCount) * [.pageCount](#module%5FRTKParticipants+pageCount) * [.setMaxActiveParticipantsCount(limit)](#module%5FRTKParticipants+setMaxActiveParticipantsCount) * [.acceptWaitingRoomRequest(id)](#module%5FRTKParticipants+acceptWaitingRoomRequest) * [.acceptAllWaitingRoomRequest(userIds)](#module%5FRTKParticipants+acceptAllWaitingRoomRequest) * [.rejectWaitingRoomRequest(id)](#module%5FRTKParticipants+rejectWaitingRoomRequest) * [.setViewMode(viewMode)](#module%5FRTKParticipants+setViewMode) * [.subscribe(peerIds, \[kinds\])](#module%5FRTKParticipants+subscribe) * [.unsubscribe(peerIds, \[kinds\])](#module%5FRTKParticipants+unsubscribe) * [.setPage(page)](#module%5FRTKParticipants+setPage) * [.disableAllAudio(allowUnmute)](#module%5FRTKParticipants+disableAllAudio) * [.disableAllVideo()](#module%5FRTKParticipants+disableAllVideo) * [.kickAll()](#module%5FRTKParticipants+kickAll) * [.broadcastMessage(type, payload, target)](#module%5FRTKParticipants+broadcastMessage) * [.getAllJoinedPeers(searchQuery, limit, offset)](#module%5FRTKParticipants+getAllJoinedPeers) * [.getParticipantsInMeetingPreJoin()](#module%5FRTKParticipants+getParticipantsInMeetingPreJoin) ### meeting.participants.waitlisted Returns a list of participants waiting to join the meeting. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.joined Returns a list of all participants in the meeting. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.active Returns a list of participants whose streams are currently consumed. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.videoSubscribed Returns a list of participants whose video streams are currently consumed. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.audioSubscribed Returns a list of participants whose audio streams are currently consumed. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.pinned Returns a list of participants who have been pinned. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.all Returns all added participants irrespective of whether they are currently in the meeting or not **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.pip Return the controls for Picture-in-Picture **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.viewMode Indicates whether the meeting is in 'ACTIVE\_GRID' mode or 'PAGINATED' mode. In 'ACTIVE\_GRID' mode, participants are populated in the participants.active map dynamically. The participants present in the map will keep changing when other participants unmute their audio or turn on their videos. In 'PAGINATED' mode, participants are populated in the participants.active map just once, and the participants in the map will only change if the page number is changed by the user using setPage(page). **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.currentPage This indicates the current page that has been set by the user in PAGINATED mode. If the meeting is in ACTIVE\_GRID mode, this value will be 0. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.lastActiveSpeaker This stores the `participantId` of the last participant who spoke in the meeting. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.selectedPeers Keeps a list of all participants who have been present in the selected peers list. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.count Returns the number of participants who are joined in the meeting. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.maxActiveParticipantsCount Returns the maximum number of participants that can be present in the active map. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.pageCount Returns the number of pages that are available in the meeting in PAGINATED mode. If the meeting is in ACTIVE\_GRID mode, this value will be 0. **Kind**: instance property of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.setMaxActiveParticipantsCount(limit) Updates the maximum number of participants that are populated in the active map. **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | Description | | ----- | ------ | ----------------- | | limit | number | Updated max limit | ### meeting.participants.acceptWaitingRoomRequest(id) Accepts requests from waitlisted participants if user has appropriate permissions. **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | Description | | ----- | ------ | ----------------------------------------------- | | id | string | peerId or userId of the waitlisted participant. | ### meeting.participants.acceptAllWaitingRoomRequest(userIds) We need a new event for socket service events since if we send them all together, sequence of events can be unreliable **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | | ------- | -------------- | | userIds | Array. | ### meeting.participants.rejectWaitingRoomRequest(id) Rejects requests from waitlisted participants if user has appropriate permissions. **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | Description | | ----- | ------ | -------------------------------------------- | | id | string | participantId of the waitlisted participant. | ### meeting.participants.setViewMode(viewMode) Sets the view mode of the meeting to either ACTIVE\_GRID or PAGINATED. **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | Description | | -------- | -------- | ---------------------------------------------------- | | viewMode | ViewMode | The mode in which the active map should be populated | ### meeting.participants.subscribe(peerIds, \[kinds\]) **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | | --------- | ---------------------------------------------------------------- | | peerIds | Array. | | \[kinds\] | Array.<('audio'\|'video'|'screenshareAudio'|'screenshareVideo')> | ### meeting.participants.unsubscribe(peerIds, \[kinds\]) **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | | --------- | ---------------------------------------------------------------- | | peerIds | Array. | | \[kinds\] | Array.<('audio'\|'video'|'screenshareAudio'|'screenshareVideo')> | ### meeting.participants.setPage(page) Populates the active map with participants present in the page number indicated by the parameter `page` in PAGINATED mode. Does not do anything in ACTIVE\_GRID mode. **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | Description | | ----- | ------ | -------------------------- | | page | number | The page number to be set. | ### meeting.participants.disableAllAudio(allowUnmute) Disables audio for all participants in the meeting. **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | Description | | ----------- | ------- | -------------------------------------------------- | | allowUnmute | boolean | Allow participants to unmute after they are muted. | ### meeting.participants.disableAllVideo() Disables video for all participants in the meeting. **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.kickAll() Kicks all participants from the meeting. **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) ### meeting.participants.broadcastMessage(type, payload, target) Broadcasts the message to participants If no `target` is specified it is sent to all participants including `self`. **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | Description | | ------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | type | string | | | payload | BroadcastMessagePayload | | | target | BroadcastMessageTarget | object containing a list of participantIds or object containing presetName \- every user with that preset will be sent the message | ### meeting.participants.getAllJoinedPeers(searchQuery, limit, offset) Returns all peers currently present in the room If you are in a group call, use `meeting.participants.joined`instead **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) | Param | Type | | ----------- | ------ | | searchQuery | string | | limit | number | | offset | number | ### meeting.participants.getParticipantsInMeetingPreJoin() Returns all peers currently in the room, is a non paginated call and should only be used if you are in a non room joined state, if in a joined group call, use `meeting.participants.joined` **Kind**: instance method of [RTKParticipants](#module%5FRTKParticipants) Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkparticipants/#page","headline":"RTKParticipants · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkparticipants/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKPermissionsPreset image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKPermissionsPreset Last updated Jul 20, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkpermissionspreset/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The PermissionPreset class represents the meeting permissions for the current participant * [PermissionPreset](#module%5FPermissionPreset) * [.stageEnabled](#module%5FPermissionPreset+stageEnabled) * [.stageAccess](#module%5FPermissionPreset+stageAccess) * [.acceptWaitingRequests](#module%5FPermissionPreset+acceptWaitingRequests) * [.requestProduceVideo](#module%5FPermissionPreset+requestProduceVideo) * [.requestProduceAudio](#module%5FPermissionPreset+requestProduceAudio) * [.requestProduceScreenshare](#module%5FPermissionPreset+requestProduceScreenshare) * [.canAllowParticipantAudio](#module%5FPermissionPreset+canAllowParticipantAudio) * [.canAllowParticipantScreensharing](#module%5FPermissionPreset+canAllowParticipantScreensharing) * [.canAllowParticipantVideo](#module%5FPermissionPreset+canAllowParticipantVideo) * [.canDisableParticipantAudio](#module%5FPermissionPreset+canDisableParticipantAudio) * [.canDisableParticipantVideo](#module%5FPermissionPreset+canDisableParticipantVideo) * [.kickParticipant](#module%5FPermissionPreset+kickParticipant) * [.pinParticipant](#module%5FPermissionPreset+pinParticipant) * [.canRecord](#module%5FPermissionPreset+canRecord) * [.waitingRoomBehaviour](#module%5FPermissionPreset+waitingRoomBehaviour) * [.plugins](#module%5FPermissionPreset+plugins) * [.polls](#module%5FPermissionPreset+polls) * [.canProduceVideo](#module%5FPermissionPreset+canProduceVideo) * [.canProduceScreenshare](#module%5FPermissionPreset+canProduceScreenshare) * [.canProduceAudio](#module%5FPermissionPreset+canProduceAudio) * [.chatPublic](#module%5FPermissionPreset+chatPublic) * [.chatPrivate](#module%5FPermissionPreset+chatPrivate) * [.hiddenParticipant](#module%5FPermissionPreset+hiddenParticipant) * [.showParticipantList](#module%5FPermissionPreset+showParticipantList) * [.canChangeParticipantPermissions](#module%5FPermissionPreset+canChangeParticipantPermissions) * [.canLivestream](#module%5FPermissionPreset+canLivestream) ### meeting.self.permissions.stageEnabled The `stageEnabled` property returns a boolean value. If `true`, stage management is available for the participant. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.stageAccess The `stageAccess` property dictates how a user interacts with the stage. The possible values are `ALLOWED`, `NOT_ALLOWED`, `CAN_REQUEST`; **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.acceptWaitingRequests The `acceptWaitingRequests` returns boolean value. If `true`, participant can accept the request of waiting participant. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.requestProduceVideo The `requestProduceVideo` returns boolean value. If `true`, participant can send request to participants about producing video. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.requestProduceAudio The `requestProduceAudio` returns boolean value. If `true`, participant can send request to participants about producing audio. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.requestProduceScreenshare The `requestProduceScreenshare` returns boolean value. If `true`, participant can send request to participants about sharing screen. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canAllowParticipantAudio The `canAllowParticipantAudio` returns boolean value. If `true`, participant can enable other participants\` audio. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canAllowParticipantScreensharing The `canAllowParticipantScreensharing` returns boolean value. If `true`, participant can enable other participants\` screen share. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canAllowParticipantVideo The `canAllowParticipantVideo` returns boolean value. If `true`, participant can enable other participants\` video. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canDisableParticipantAudio If `true`, a participant can disable other participants\` audio. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canDisableParticipantVideo If `true`, a participant can disable other participants\` video. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.kickParticipant The `kickParticipant` returns boolean value. If `true`, participant can remove other participants from the meeting. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.pinParticipant The `pinParticipant` returns boolean value. If `true`, participant can pin a participant in the meeting. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canRecord The `canRecord` returns boolean value. If `true`, participant can record the meeting. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.waitingRoomBehaviour The `waitingRoomType` returns string value. type of waiting room behavior possible values are `SKIP`, `ON_PRIVILEGED_USER_ENTRY`, `SKIP_ON_ACCEPT` **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.plugins The `plugins` tells if the participant can act on plugins there are 2 permissions with boolean values, `canStart` and `canClose`. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.polls The `polls` tells if the participant can use polls. There are 3 permissions with boolean values, `canCreate`, `canVote`, `canViewResults` **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canProduceVideo The `canProduceVideo` shows permissions for enabling video. There possible values are `ALLOWED`, `NOT_ALLOWED`, `CAN_REQUEST` **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canProduceScreenshare The `canProduceScreenshare` shows permissions for sharing screen. There possible values are `ALLOWED`, `NOT_ALLOWED`, `CAN_REQUEST` **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canProduceAudio The `canProduceAudio` shows permissions for enabling audio. There possible values are `ALLOWED`, `NOT_ALLOWED`, `CAN_REQUEST` **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.chatPublic The `chatPublic` shows permissions for public chat there are 4 permissions `canSend` \- if true, the participant can send chat `text` \- if true, the participant can send text `files` \- if true, the participant can send files **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.chatPrivate The `chatPrivate` shows permissions for public chat there are 4 permissions `canSend` \- if true, the participant can send private chat `text` \- if true, the participant can send text as private chat `files` \- if true, the participant can send files as private chat `canReceive` \- (optional) if true, the participant can receive private chat **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.hiddenParticipant The `hiddenParticipant` returns boolean value. If `true`, participant is hidden. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.showParticipantList The `showParticipantList` returns boolean value. If `true`, participant list can be shown to the participant. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canChangeParticipantPermissions The `canChangeParticipantPermissions` returns boolean value. If `true`, allow changing the participants' permissions. **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) ### meeting.self.permissions.canLivestream Livestream **Kind**: instance property of [PermissionPreset](#module%5FPermissionPreset) Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkpermissionspreset/#page","headline":"RTKPermissionsPreset · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkpermissionspreset/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKPip image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKPip Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkpip/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) ## Modules [RTKPip](#module%5FRTKPip) ## Functions [getInitials()](#getInitials) Code from ui-kit. Same method used in the avatar component * [RTKPip](#module%5FRTKPip) * [.disable](#module%5FRTKPip+disable) * [.init(\[options\])](#module%5FRTKPip+init) * [.disableSource(source)](#module%5FRTKPip+disableSource) * [.addSource(id, element, enabled, \[displayText\])](#module%5FRTKPip+addSource) * [.updateSource(id, source)](#module%5FRTKPip+updateSource) * [.removeSource(id)](#module%5FRTKPip+removeSource) * [.removePinnedSource(id)](#module%5FRTKPip+removePinnedSource) * [.removeAllSources()](#module%5FRTKPip+removeAllSources) * [.enable()](#module%5FRTKPip+enable) ### meeting.participants.pip.disable Disable PiP **Kind**: instance property of [RTKPip](#module%5FRTKPip) ### meeting.participants.pip.init(\[options\]) Initialize PiP and prepare sources **Kind**: instance method of [RTKPip](#module%5FRTKPip) | Param | Type | | ------------------ | ------ | | \[options\] | Object | | \[options.height\] | number | | \[options.width\] | number | ### meeting.participants.pip.disableSource(source) **Kind**: instance method of [RTKPip](#module%5FRTKPip) | Param | Type | | ------ | ------ | | source | string | ### meeting.participants.pip.addSource(id, element, enabled, \[displayText\]) Add a video source from the participant grid **Kind**: instance method of [RTKPip](#module%5FRTKPip) | Param | Type | Description | | --------------- | ---------------- | -------------------------------------- | | id | string | id for the source (ex. participant id) | | element | HTMLVideoElement | HTMLVideoElement for the video source | | enabled | boolean | if source is enabled | | \[displayText\] | string | two character display text | ### meeting.participants.pip.updateSource(id, source) Update a video source **Kind**: instance method of [RTKPip](#module%5FRTKPip) | Param | Type | | ------ | ------ | | id | string | | source | any | ### meeting.participants.pip.removeSource(id) Remove the video source for the participant **Kind**: instance method of [RTKPip](#module%5FRTKPip) | Param | Description | | ----- | -------------------------------------- | | id | id for the source (ex. participant id) | ### meeting.participants.pip.removePinnedSource(id) Remove the pinned source **Kind**: instance method of [RTKPip](#module%5FRTKPip) | Param | Description | | ----- | -------------------------------------- | | id | id for the source (ex. participant id) | ### meeting.participants.pip.removeAllSources() Remove all sources **Kind**: instance method of [RTKPip](#module%5FRTKPip) ### meeting.participants.pip.enable() Enable PiP **Kind**: instance method of [RTKPip](#module%5FRTKPip) Code from ui-kit. Same method used in the avatar component **Kind**: global function Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkpip/#page","headline":"RTKPip · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkpip/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKPlugin image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKPlugin Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkplugin/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RTKPlugin module represents a single plugin in the meeting. A plugin can be obtained from one of the plugin arrays in `meeting.plugins`. For example, ```ts const plugin1 = meeting.plugins.active.get(pluginId); const plugin2 = meeting.plugins.all.get(pluginId); ``` * [RTKPlugin](#module%5FRTKPlugin) * [.component](#module%5FRTKPlugin+component) * [.activateForSelf()](#module%5FRTKPlugin+activateForSelf) * [.deactivateForSelf()](#module%5FRTKPlugin+deactivateForSelf) * [.activate()](#module%5FRTKPlugin+activate) * [.deactivate()](#module%5FRTKPlugin+deactivate) ### plugin.component The component for this plugin, as provided in the plugin config. **Kind**: instance property of [RTKPlugin](#module%5FRTKPlugin) ### plugin.activateForSelf() **Kind**: instance method of [RTKPlugin](#module%5FRTKPlugin) ### plugin.deactivateForSelf() **Kind**: instance method of [RTKPlugin](#module%5FRTKPlugin) ### plugin.activate() Activate this plugin for all participants. **Kind**: instance method of [RTKPlugin](#module%5FRTKPlugin) ### plugin.deactivate() Deactivate this plugin for all participants. **Kind**: instance method of [RTKPlugin](#module%5FRTKPlugin) Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkplugin/#page","headline":"RTKPlugin · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkplugin/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKPlugins image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKPlugins Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkplugins/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RTKPlugins module consists of all the plugins in the meeting. It has 2 maps: * `all`: Consists of all the plugins in the meeting. * `active`: Consists of the plugins that are currently in use. * [RTKPlugins](#module%5FRTKPlugins) * [.all](#module%5FRTKPlugins+all) * [.active](#module%5FRTKPlugins+active) ### meeting.plugins.all All plugins accessible by the current user. **Kind**: instance property of [RTKPlugins](#module%5FRTKPlugins) ### meeting.plugins.active All plugins that are currently enabled in the room. **Kind**: instance property of [RTKPlugins](#module%5FRTKPlugins) Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkplugins/#page","headline":"RTKPlugins · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkplugins/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKPolls image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKPolls Last updated Jul 20, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkpolls/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RTKPolls module consists of the polls that have been created in the meeting. * [RTKPolls](#module%5FRTKPolls) * [.items](#module%5FRTKPolls+items) * [.create(question, options, anonymous, hideVotes)](#module%5FRTKPolls+create) * [.vote(pollId, index)](#module%5FRTKPolls+vote) ### meeting.polls.items An array of poll items. **Kind**: instance property of [RTKPolls](#module%5FRTKPolls) ### meeting.polls.create(question, options, anonymous, hideVotes) Creates a poll in the meeting. **Kind**: instance method of [RTKPolls](#module%5FRTKPolls) | Param | Default | Description | | --------- | ------- | ------------------------------------------ | | question | | The question that is to be voted for. | | options | | The options of the poll. | | anonymous | false | If true, the poll votes are anonymous. | | hideVotes | false | If true, the votes on the poll are hidden. | ### meeting.polls.vote(pollId, index) Casts a vote on an existing poll. **Kind**: instance method of [RTKPolls](#module%5FRTKPolls) | Param | Description | | ------ | ------------------------------------------ | | pollId | The ID of the poll that is to be voted on. | | index | The index of the option. | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkpolls/#page","headline":"RTKPolls · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkpolls/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKRecording image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKRecording Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkrecording/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RTKRecording module represents the state of the current recording, and allows to start/stop recordings and check if there's a recording in progress. * [RTKRecording](#module%5FRTKRecording) * [.start()](#module%5FRTKRecording+start) * [.stop()](#module%5FRTKRecording+stop) * [.pause()](#module%5FRTKRecording+pause) * [.resume()](#module%5FRTKRecording+resume) ### meeting.recording.start() Starts recording the meeting. **Kind**: instance method of [RTKRecording](#module%5FRTKRecording) ### meeting.recording.stop() Stops all recording currently in 'RECORDING' state **Kind**: instance method of [RTKRecording](#module%5FRTKRecording) ### meeting.recording.pause() Pauses all recording currently in 'RECORDING' state **Kind**: instance method of [RTKRecording](#module%5FRTKRecording) ### meeting.recording.resume() Resumes all recording currently in 'PAUSED' state **Kind**: instance method of [RTKRecording](#module%5FRTKRecording) Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkrecording/#page","headline":"RTKRecording · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkrecording/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKSelf image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKSelf Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkself/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RTKSelf module represents the current user, and allows to modify the state of the user in the meeting. The audio and video streams of the user can be retrieved from this module. * [RTKSelf](#module%5FRTKSelf) * [.peerId](#module%5FRTKSelf+peerId) * [.roomState](#module%5FRTKSelf+roomState) * [.permissions](#module%5FRTKSelf+permissions) * [.config](#module%5FRTKSelf+config) * [.roomJoined](#module%5FRTKSelf+roomJoined) * [.isPinned](#module%5FRTKSelf+isPinned) * [.cleanupEvents()](#module%5FRTKSelf+cleanupEvents) * [.setName(name)](#module%5FRTKSelf+setName) * [.setupTracks(options)](#module%5FRTKSelf+setupTracks) * [.enableAudio()](#module%5FRTKSelf+enableAudio) * [.enableVideo()](#module%5FRTKSelf+enableVideo) * [.updateVideoConstraints()](#module%5FRTKSelf+updateVideoConstraints) * [.enableScreenShare()](#module%5FRTKSelf+enableScreenShare) * [.updateScreenshareConstraints()](#module%5FRTKSelf+updateScreenshareConstraints) * [.disableAudio()](#module%5FRTKSelf+disableAudio) * [.disableVideo()](#module%5FRTKSelf+disableVideo) * [.disableScreenShare()](#module%5FRTKSelf+disableScreenShare) * [.getAllDevices()](#module%5FRTKSelf+getAllDevices) * [.pin()](#module%5FRTKSelf+pin) * [.unpin()](#module%5FRTKSelf+unpin) * [.hide()](#module%5FRTKSelf+hide) * [.show()](#module%5FRTKSelf+show) * [.setDevice(device)](#module%5FRTKSelf+setDevice) ### meeting.self.peerId NOTE(ishita1805): Discussed with Ravindra, added a duplicate for consistency when using identifiers in Locker. We might want to look at deprecating the `id` sometime later. **Kind**: instance property of [RTKSelf](#module%5FRTKSelf) ### meeting.self.roomState Returns the current state of room init - Initial State joined - User is in the meeting waitlisted - User is in the waitlist state rejected - User's was in the waiting room, but the entry was rejected kicked - A privileged user removed the user from the meeting left - User left the meeting ended - The meeting was ended **Kind**: instance property of [RTKSelf](#module%5FRTKSelf) ### meeting.self.permissions Returns the current permission given to the user for the meeting. **Kind**: instance property of [RTKSelf](#module%5FRTKSelf) ### meeting.self.config Returns configuration for the meeting. **Kind**: instance property of [RTKSelf](#module%5FRTKSelf) ### meeting.self.roomJoined Returns true if the local participant has joined the meeting. **Kind**: instance property of [RTKSelf](#module%5FRTKSelf) ### meeting.self.isPinned Returns true if the current user is pinned. **Kind**: instance property of [RTKSelf](#module%5FRTKSelf) ### meeting.self.cleanupEvents() **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.setName(name) The name of the user can be set by calling this method. This will get reflected to other participants ONLY if this method is called before the room is joined. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) | Param | Type | Description | | ----- | ------ | ----------------- | | name | string | Name of the user. | ### meeting.self.setupTracks(options) Sets up the local media tracks. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) | Param | Type | Description | | ---------------------- | ------- | ------------------------------------------------- | | options | Object | The audio and video options. | | \[options.video\] | boolean | If true, the video stream is fetched. | | \[options.audio\] | boolean | If true, the audio stream is fetched. | | \[options.forceReset\] | boolean | If true, force resets tracks before re-acquiring. | ### meeting.self.enableAudio() This method is used to unmute the local participant's audio. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.enableVideo() This method is used to start streaming the local participant's video to the meeting. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.updateVideoConstraints() This method is used to apply constraints to the current video stream. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.enableScreenShare() This method is used to start sharing the local participant's screen to the meeting. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.updateScreenshareConstraints() This method is used to apply constraints to the current screenshare stream. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.disableAudio() This method is used to mute the local participant's audio. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.disableVideo() This participant is used to disable the local participant's video. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.disableScreenShare() This method is used to stop sharing the local participant's screen. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.getAllDevices() Returns all media devices accessible by the local participant. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.pin() Returns `self.id` if user has permission to pin participants. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.unpin() Returns `self.id` if user has permission to unpin participants. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.hide() Hide's user's tile in the UI (locally) **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.show() Show's user's tile in the UI if hidden (locally) **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) ### meeting.self.setDevice(device) Change the current media device that is being used by the local participant. **Kind**: instance method of [RTKSelf](#module%5FRTKSelf) | Param | Type | Description | | ------ | --------------- | ---------------------------------------------------------------------------------------------- | | device | MediaDeviceInfo | The device that is to be used. A device of the same kind will be replaced. the primary stream. | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkself/#page","headline":"RTKSelf · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkself/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKSelfMedia image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKSelfMedia Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkselfmedia/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RTKSelfMedia class provides methods to manage the local participant's media. * [RTKSelfMedia](#module%5FRTKSelfMedia) * [.audioTrack](#module%5FRTKSelfMedia+audioTrack) * [.rawAudioTrack](#module%5FRTKSelfMedia+rawAudioTrack) * [.mediaPermissions](#module%5FRTKSelfMedia+mediaPermissions) * [.videoTrack](#module%5FRTKSelfMedia+videoTrack) * [.rawVideoTrack](#module%5FRTKSelfMedia+rawVideoTrack) * [.screenShareTracks](#module%5FRTKSelfMedia+screenShareTracks) * [.audioEnabled](#module%5FRTKSelfMedia+audioEnabled) * [.videoEnabled](#module%5FRTKSelfMedia+videoEnabled) * [.screenShareEnabled](#module%5FRTKSelfMedia+screenShareEnabled) * [.addAudioMiddleware(audioMiddleware)](#module%5FRTKSelfMedia+addAudioMiddleware) * [.removeAudioMiddleware(audioMiddleware)](#module%5FRTKSelfMedia+removeAudioMiddleware) * [.removeAllAudioMiddlewares()](#module%5FRTKSelfMedia+removeAllAudioMiddlewares) * [.addVideoMiddleware(videoMiddleware)](#module%5FRTKSelfMedia+addVideoMiddleware) * [.setVideoMiddlewareGlobalConfig(config)](#module%5FRTKSelfMedia+setVideoMiddlewareGlobalConfig) * [.removeVideoMiddleware(videoMiddleware)](#module%5FRTKSelfMedia+removeVideoMiddleware) * [.removeAllVideoMiddlewares()](#module%5FRTKSelfMedia+removeAllVideoMiddlewares) * [.getCurrentDevices()](#module%5FRTKSelfMedia+getCurrentDevices) * [.getAudioDevices()](#module%5FRTKSelfMedia+getAudioDevices) * [.getVideoDevices()](#module%5FRTKSelfMedia+getVideoDevices) * [.getSpeakerDevices()](#module%5FRTKSelfMedia+getSpeakerDevices) * [.getDeviceById(deviceId, kind)](#module%5FRTKSelfMedia+getDeviceById) * [.setDevice(device)](#module%5FRTKSelfMedia+setDevice) ### meeting.self.audioTrack Returns the `audioTrack`. **Kind**: instance property of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.rawAudioTrack Returns the `rawAudioTrack` having no middleware executed on it. **Kind**: instance property of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.mediaPermissions Returns the current audio and video permissions given by the user. 'ACCEPTED' if the user has given permission to use the media. 'CANCELED' if the user has canceled the screenshare. 'DENIED' if the user has denied permission to use the media. 'SYS\_DENIED' if the user's system has denied permission to use the media. 'UNAVAILABLE' if the media is not available (or being used by a different application). **Kind**: instance property of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.videoTrack Returns the `videoTrack`. **Kind**: instance property of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.rawVideoTrack Returns the `videoTrack` having no middleware executed on it. **Kind**: instance property of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.screenShareTracks Returns the screen share tracks. **Kind**: instance property of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.audioEnabled Returns true if audio is enabled. **Kind**: instance property of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.videoEnabled Returns true if video is enabled. **Kind**: instance property of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.screenShareEnabled Returns true if screen share is enabled. **Kind**: instance property of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.addAudioMiddleware(audioMiddleware) Adds the audio middleware to be executed on the raw audio stream. If there are more than 1 audio middlewares, they will be executed in the sequence they were added in. If you want the sequence to be altered, please remove all previous middlewares and re-add. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) | Param | Type | | --------------- | --------------- | | audioMiddleware | AudioMiddleware | ### meeting.self.removeAudioMiddleware(audioMiddleware) Removes the audio middleware, if it is there. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) | Param | Type | | --------------- | --------------- | | audioMiddleware | AudioMiddleware | ### meeting.self.removeAllAudioMiddlewares() Removes all audio middlewares, if they are there. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.addVideoMiddleware(videoMiddleware) Adds the video middleware to be executed on the raw video stream. If there are more than 1 video middlewares, they will be executed in the sequence they were added in. If you want the sequence to be altered, please remove all previous middlewares and re-add. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) | Param | Type | | --------------- | --------------- | | videoMiddleware | VideoMiddleware | ### meeting.self.setVideoMiddlewareGlobalConfig(config) Sets global config to be used by video middlewares. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) | Param | Type | Description | | ------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | config | VideoMiddlewareGlobalConfig | config | | config.disablePerFrameCanvasRendering | boolean | If set to true, Instead of calling Middleware for every frame, Middleware will only be called once that too with empty canvas, it is the responsibility of the middleware author to keep updating this canvas. meeting.self.rawVideoTrack can be used to retrieve video track for the periodic updates. | ### meeting.self.removeVideoMiddleware(videoMiddleware) Removes the video middleware, if it is there. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) | Param | Type | | --------------- | --------------- | | videoMiddleware | VideoMiddleware | ### meeting.self.removeAllVideoMiddlewares() Removes all video middlewares, if they are there. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.getCurrentDevices() Returns the media devices currently being used. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.getAudioDevices() Returns the local participant's audio devices. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.getVideoDevices() Returns the local participant's video devices. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.getSpeakerDevices() Returns the local participant's speaker devices. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) ### meeting.self.getDeviceById(deviceId, kind) Returns the local participant's device, indexed by ID and kind. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) | Param | Type | Description | | | -------- | ------------------ | --------------------- | ------------------------------------------------- | | deviceId | string | The ID of the device. | | | kind | 'audio' \| 'video' | 'speaker' | The kind of the device: audio, video, or speaker. | ### meeting.self.setDevice(device) Change the current media device that is being used by the local participant. **Kind**: instance method of [RTKSelfMedia](#module%5FRTKSelfMedia) | Param | Type | Description | | ------ | --------------- | ---------------------------------------------------------------------------------------------- | | device | MediaDeviceInfo | The device that is to be used. A device of the same kind will be replaced. the primary stream. | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkselfmedia/#page","headline":"RTKSelfMedia · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkselfmedia/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKStage image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKStage Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkstage/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RTKStage module represents a class to mange the RTKStage of the meeting RTKStage refers to a virtual area, where participants stream are visible to other participants. When a participant is off stage, they are not producing media but only consuming media from participants who are on RTKStage * [RTKStage](#module%5FRTKStage) * [.peerId](#module%5FRTKStage+peerId) * [.getAccessRequests()](#module%5FRTKStage+getAccessRequests) * [.requestAccess()](#module%5FRTKStage+requestAccess) * [.cancelRequestAccess()](#module%5FRTKStage+cancelRequestAccess) * [.grantAccess()](#module%5FRTKStage+grantAccess) * [.denyAccess()](#module%5FRTKStage+denyAccess) * [.join()](#module%5FRTKStage+join) * [.leave()](#module%5FRTKStage+leave) * [.kick(userIds)](#module%5FRTKStage+kick) ### meeting.stage.peerId Returns the peerId of the current user **Kind**: instance property of [RTKStage](#module%5FRTKStage) ### meeting.stage.getAccessRequests() Method to fetch all RTKStage access requests from viewers **Kind**: instance method of [RTKStage](#module%5FRTKStage) ### meeting.stage.requestAccess() Method to send a request to privileged users to join the stage **Kind**: instance method of [RTKStage](#module%5FRTKStage) ### meeting.stage.cancelRequestAccess() Method to cancel a previous RTKStage join request **Kind**: instance method of [RTKStage](#module%5FRTKStage) ### meeting.stage.grantAccess() Method to grant access to RTKStage. This can be in response to a RTKStage Join request but it can be called on other users as well `permissions.acceptStageRequests` privilege required **Kind**: instance method of [RTKStage](#module%5FRTKStage) ### meeting.stage.denyAccess() Method to deny access to RTKStage. This should be called in response to a RTKStage Join request **Kind**: instance method of [RTKStage](#module%5FRTKStage) ### meeting.stage.join() Method to join the stage Users either need to have the permission in the preset or must be accepted by a privileged user to call this method **Kind**: instance method of [RTKStage](#module%5FRTKStage) ### meeting.stage.leave() Method to leave the stage Users must either be on the stage already or be accepted to join the stage to call this method **Kind**: instance method of [RTKStage](#module%5FRTKStage) ### meeting.stage.kick(userIds) Method to kick a user off the stage `permissions.acceptStageRequests` privilege required **Kind**: instance method of [RTKStage](#module%5FRTKStage) | Param | Type | | ------- | -------------- | | userIds | Array. | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkstage/#page","headline":"RTKStage · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkstage/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKStore image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKStore Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkstore/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This module represents a single global store. The store can be accessed from the `meeting.stores` module. **Returns**: An instance of RTKStore. **Example** ```js const handRaiseStore = meeting.stores.stores.get('handRaise'); ``` * [RTKStore](#module%5FRTKStore) ⇒ * [.set(key, value, \[sync\], \[emit\])](#module%5FRTKStore+set) ⇒ `Promise.` * [.bulkSet(data)](#module%5FRTKStore+bulkSet) ⇒ `Promise.` * [.update(key, value, \[sync\])](#module%5FRTKStore+update) ⇒ `Promise.` * [.delete(key, \[sync\], \[emit\])](#module%5FRTKStore+delete) ⇒ `Promise.` * [.bulkDelete(data)](#module%5FRTKStore+bulkDelete) ⇒ `Promise.` * [.get(key)](#module%5FRTKStore+get) ⇒ `any` * [.getAll()](#module%5FRTKStore+getAll) ⇒ `RTKStoreData` * [.clear()](#module%5FRTKStore+clear) * [.updateRateLimits(num, period)](#module%5FRTKStore+updateRateLimits) * [.updateBulkRateLimits(num, period)](#module%5FRTKStore+updateBulkRateLimits) * [.subscribe(key, cb)](#module%5FRTKStore+subscribe) ⇒ `void` * [.unsubscribe(key, \[cb\])](#module%5FRTKStore+unsubscribe) ⇒ `void` * [.populate(data)](#module%5FRTKStore+populate) ### store.set(key, value, \[sync\], \[emit\]) ⇒ `Promise.` Sets a value in the store. **Kind**: instance method of [RTKStore](#module%5FRTKStore) **Returns**: `Promise.` \- A promise. | Param | Type | Default | Description | | -------- | ------- | ------- | --------------------------------------- | | key | string | | Unique identifier used to store value. | | value | any | | Data to be set. | | \[sync\] | boolean | true | Whether to sync change to remote store. | | \[emit\] | boolean | false | Whether to emit to local subscribers. | ### store.bulkSet(data) ⇒ `Promise.` Sets multiple values in the store. **Kind**: instance method of [RTKStore](#module%5FRTKStore) **Returns**: `Promise.` \- A promise. | Param | Type | | ----- | ----------------------------------- | | data | Array.<{key: string, payload: any}> | ### store.update(key, value, \[sync\]) ⇒ `Promise.` Updates an already existing value in the store. If the value stored is `['a', 'b']`, the operation `store.update(key, ['c'])` will modify the value to `['a','b','c']`. **Kind**: instance method of [RTKStore](#module%5FRTKStore) **Returns**: `Promise.` \- A promise. | Param | Type | Default | Description | | -------- | ------- | ------- | --------------------------------------- | | key | string | | Unique identifier used to store value. | | value | any | | Data to be updated. | | \[sync\] | boolean | true | Whether to sync change to remote store. | ### store.delete(key, \[sync\], \[emit\]) ⇒ `Promise.` Deletes a key value pair form the store. **Kind**: instance method of [RTKStore](#module%5FRTKStore) **Returns**: `Promise.` \- A promise. | Param | Type | Default | Description | | -------- | ------- | ------- | --------------------------------------- | | key | string | | Unique identifier used to store value. | | \[sync\] | boolean | true | Whether to sync change to remote store. | | \[emit\] | boolean | false | Whether to emit to local subscribers. | ### store.bulkDelete(data) ⇒ `Promise.` Deletes multiple values from the store. **Kind**: instance method of [RTKStore](#module%5FRTKStore) **Returns**: `Promise.` \- A promise. | Param | Type | | ----- | --------------------- | | data | Array.<{key: string}> | ### store.get(key) ⇒ `any` Returns value for the given key. **Kind**: instance method of [RTKStore](#module%5FRTKStore) **Returns**: `any` \- Value for the given key. | Param | Type | Description | | ----- | ------ | -------------------------------------- | | key | string | Unique identifier used to store value. | ### store.getAll() ⇒ `RTKStoreData` Returns the entire store. **Kind**: instance method of [RTKStore](#module%5FRTKStore) **Returns**: `RTKStoreData` \- An instance of RTKStoreData. ### store.clear() Clears all data in the store. **Kind**: instance method of [RTKStore](#module%5FRTKStore) ### store.updateRateLimits(num, period) **Kind**: instance method of [RTKStore](#module%5FRTKStore) | Param | Type | | ------ | ------ | | num | number | | period | number | ### store.updateBulkRateLimits(num, period) **Kind**: instance method of [RTKStore](#module%5FRTKStore) | Param | Type | | ------ | ------ | | num | number | | period | number | ### store.subscribe(key, cb) ⇒ `void` Listens for data change on a store key. **Kind**: instance method of [RTKStore](#module%5FRTKStore) **Returns**: `void` \- void | Param | Type | Description | | ----- | -------- | --------------------------------------------------------------- | | key | string | Unique identifier used to store value. | | cb | function | The callback function that gets executed when data is modified. | ### store.unsubscribe(key, \[cb\]) ⇒ `void` Removes all listeners for a key on the store. **Kind**: instance method of [RTKStore](#module%5FRTKStore) **Returns**: `void` \- void | Param | Type | Description | | ------ | -------- | -------------------------------------- | | key | string | Unique identifier used to store value. | | \[cb\] | function | Callback to be removed. | ### store.populate(data) **Kind**: instance method of [RTKStore](#module%5FRTKStore) | Param | Type | | ----- | ------------ | | data | RTKStoreData | Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkstore/#page","headline":"RTKStore · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkstore/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- title: RTKThemePreset image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # RTKThemePreset Last updated Jul 22, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkthemepreset/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) The RTKThemePreset class represents the meeting theme for the current participant * [RTKThemePreset](#module%5FRTKThemePreset) * ~~[.setupScreen](#module%5FRTKThemePreset+setupScreen)~~ * ~~[.waitingRoom](#module%5FRTKThemePreset+waitingRoom)~~ * ~~[.controlBar](#module%5FRTKThemePreset+controlBar)~~ * ~~[.header](#module%5FRTKThemePreset+header)~~ * ~~[.pipMode](#module%5FRTKThemePreset+pipMode)~~ * [.viewType](#module%5FRTKThemePreset+viewType) * [.livestreamViewerQualities](#module%5FRTKThemePreset+livestreamViewerQualities) * [.maxVideoStreams](#module%5FRTKThemePreset+maxVideoStreams) * [.maxScreenShareCount](#module%5FRTKThemePreset+maxScreenShareCount) * ~~[.plugins](#module%5FRTKThemePreset+plugins)~~ * [.disabledPlugins](#module%5FRTKThemePreset+disabledPlugins) ### ~~meeting.self.config.setupScreen~~ _**Deprecated**_ **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### ~~meeting.self.config.waitingRoom~~ _**Deprecated**_ **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### ~~meeting.self.config.controlBar~~ _**Deprecated**_ **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### ~~meeting.self.config.header~~ _**Deprecated**_ **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### ~~meeting.self.config.pipMode~~ _**Deprecated**_ **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### meeting.self.config.viewType The `viewType` tells the type of the meeting possible values are: GROUP\_CALL| LIVESTREAM | CHAT | AUDIO\_ROOM **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### meeting.self.config.livestreamViewerQualities The `livestreamViewerQualities` specifies the allowed qualities of a stream, that can be viewed by a livestream viewer **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### meeting.self.config.maxVideoStreams The `maxVideoStreams` contains the maximum video streams for mobile and desktop **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### meeting.self.config.maxScreenShareCount The `maxScreenShareCount` contains the maximum possible concurrent screen shares **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### ~~meeting.self.config.plugins~~ _**Deprecated**_ **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) ### meeting.self.config.disabledPlugins The `disabledPlugins` property returns id of all disabled plugins **Kind**: instance property of [RTKThemePreset](#module%5FRTKThemePreset) Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkthemepreset/#page","headline":"RTKThemePreset · Cloudflare Realtime docs","url":"https://developers.cloudflare.com/realtime/realtimekit/core/api-reference/rtkthemepreset/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-22","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Create and manage breakout rooms to split participants into smaller groups in RealtimeKit. title: Breakout Rooms image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Breakout Rooms Last updated Jul 9, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/breakout-rooms/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This page is not available for the **Flutter**platform. WebMobile ReactWeb ComponentsAngular Note The breakout rooms feature, also known as connected meetings, is currently in beta, which means it is still being tested and evaluated, and may undergo some changes. Breakout rooms allow participants of a meeting to split into smaller groups for targeted discussions and collaboration. With the rise of remote work and online learning, breakout rooms have become an essential tool for enhancing engagement and building community in virtual settings. They are an ideal choice for workshops, online classrooms, or when you need to speak privately with select participants outside the main meeting. In RealtimeKit, breakout rooms are created as a separate meeting. Each breakout room is an independent meeting and can be managed like any other RealtimeKit meeting. RealtimeKit provides a set of SDK APIs to create, manage, and switch between breakout rooms. ## Key features The following are some of the key features of RealtimeKit's breakout rooms: * Manage permissions and privileges of hosts and participants using presets * Hosts can create breakout rooms, assign participants, start and close the breakout rooms, and switch between rooms * Participants can start and stop video, interact with other participants using chat and polls, and mute/unmute audio * Record all breakout sessions individually like any other RealtimeKit meeting ## Roles in a breakout room Roles in the breakout room are managed by presets. ### Host Hosts can create breakout rooms, assign participants, start and close the breakout rooms, and switch between rooms. ### Participants As a participant in a breakout room, you can: * **Switch to Parent Meeting** \- Switch back to the main meeting (if you have the required permissions) * **Switch Connected Meetings** \- Move from the main meeting to smaller, focused discussion groups (breakout rooms) for collaboration * **Collaborate** \- Use tools such as chat and polls during breakout sessions ## Audio and video Each breakout room functions as an independent meeting. When you switch to a breakout room from the main meeting, it automatically switches to the audio and video of the breakout session. You can mute or unmute your audio and start or stop your video at any time during the breakout session, just as you can in the main meeting. When the breakout session ends, your audio and video automatically switch back to the main meeting. * If your video was turned on during a breakout session, it will remain on when you return to the main session * If your microphone was on during a breakout session, it will stay on when you return to the main session ## Recording breakout sessions Each breakout session is a separate session. Each breakout session's recording is stored and managed separately, just like any other RealtimeKit meeting. For more information, refer to [Recording](https://developers.cloudflare.com/realtime/realtimekit/recording-guide/). ## Breakout rooms management Breakout rooms allow the participants to split into separate sessions. The host can create breakout rooms, assign participants, start and close the breakout rooms. ### Create presets A preset is a set of permissions and UI configurations that are applied to hosts and participants. They determine the look, feel, and behavior of the breakout room. For breakout rooms, you must provide the following permissions for hosts and participants in Connected Meetings: #### Host The host preset should have **Full Access** permission in Connected Meetings. This allows the host to: * Create breakout rooms * Assign participants to rooms * Start and close breakout rooms * Switch between rooms #### Participants You can choose to provide the following permissions to participants: * **Switch Connected Meetings** \- Allows participants to move between breakout rooms * **Switch to Parent Meeting** \- Allows participants to return to the main meeting ### Save the preset 1. Once you have made all the changes to your preset, click **Save** 2. Enter a name for your preset and click **Save** 3. Your preset is listed - click **Edit** to make any changes ### Create a meeting Create a RealtimeKit meeting using the [Create meeting API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/create/). This API returns a unique identifier for your meeting. ### Add participants After creating the meeting, add each participant using the [Add participant API](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/add%5Fparticipant/). The `presetName` created earlier must be passed in the body of the Add Participant API request. ### Validate permissions Before creating breakout rooms, validate the permissions of the current participant to ensure that the participant has the required permissions to create breakout rooms. Incorrect permissions can lead to errors being thrown. ```js // Check if breakout rooms are supported const areBreakoutRoomsSupported = meeting.connectedMeetings.supportsConnectedMeetings; // Check if any breakout rooms are already created const areBreakoutRoomsActive = meeting.connectedMeetings.isActive; // Check if the current participant has permission to create breakout rooms const hasPermissionToCreateBreakoutRooms = meeting.self.permissions.connectedMeetings.canAlterConnectedMeetings; // Check if the current participant has permission to switch to parent meeting const hasPermissionToSwitchToParentMeeting = meeting.self.permissions.connectedMeetings.canSwitchToParentMeeting; // Check if the current participant has permission to switch to connected meeting const hasPermissionToSwitchToConnectedMeeting = meeting.self.permissions.connectedMeetings.canSwitchConnectedMeetings; ``` ```js // Check if breakout rooms are supported const areBreakoutRoomsSupported = meeting.connectedMeetings.supportsConnectedMeetings; // Check if any breakout rooms are already created const areBreakoutRoomsActive = meeting.connectedMeetings.isActive; // Check if the current participant has permission to create breakout rooms const hasPermissionToCreateBreakoutRooms = meeting.self.permissions.connectedMeetings.canAlterConnectedMeetings; // Check if the current participant has permission to switch to parent meeting const hasPermissionToSwitchToParentMeeting = meeting.self.permissions.connectedMeetings.canSwitchToParentMeeting; // Check if the current participant has permission to switch to connected meeting const hasPermissionToSwitchToConnectedMeeting = meeting.self.permissions.connectedMeetings.canSwitchConnectedMeetings; ``` ```js // Check if breakout rooms are supported const areBreakoutRoomsSupported = meeting.connectedMeetings.supportsConnectedMeetings; // Check if any breakout rooms are already created const areBreakoutRoomsActive = meeting.connectedMeetings.isActive; // Check if the current participant has permission to create breakout rooms const hasPermissionToCreateBreakoutRooms = meeting.self.permissions.connectedMeetings.canAlterConnectedMeetings; // Check if the current participant has permission to switch to parent meeting const hasPermissionToSwitchToParentMeeting = meeting.self.permissions.connectedMeetings.canSwitchToParentMeeting; // Check if the current participant has permission to switch to connected meeting const hasPermissionToSwitchToConnectedMeeting = meeting.self.permissions.connectedMeetings.canSwitchConnectedMeetings; ``` ```kotlin val connectedMeetings = meeting.connectedMeetings // Check if breakout rooms are active val areBreakoutRoomsActive = connectedMeetings.isActive // Check permissions on the local participant val permissions = meeting.self.permissions.connectedMeetings // Check if the current participant has permission to create/alter breakout rooms val hasPermissionToAlterBreakoutRooms = permissions.canAlterConnectedMeetings // Check if the current participant has permission to switch between breakout rooms val hasPermissionToSwitchConnectedMeetings = permissions.canSwitchConnectedMeetings // Check if the current participant has permission to switch back to the parent meeting val hasPermissionToSwitchToParentMeeting = permissions.canSwitchToParentMeeting ``` ```swift let connectedMeetings = meeting.connectedMeetings // Check if breakout rooms are active let areBreakoutRoomsActive = connectedMeetings.isActive // Check permissions on the local participant let permissions = meeting.self.permissions.connectedMeetings // Check if the current participant has permission to create/alter breakout rooms let hasPermissionToAlterBreakoutRooms = permissions.canAlterConnectedMeetings // Check if the current participant has permission to switch between breakout rooms let hasPermissionToSwitchConnectedMeetings = permissions.canSwitchConnectedMeetings // Check if the current participant has permission to switch back to the parent meeting let hasPermissionToSwitchToParentMeeting = permissions.canSwitchToParentMeeting ``` ```tsx const [meeting] = useRealtimeKitClient(); // Check if any breakout rooms are currently active const areBreakoutRoomsActive = meeting?.connectedMeetings.isActive; // Check whether the local participant can create, rename, or delete rooms and move any participant const canAlterBreakoutRooms = meeting?.self.permissions.connectedMeetings.canAlterConnectedMeetings; // Check whether the local participant can move themselves to any breakout room const canSwitchRooms = meeting?.self.permissions.connectedMeetings.canSwitchConnectedMeetings; // Check whether the local participant can return to the parent meeting const canReturnToParent = meeting?.self.permissions.connectedMeetings.canSwitchToParentMeeting; ``` ### Create breakout rooms ```js const breakoutRooms = await meeting.connectedMeetings.createMeetings([ { title: "Breakout Room 1" }, { title: "Breakout Room 2" }, { title: "Breakout Room 3" }, ]); console.log("Created Breakout Rooms: ", breakoutRooms.map((room) => "Id:: " + room.id + " --- Title:: " + room.title).join("\n")); ``` `breakoutRooms` is an array of basic meeting information such as id and title. You can use the `id` of these objects to move participants to the breakout room. ```js const breakoutRooms = await meeting.connectedMeetings.createMeetings([ { title: "Breakout Room 1" }, { title: "Breakout Room 2" }, { title: "Breakout Room 3" }, ]); console.log("Created Breakout Rooms: ", breakoutRooms.map((room) => "Id:: " + room.id + " --- Title:: " + room.title).join("\n")); ``` `breakoutRooms` is an array of basic meeting information such as id and title. You can use the `id` of these objects to move participants to the breakout room. ```js const breakoutRooms = await meeting.connectedMeetings.createMeetings([ { title: "Breakout Room 1" }, { title: "Breakout Room 2" }, { title: "Breakout Room 3" }, ]); console.log("Created Breakout Rooms: ", breakoutRooms.map((room) => "Id:: " + room.id + " --- Title:: " + room.title).join("\n")); ``` `breakoutRooms` is an array of basic meeting information such as id and title. You can use the `id` of these objects to move participants to the breakout room. ```kotlin meeting.connectedMeetings.createMeetings( titles = listOf("Breakout Room 1", "Breakout Room 2", "Breakout Room 3") ) { result -> result.onSuccess { breakoutRooms -> val roomInfo = breakoutRooms.joinToString("\n") { "Id:: ${it.id} --- Title:: ${it.title}" } println("Created Breakout Rooms: $roomInfo") }.onFailure { error -> println("Failed to create breakout rooms: ${error.value.message}") } } ``` `breakoutRooms` is a list of basic meeting information such as `id` and `title`. You can use the `id` of these objects to move participants to the breakout room. ```swift meeting.connectedMeetings.createMeetings(titles: ["Breakout Room 1", "Breakout Room 2", "Breakout Room 3"]) { result in switch result { case .success(let breakoutRooms): let roomInfo = breakoutRooms.map { "Id:: \($0.id) --- Title:: \($0.title)" }.joined(separator: "\n") print("Created Breakout Rooms: \(roomInfo)") case .failure(let error): print("Failed to create breakout rooms: \(error.value.message)") } } ``` `breakoutRooms` is an array of basic meeting information such as `id` and `title`. You can use the `id` of these objects to move participants to the breakout room. ```js const breakoutRooms = await meeting.connectedMeetings.createMeetings([ { title: "Breakout Room 1" }, { title: "Breakout Room 2" }, { title: "Breakout Room 3" }, ]); console.log("Created Breakout Rooms: ", breakoutRooms.map((room) => "Id:: " + room.id + " --- Title:: " + room.title).join("\n")); ``` `breakoutRooms` is an array of basic meeting information such as id and title. You can use the `id` of these objects to move participants to the breakout room. ### Retrieve list of breakout rooms and their participants If there are more than one host in the room creating breakouts, you can retrieve consolidated list of breakout rooms using the following API. ```js const breakoutRoomsInfo = await meeting.connectedMeetings.getConnectedMeetings(); ``` `breakoutRoomsInfo` is an object containing the list of breakout rooms and their participants, along with details of the parent meeting. This data can be used to display the list of breakout rooms & participants in the UI. This API refetches the list of breakout rooms & participants, therefore can be considered the source of truth for breakout rooms & participants. It is advised to call this API, to get the latest list of breakout rooms & participants, if a lot of changes are in progress. You can also listen to `stateUpdate` event to get the latest list of breakout rooms & participants, as they are updated in real-time. ```js meeting.connectedMeetings.on("stateUpdate", ({meetings, parentMeeting}) => { console.log("stateUpdate", {meetings, parentMeeting}); // Alternatively, you can access the meetings and parentMeeting from the connectedMeetings object console.log("Meetings List", meeting.connectedMeetings.meetings); console.log("Parent Meeting", meeting.connectedMeetings.parentMeeting); }); ``` ```js const breakoutRoomsInfo = await meeting.connectedMeetings.getConnectedMeetings(); ``` `breakoutRoomsInfo` is an object containing the list of breakout rooms and their participants, along with details of the parent meeting. This data can be used to display the list of breakout rooms & participants in the UI. This API refetches the list of breakout rooms & participants, therefore can be considered the source of truth for breakout rooms & participants. It is advised to call this API, to get the latest list of breakout rooms & participants, if a lot of changes are in progress. You can also listen to `stateUpdate` event to get the latest list of breakout rooms & participants, as they are updated in real-time. ```js meeting.connectedMeetings.on("stateUpdate", ({meetings, parentMeeting}) => { console.log("stateUpdate", {meetings, parentMeeting}); // Alternatively, you can access the meetings and parentMeeting from the connectedMeetings object console.log("Meetings List", meeting.connectedMeetings.meetings); console.log("Parent Meeting", meeting.connectedMeetings.parentMeeting); }); ``` ```js const breakoutRooms = await meeting.connectedMeetings.createMeetings([ { title: "Breakout Room 1" }, { title: "Breakout Room 2" }, { title: "Breakout Room 3" }, ]); console.log("Created Breakout Rooms: ", breakoutRooms.map((room) => "Id:: " + room.id + " --- Title:: " + room.title).join("\n")); ``` `breakoutRooms` is an array of basic meeting information such as id and title. You can use the `id` of these objects to move participants to the breakout room. ```kotlin meeting.connectedMeetings.getConnectedMeetings { result -> result.onSuccess { breakoutRoomsInfo -> println("Parent Meeting: ${breakoutRoomsInfo.parentMeeting}") println("Breakout Rooms: ${breakoutRoomsInfo.meetings}") }.onFailure { error -> println("Failed to fetch breakout rooms: ${error.value.message}") } } ``` `breakoutRoomsInfo` is an object containing the list of breakout rooms and their participants, along with details of the parent meeting. This data can be used to display the list of breakout rooms and participants in the UI. This API refetches the list of breakout rooms and participants, and can therefore be considered the source of truth. It is advised to call this API to get the latest state if a lot of changes are in progress. You can also listen to the `onStateUpdate` event to receive real-time updates to the list of breakout rooms and participants. ```kotlin meeting.addConnectedMeetingsEventListener(object : RtkConnectedMeetingsEventListener { override fun onStateUpdate(meetings: List, parentMeeting: RtkConnectedMeeting?) { println("State updated. Meetings: $meetings, Parent: $parentMeeting") // Alternatively, access state directly from the connectedMeetings object println("Meetings List: ${meeting.connectedMeetings.meetings}") println("Parent Meeting: ${meeting.connectedMeetings.parentMeeting}") } override fun onChangingMeeting(meetingId: String) {} override fun onMeetingChanged(error: MeetingError?) {} }) ``` ```swift meeting.connectedMeetings.getConnectedMeetings { result in switch result { case .success(let breakoutRoomsInfo): print("Parent Meeting: \(String(describing: breakoutRoomsInfo.parentMeeting))") print("Breakout Rooms: \(breakoutRoomsInfo.meetings)") case .failure(let error): print("Failed to fetch breakout rooms: \(error.value.message)") } } ``` `breakoutRoomsInfo` is an object containing the list of breakout rooms and their participants, along with details of the parent meeting. This data can be used to display the list of breakout rooms and participants in the UI. This API refetches the list of breakout rooms and participants, and can therefore be considered the source of truth. It is advised to call this API to get the latest state if a lot of changes are in progress. You can also listen to the `onStateUpdate` event to receive real-time updates to the list of breakout rooms and participants. ```swift class MyListener: RtkConnectedMeetingsEventListener { func onStateUpdate(meetings: [RtkConnectedMeeting], parentMeeting: RtkConnectedMeeting?) { print("State updated. Meetings: \(meetings), Parent: \(String(describing: parentMeeting))") // Alternatively, access state directly from the connectedMeetings object print("Meetings List: \(meeting.connectedMeetings.meetings)") print("Parent Meeting: \(String(describing: meeting.connectedMeetings.parentMeeting))") } func onChangingMeeting(meetingId: String) {} func onMeetingChanged(error: MeetingError?) {} } meeting.addConnectedMeetingsEventListener(MyListener()) ``` ```js const breakoutRoomsInfo = await meeting.connectedMeetings.getConnectedMeetings(); ``` `breakoutRoomsInfo` is an object containing the list of breakout rooms and their participants, along with details of the parent meeting. This data can be used to display the list of breakout rooms & participants in the UI. This API refetches the list of breakout rooms & participants, therefore can be considered the source of truth for breakout rooms & participants. It is advised to call this API, to get the latest list of breakout rooms & participants, if a lot of changes are in progress. You can also listen to `stateUpdate` event to get the latest list of breakout rooms & participants, as they are updated in real-time. ```js meeting.connectedMeetings.on("stateUpdate", ({meetings, parentMeeting}) => { console.log("stateUpdate", {meetings, parentMeeting}); // Alternatively, you can access the meetings and parentMeeting from the connectedMeetings object console.log("Meetings List", meeting.connectedMeetings.meetings); console.log("Parent Meeting", meeting.connectedMeetings.parentMeeting); }); ``` ### Move participants to breakout rooms Once you have created breakout rooms, assign participants to the rooms. ```js // Retrieve list of breakout rooms & participants const breakoutRoomsInfo = await meeting.connectedMeetings.getConnectedMeetings(); /* * You can retrieve meetingIds and participantIds from the breakoutRoomsInfo object. * Based on where the participant currently is, you can decide the sourceMeetingId and targetMeetingId. */ // Move participants to breakout rooms const response = await meeting.connectedMeetings.moveParticipants( "SOURCE_MEETING_ID", // sourceMeetingId, meeting id where participants are currently in "TARGET_MEETING_ID", // targetMeetingId, meeting id where participants are to be moved ["PARTICIPANT_ID_1", "PARTICIPANT_ID_2"], // participantIds, array of participant ids to be moved ); ``` ```js // Retrieve list of breakout rooms & participants const breakoutRoomsInfo = await meeting.connectedMeetings.getConnectedMeetings(); /* * You can retrieve meetingIds and participantIds from the breakoutRoomsInfo object. * Based on where the participant currently is, you can decide the sourceMeetingId and targetMeetingId. */ // Move participants to breakout rooms const response = await meeting.connectedMeetings.moveParticipants( "SOURCE_MEETING_ID", // sourceMeetingId, meeting id where participants are currently in "TARGET_MEETING_ID", // targetMeetingId, meeting id where participants are to be moved ["PARTICIPANT_ID_1", "PARTICIPANT_ID_2"], // participantIds, array of participant ids to be moved ); ``` ```js // Retrieve list of breakout rooms & participants const breakoutRoomsInfo = await meeting.connectedMeetings.getConnectedMeetings(); /* * You can retrieve meetingIds and participantIds from the breakoutRoomsInfo object. * Based on where the participant currently is, you can decide the sourceMeetingId and targetMeetingId. */ // Move participants to breakout rooms const response = await meeting.connectedMeetings.moveParticipants( "SOURCE_MEETING_ID", // sourceMeetingId, meeting id where participants are currently in "TARGET_MEETING_ID", // targetMeetingId, meeting id where participants are to be moved ["PARTICIPANT_ID_1", "PARTICIPANT_ID_2"], // participantIds, array of participant ids to be moved ); ``` ```kotlin // Retrieve list of breakout rooms & participants meeting.connectedMeetings.getConnectedMeetings { result -> result.onSuccess { breakoutRoomsInfo -> /* * You can retrieve meetingIds and participantIds from breakoutRoomsInfo. * Based on where the participant currently is, decide the sourceMeetingId and targetMeetingId. */ // Move participants to a breakout room meeting.connectedMeetings.moveParticipants( sourceMeetingId = "SOURCE_MEETING_ID", destinationMeetingId = "TARGET_MEETING_ID", participantIds = listOf("PARTICIPANT_ID_1", "PARTICIPANT_ID_2") ) { error -> if (error != null) { println("Failed to move participants: $error") } else { println("Participants moved successfully") } } } } ``` ```swift // Retrieve list of breakout rooms & participants meeting.connectedMeetings.getConnectedMeetings { result in switch result { case .success(let breakoutRoomsInfo): /* * You can retrieve meetingIds and participantIds from breakoutRoomsInfo. * Based on where the participant currently is, decide the fromMeetingId and toMeetingId. */ // Move participants to a breakout room meeting.connectedMeetings.moveParticipants( sourceMeetingId: "SOURCE_MEETING_ID", destinationMeetingId: "TARGET_MEETING_ID", participantIds: ["PARTICIPANT_ID_1", "PARTICIPANT_ID_2"] ) { error in if let error = error { print("Failed to move participants: \(error)") } else { print("Participants moved successfully") } } case .failure(let error): print("Failed to fetch breakout rooms: \(error.value.message)") } } ``` ```js // Retrieve list of breakout rooms & participants const breakoutRoomsInfo = await meeting.connectedMeetings.getConnectedMeetings(); /* * You can retrieve meetingIds and participantIds from the breakoutRoomsInfo object. * Based on where the participant currently is, you can decide the sourceMeetingId and targetMeetingId. */ // Move participants to breakout rooms const response = await meeting.connectedMeetings.moveParticipants( "SOURCE_MEETING_ID", // sourceMeetingId, meeting id where participants are currently in "TARGET_MEETING_ID", // targetMeetingId, meeting id where participants are to be moved ["PARTICIPANT_ID_1", "PARTICIPANT_ID_2"], // participantIds, array of participant ids to be moved ); ``` ### Move participants, with a specific preset, to breakout rooms Once you have created breakout rooms, assign participants to the rooms. ```js const response = await meeting.connectedMeetings.moveParticipantsWithCustomPreset( "SOURCE_MEETING_ID", // sourceMeetingId, meeting id where participants are currently in "TARGET_MEETING_ID", // targetMeetingId, meeting id where participants are to be moved [{ presetId: "PRESET_ID_1" }, { presetId: "PRESET_ID_2" }], // array of objects with presetId field ); ``` ```js const response = await meeting.connectedMeetings.moveParticipantsWithCustomPreset( "SOURCE_MEETING_ID", // sourceMeetingId, meeting id where participants are currently in "TARGET_MEETING_ID", // targetMeetingId, meeting id where participants are to be moved [{ presetId: "PRESET_ID_1" }, { presetId: "PRESET_ID_2" }], // array of objects with presetId field ); ``` ```js const response = await meeting.connectedMeetings.moveParticipantsWithCustomPreset( "SOURCE_MEETING_ID", // sourceMeetingId, meeting id where participants are currently in "TARGET_MEETING_ID", // targetMeetingId, meeting id where participants are to be moved [{ presetId: "PRESET_ID_1" }, { presetId: "PRESET_ID_2" }], // array of objects with presetId field ); ``` ```kotlin meeting.connectedMeetings.moveParticipantsWithCustomPreset( sourceMeetingId = "SOURCE_MEETING_ID", destinationMeetingId = "TARGET_MEETING_ID", participants = listOf( MoveParticipantPayload(id = "PARTICIPANT_ID_1", presetId = "PRESET_ID_1"), MoveParticipantPayload(id = "PARTICIPANT_ID_2", presetId = "PRESET_ID_2") ) ) { error -> if (error != null) { println("Failed to move participants: $error") } else { println("Participants moved with custom preset successfully") } } ``` ```swift meeting.connectedMeetings.moveParticipantsWithCustomPreset( sourceMeetingId: "SOURCE_MEETING_ID", destinationMeetingId: "TARGET_MEETING_ID", participants: [ MoveParticipantPayload(id: "PARTICIPANT_ID_1", presetId: "PRESET_ID_1"), MoveParticipantPayload(id: "PARTICIPANT_ID_2", presetId: "PRESET_ID_2") ] ) { error in if let error = error { print("Failed to move participants: \(error)") } else { print("Participants moved with custom preset successfully") } } ``` ```js const response = await meeting.connectedMeetings.moveParticipantsWithCustomPreset( "SOURCE_MEETING_ID", // sourceMeetingId, meeting id where participants are currently in "TARGET_MEETING_ID", // targetMeetingId, meeting id where participants are to be moved [{ presetId: "PRESET_ID_1" }, { presetId: "PRESET_ID_2" }], // array of objects with presetId field ); ``` ### Move local participant to breakout room To move the local participant to a different breakout room or back to the parent meeting, use the same API as for moving other participants, but pass the local participant's ID. The local participant must have the appropriate permissions: `canSwitchConnectedMeetings` to switch between breakout rooms, or `canSwitchToParentMeeting` to return to the parent meeting, if the request was originated by the non-host local participant. ### Handle breakout room events If a participant has been moved to a breakout room, the `changingMeeting` event is triggered, followed by the `meetingChanged` event. These events are also triggered when a participant switches between the main meeting and breakout rooms. Participants will autojoin the breakout room if they are assigned to it. You won't have to join meeting explicitly. ```js // Listen to changingMeeting event to show a custom UI to indicate that a meeting switch is happening meeting.connectedMeetings.on("changingMeeting", (meetingId) => { console.log("Switching to breakout room or main meeting with id: " + meetingId); console.log("Show a Custom UI to indicate that a meeting switch is happening"); }); // Listen to meetingChanged event to update the meeting object reference meeting.connectedMeetings.on("meetingChanged", (newMeeting) => { console.log("Switched to breakout room or main meeting"); console.log("Every action now should be performed on this meeting"); }); ``` ```js // Listen to changingMeeting event to show a custom UI to indicate that a meeting switch is happening meeting.connectedMeetings.on("changingMeeting", (meetingId) => { console.log("Switching to breakout room or main meeting with id: " + meetingId); console.log("Show a Custom UI to indicate that a meeting switch is happening"); }); // Listen to meetingChanged event to update the meeting object reference meeting.connectedMeetings.on("meetingChanged", (newMeeting) => { console.log("Switched to breakout room or main meeting"); console.log("Every action now should be performed on this meeting"); }); ``` ```js // Listen to changingMeeting event to show a custom UI to indicate that a meeting switch is happening meeting.connectedMeetings.on("changingMeeting", (meetingId) => { console.log("Switching to breakout room or main meeting with id: " + meetingId); console.log("Show a Custom UI to indicate that a meeting switch is happening"); }); // Listen to meetingChanged event to update the meeting object reference meeting.connectedMeetings.on("meetingChanged", (newMeeting) => { console.log("Switched to breakout room or main meeting"); console.log("Every action now should be performed on this meeting"); }); ``` ```kotlin meeting.addConnectedMeetingsEventListener(object : RtkConnectedMeetingsEventListener { // Triggered when a meeting switch is initiated — use this to show a loading UI override fun onChangingMeeting(meetingId: String) { println("Switching to breakout room or main meeting with id: $meetingId") println("Show a custom UI to indicate that a meeting switch is happening") } // Triggered when the meeting switch is complete override fun onMeetingChanged(error: MeetingError?) { if (error != null) { println("Failed to switch meeting: $error") } else { println("Switched to breakout room or main meeting successfully") } } override fun onStateUpdate(meetings: List, parentMeeting: RtkConnectedMeeting?) {} }) ``` ```swift class MyListener: RtkConnectedMeetingsEventListener { // Triggered when a meeting switch is initiated — use this to show a loading UI func onChangingMeeting(meetingId: String) { print("Switching to breakout room or main meeting with id: \(meetingId)") print("Show a custom UI to indicate that a meeting switch is happening") } // Triggered when the meeting switch is complete func onMeetingChanged(error: MeetingError?) { if let error = error { print("Failed to switch meeting: \(error)") } else { print("Switched to breakout room or main meeting successfully") } } func onStateUpdate(meetings: [RtkConnectedMeeting], parentMeeting: RtkConnectedMeeting?) {} } meeting.addConnectedMeetingsEventListener(MyListener()) ``` The `useRealtimeKitClient` hook automatically handles the `meetingChanged` event and swaps the active client reference when the local participant is moved to a breakout room or back to the main meeting. No additional setup is needed. If you are using the `RtkMeeting` UI Kit component, the transition UI (the "Joining…" screen) is also handled automatically. To show a custom loading screen during the switch, listen to the `changingMeeting` event: ```tsx useEffect(() => { if (!meeting) return; const onChangingMeeting = (meetingId: string) => { console.log("Switching to meeting:", meetingId); // Show a loading indicator }; meeting.connectedMeetings.on("changingMeeting", onChangingMeeting); return () => meeting.connectedMeetings.removeListener("changingMeeting", onChangingMeeting); }, [meeting]); ``` ### Close breakout rooms You can close/delete the breakout rooms. This will force participants in those meetings to come to the main room. ```js await meeting.connectedMeetings.deleteMeetings([ "MEETING_ID_TO_CLOSE_1", "MEETING_ID_TO_CLOSE_2", ]); ``` This would also trigger `stateUpdate` event updating the list of breakout rooms & participants. ```js meeting.connectedMeetings.on("stateUpdate", ({meetings, parentMeeting}) => { console.log("stateUpdate", {meetings, parentMeeting}); }); // Alternatively, you can access the meetings and parentMeeting from the connectedMeetings object console.log("Meetings List", meeting.connectedMeetings.meetings); console.log("Parent Meeting", meeting.connectedMeetings.parentMeeting); ``` ```js await meeting.connectedMeetings.deleteMeetings([ "MEETING_ID_TO_CLOSE_1", "MEETING_ID_TO_CLOSE_2", ]); ``` This would also trigger `stateUpdate` event updating the list of breakout rooms & participants. ```js meeting.connectedMeetings.on("stateUpdate", ({meetings, parentMeeting}) => { console.log("stateUpdate", {meetings, parentMeeting}); }); // Alternatively, you can access the meetings and parentMeeting from the connectedMeetings object console.log("Meetings List", meeting.connectedMeetings.meetings); console.log("Parent Meeting", meeting.connectedMeetings.parentMeeting); ``` ```js await meeting.connectedMeetings.deleteMeetings([ "MEETING_ID_TO_CLOSE_1", "MEETING_ID_TO_CLOSE_2", ]); ``` This would also trigger `stateUpdate` event updating the list of breakout rooms & participants. ```js meeting.connectedMeetings.on("stateUpdate", ({meetings, parentMeeting}) => { console.log("stateUpdate", {meetings, parentMeeting}); }); // Alternatively, you can access the meetings and parentMeeting from the connectedMeetings object console.log("Meetings List", meeting.connectedMeetings.meetings); console.log("Parent Meeting", meeting.connectedMeetings.parentMeeting); ``` ```kotlin meeting.connectedMeetings.deleteMeetings( meetingIds = listOf("MEETING_ID_TO_CLOSE_1", "MEETING_ID_TO_CLOSE_2") ) { error -> if (error != null) { println("Failed to close breakout rooms: $error") } else { println("Breakout rooms closed successfully") } } ``` This will also trigger the `onStateUpdate` event, updating the list of breakout rooms and participants. ```kotlin meeting.addConnectedMeetingsEventListener(object : RtkConnectedMeetingsEventListener { override fun onStateUpdate(meetings: List, parentMeeting: RtkConnectedMeeting?) { println("State updated. Meetings: $meetings, Parent: $parentMeeting") // Alternatively, access state directly from the connectedMeetings object println("Meetings List: ${meeting.connectedMeetings.meetings}") println("Parent Meeting: ${meeting.connectedMeetings.parentMeeting}") } override fun onChangingMeeting(meetingId: String) {} override fun onMeetingChanged(error: MeetingError?) {} }) ``` ```swift meeting.connectedMeetings.deleteMeetings(meetingIds: ["MEETING_ID_TO_CLOSE_1", "MEETING_ID_TO_CLOSE_2"]) { error in if let error = error { print("Failed to close breakout rooms: \(error)") } else { print("Breakout rooms closed successfully") } } ``` This will also trigger the `onStateUpdate` event, updating the list of breakout rooms and participants. ```swift class MyListener: RtkConnectedMeetingsEventListener { func onStateUpdate(meetings: [RtkConnectedMeeting], parentMeeting: RtkConnectedMeeting?) { print("State updated. Meetings: \(meetings), Parent: \(String(describing: parentMeeting))") // Alternatively, access state directly from the connectedMeetings object print("Meetings List: \(meeting.connectedMeetings.meetings)") print("Parent Meeting: \(String(describing: meeting.connectedMeetings.parentMeeting))") } func onChangingMeeting(meetingId: String) {} func onMeetingChanged(error: MeetingError?) {} } meeting.addConnectedMeetingsEventListener(MyListener()) ``` ```js await meeting.connectedMeetings.deleteMeetings([ "MEETING_ID_TO_CLOSE_1", "MEETING_ID_TO_CLOSE_2", ]); ``` This would also trigger `stateUpdate` event updating the list of breakout rooms & participants. ```js meeting.connectedMeetings.on("stateUpdate", ({meetings, parentMeeting}) => { console.log("stateUpdate", {meetings, parentMeeting}); }); // Alternatively, you can access the meetings and parentMeeting from the connectedMeetings object console.log("Meetings List", meeting.connectedMeetings.meetings); console.log("Parent Meeting", meeting.connectedMeetings.parentMeeting); ``` ## Next steps You have successfully integrated breakout rooms into your RealtimeKit application. Participants can now: * Join the main meeting * Be assigned to breakout rooms by the host * Switch between the main meeting and breakout rooms * Collaborate in smaller focused groups For more advanced customization, explore the following: * [UI Kit Components Library](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/component-library/) \- Browse available components * [UI Kit States](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/state-management/) \- Learn how components synchronize * [Build Your Own UI](https://developers.cloudflare.com/realtime/realtimekit/ui-kit/build-your-own-ui/) \- Create custom meeting interfaces Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/breakout-rooms/#page","headline":"Breakout Rooms · Cloudflare Realtime docs","description":"Create and manage breakout rooms to split participants into smaller groups in RealtimeKit.","url":"https://developers.cloudflare.com/realtime/realtimekit/core/breakout-rooms/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-07-09","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Send and receive chat messages in RealtimeKit meetings using the Core SDK. title: Chat image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Chat Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/chat/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This guide explains how to send and receive chat messages in a meeting using Cloudflare RealtimeKit. WebMobile ReactWeb ComponentsAngular ## Introduction There are three types of messages that can be sent in chat: * Text messages * Images * Files The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. ```js console.log("Chat object:", meeting.chat); ``` The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `Message`. ```js console.log("All chat messages:", meeting.chat.messages); ``` There are three types of messages that can be sent in chat: * Text messages * Images * Files The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. ```jsx meeting.chat; ``` The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `Message`. ```jsx meeting.chat.messages; ``` There are three types of messages that can be sent in chat: * Text messages * Images * Files The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. ```plaintext meeting.chat ``` The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `com.cloudflare.realtimekit.chat.ChatMessage`. ```plaintext meeting.chat.messages ``` There are three types of messages that can be sent in chat: * Text messages * Images * Files The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. ```plaintext meeting.chat ``` The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `RealtimeKit.ChatMessage`. ```plaintext meeting.chat.messages ``` There are three types of messages that can be sent in chat: * Text messages * Images * Files The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. ```plaintext meeting.chat; ``` The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `ChatMessage`. ```plaintext meeting.chat.messages; ``` There are three types of messages that can be sent in chat: * Text messages * Images * Files The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. ```jsx meeting.chat; ``` The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `Message`. ```jsx meeting.chat.messages; ``` ### Message Type The `Message` type is defined as follows: ```typescript interface BaseMessage { type: T; userId: string; displayName: string; time: Date; id: string; isEdited?: boolean; read?: boolean; pluginId?: string; pinned?: boolean; targetUserIds?: string[]; } interface TextMessage extends BaseMessage { message: string; } interface ImageMessage extends BaseMessage { link: string; } interface FileMessage extends BaseMessage { name: string; size: number; link: string; } type Message = TextMessage | ImageMessage | FileMessage; ``` The `Message` type is defined as follows: ```typescript interface BaseMessage { type: T; userId: string; displayName: string; time: Date; id: string; isEdited?: boolean; read?: boolean; pluginId?: string; pinned?: boolean; targetUserIds?: string[]; } interface TextMessage extends BaseMessage { message: string; } interface ImageMessage extends BaseMessage { link: string; } interface FileMessage extends BaseMessage { name: string; size: number; link: string; } type Message = TextMessage | ImageMessage | FileMessage; ``` The `ChatMessage` class is defined as follows: ```kotlin enum class ChatMessageType { TEXT, IMAGE, FILE } open class ChatMessage( val userId: String, val displayName: String, val read: Boolean, val pluginId: String?, val type: ChatMessageType, val time: String, val createdAtMillis: Long, val targetUserIds: List?, ) class TextMessage(val message: String): ChatMessage(...) class ImageMessage(val link: String): ChatMessage(...) class FileMessage( val name: String, val link: String, val size: Long, ): ChatMessage(...) ``` The `ChatMessage` class is defined as follows: ```swift public enum ChatMessageType { case text case image case file } open class ChatMessage { public let userId: String public let displayName: String public let read: Bool public let pluginId: String? public let type: ChatMessageType public let time: String public let createdAtMillis: Int64 public let targetUserIds: [String]? } public final class TextMessage: ChatMessage { public let message: String } public final class ImageMessage: ChatMessage { public let link: String } public final class FileMessage: ChatMessage { public let name: String public let link: String public let size: Int64 } ``` The `ChatMessage` class is defined as follows: ```dart enum MessageType { text, image, file } class ChatMessage { final String displayName; final MessageType type; final bool read; final String userId; final String? pluginId; final String time; } class TextMessage extends ChatMessage { final String message; } class ImageMessage extends ChatMessage { final String link; } class FileMessage extends ChatMessage { final String name; final String link; final int size; } ``` The `Message` type is defined as follows: ```typescript interface BaseMessage { type: T; userId: string; displayName: string; time: Date; id: string; isEdited?: boolean; read?: boolean; pluginId?: string; pinned?: boolean; targetUserIds?: string[]; } interface TextMessage extends BaseMessage { message: string; } interface ImageMessage extends BaseMessage { link: string; } interface FileMessage extends BaseMessage { name: string; size: number; link: string; } type Message = TextMessage | ImageMessage | FileMessage; ``` ## Sending a Chat Message ### Send a Text Message There is a method in `meeting.chat` to send a message of each type. To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. ```js const message = "Is this the real life?"; await meeting.chat.sendTextMessage(message); ``` There is a method in `meeting.chat` to send a message of each type. To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. ```jsx const message = "Is this the real life?"; await meeting.chat.sendTextMessage(message); ``` There is a method in `meeting.chat` to send a message of each type. To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. ```kotlin val message = "Is this the real life?" meeting.chat.sendTextMessage(message) ``` There is a method in `meeting.chat` to send a message of each type. To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. ```swift var message = "Is this the real life?" meeting.chat.sendTextMessage(message) ``` There is a method in `meeting.chat` to send a message of each type. To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. ```dart final message = "Is this the real life?"; meeting.chat.sendTextMessage(message); ``` There is a method in `meeting.chat` to send a message of each type. To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. ```jsx const message = "Is this the real life?"; await meeting.chat.sendTextMessage(message); ``` ### Send an Image You can send an image with the help of `meeting.chat.sendImageMessage()`. This accepts an image of type `File`, and sends it to the participants in the meeting. ```html ``` ```js async function onSendImage() { const image = document.getElementById("img"); await meeting.chat.sendImageMessage(image.files[0]); } ``` You can send an image with the help of `meeting.chat.sendImageMessage()`. This accepts an image of type `File`, and sends it to the participants in the meeting. ```jsx import { useRef } from "react"; function ChatComponent() { const imageInputRef = useRef(null); const onSendImage = async () => { const image = imageInputRef.current; if (image && image.files[0]) { await meeting.chat.sendImageMessage(image.files[0]); } }; return ( <> ); } ``` You can send an image with the help of `meeting.chat.sendImageMessage()` and sends it to the participants in the meeting. ```kotlin meeting.chat.sendImageMessage(imageUri) { err -> // Handle error if any } ``` You can send an image with the help of `meeting.chat.sendImageMessage()` and sends it to the participants in the meeting. ```swift meeting.chat.sendImageMessage(imageURL: url) { err in // Handle error if any } ``` You can send an image with the help of `meeting.chat.sendImageMessage()` which sends it to the participants in the meeting. It takes a string filePath as argument. ```dart final filePath = "file_path_of_image"; meeting.chat.sendImageMessage(filePath, (error) { // Handle error if any }); ``` You can send an image with the help of `meeting.chat.sendImageMessage()`. This accepts an image of type File, and sends it to the participants in the meeting. ```jsx import DocumentPicker from "@react-native-documents/picker"; async function onSendImage() { // Get the image uri and create an object with the following fields const res = await DocumentPicker.pickSingle({ type: [DocumentPicker.types.images], }); const image = { uri: res.uri, name: res.name, size: res.size, type: res.type, }; await meeting.chat.sendImageMessage(image); } ``` ### Send a File Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. ```html ``` ```js async function onSendFile() { const file = document.getElementById("file"); await meeting.chat.sendFileMessage(file.files[0]); } ``` Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. ```jsx import { useRef } from "react"; function ChatComponent() { const fileInputRef = useRef(null); const onSendFile = async () => { const file = fileInputRef.current; if (file && file.files[0]) { await meeting.chat.sendFileMessage(file.files[0]); } }; return ( <> ); } ``` Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. ```kotlin meeting.chat.sendFileMessage(fileUri) { err -> // Handle error if any } ``` Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. ```swift meeting.chat.sendFileMessage(fileURL: url) { err in // Handle error if any } ``` You can send a file with the help of `meeting.chat.sendFileMessage()` which sends it to the participants in the meeting. It takes a string filePath as argument. ```dart final filePath = "file_path_of_pdf"; meeting.chat.sendFileMessage(filePath, (error) { // Handle error if any }); ``` Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. ```jsx import DocumentPicker from "@react-native-documents/picker"; async function onSendFile() { // Get the file uri and create an object with the following fields const res = await DocumentPicker.pickSingle({ type: [DocumentPicker.types.allFiles], }); const file = { uri: res.uri, name: res.name, size: res.size, type: res.type, }; await meeting.chat.sendFileMessage(file); } ``` ### Send Any Message Type There is also a common method called `meeting.chat.sendMessage()` that can be used to send any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `sendMessage()` method accepts a parameter `message` of the following type: ```typescript async function sendMessage( message: | { type: "text"; message: string } | { type: "image"; image: File } | { type: "file"; file: File }, ) { // ... } ``` Here is how you would use the `sendMessage()` method to send a text message: ```js const message = "Is this just fantasy?"; await meeting.chat.sendMessage({ type: "text", message }); ``` There is also a common method called `meeting.chat.sendMessage()` that can be used to send any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `sendMessage()` method accepts a parameter `message` of the following type: ```typescript async function sendMessage( message: | { type: "text"; message: string } | { type: "image"; image: File } | { type: "file"; file: File }, ) { // ... } ``` Here is how you would use the `sendMessage()` method to send a text message: ```jsx const message = "Is this just fantasy?"; await meeting.chat.sendMessage({ type: "text", message }); ``` There is also a common method called `meeting.chat.sendMessage()` that can be used to send any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `sendMessage()` method accepts a parameter `message` of the following type: ```typescript async function sendMessage( message: | { type: "text"; message: string } | { type: "image"; image: File } | { type: "file"; file: File }, ) { // ... } ``` Here is how you would use the `sendMessage()` method to send a text message: ```jsx const message = "Is this just fantasy?"; await meeting.chat.sendMessage({ type: "text", message }); ``` ## Receiving Chat Messages The `meeting.chat` object emits events when new chat messages are received. You can listen for the `chatUpdate` event to log when a new chat message is received. ```js meeting.chat.on("chatUpdate", ({ message, messages }) => { console.log(`Received message ${message}`); console.log(`All messages in chat: ${messages.join(", ")}`); }); ``` Here, `message` is of type `Message`, as defined in the introduction. `messages` is a list of all chat messages in the meeting, which is the same as `meeting.chat.messages`. When a chat message is received, the `meeting.chat.messages` list is also updated. ```js console.log(JSON.stringify(meeting.chat.messages)); meeting.chat.on("chatUpdate", () => { console.log(JSON.stringify(meeting.chat.messages)); }); ``` The `meeting.chat` object emits events when new chat messages are received. You can listen for the `chatUpdate` event to log when a new chat message is received. ```jsx import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react"; // useRealtimeKitSelector hooks only works when `RealtimeKitProvider` is used. const messages = useRealtimeKitSelector((m) => m.chat.messages); ``` Alternatively: ```jsx meeting.chat.on("chatUpdate", ({ message, messages }) => { console.log(`Received message ${message}`); console.log(`All messages in chat: ${messages.join(", ")}`); }); ``` Here, `message` is of type `Message`, as defined in the introduction. `messages` is a list of all chat messages in the meeting, which is the same as `meeting.chat.messages`. When a chat message is received, the `meeting.chat.messages` list is also updated. ```jsx console.log(JSON.stringify(meeting.chat.messages)); meeting.chat.on("chatUpdate", () => { console.log(JSON.stringify(meeting.chat.messages)); }); ``` To be able to receive chat messages you need to implement a method `onChatUpdates()` method from callback `RtkChatEventListener`. You can subscribe to this events by calling `meeting.addChatEventListener(rtkChatEventListener)`. ```kotlin meeting.addChatEventListener(object : RtkChatEventListener { override fun onChatUpdates(messages: List) { // to load chat messages } override fun onNewChatMessage(message: ChatMessage) { // when a new chat message is shared in the meeting } override fun onMessageRateLimitReset() { // when the rate limit for sending messages of self is reset } }) ``` The `onChatUpdates()` method will be called whenever there is a change in the chat messages. The `messages` parameter is a list of `ChatMessage` objects that have been sent in the chat. The `onNewChatMessage()` method will be called whenever a new chat message is shared in the meeting. The `message` parameter is a `ChatMessage` object that has been sent in the chat. The `onMessageRateLimitReset()` method will be called when the rate limit for sending messages of self is reset and you can send messages again. The default rate limit is 180 messages within 60 seconds. To be able to receive chat messages you need to implement a method `onChatUpdates()` method from callback `RtkChatEventListener`. You can subscribe to this events by calling `meeting.addChatEventListener(rtkChatEventListener)`. ```swift extension MainChatListener: RtkChatEventListener { func onChatUpdates(messages: [ChatMessage]) { // to load chat messages } func onNewChatMessage(message: ChatMessage) { // when a new chat message is shared in the meeting } func onMessageRateLimitReset() { // when the rate limit for sending messages of self is reset } } ``` The `onChatUpdates()` method will be called whenever there is a change in the chat messages. The `messages` parameter is a list of `ChatMessage` objects that have been sent in the chat. The `onNewChatMessage()` method will be called whenever a new chat message is shared in the meeting. The `message` parameter is a `ChatMessage` object that has been sent in the chat. The `onMessageRateLimitReset()` method will be called when the rate limit for sending messages of self is reset and you can send messages again. The default rate limit is 180 messages within 60 seconds. To be able to receive chat messages you need to implement a method `onChatUpdates()` method from callback `RtkChatEventListener`. You can subscribe to this events by calling `meeting.addChatEventListener(rtkChatEventListener)`. ```dart class ChatEventListener extends RtkChatEventListener { @override void onChatUpdates(List messages) { messages.map((msg) { switch (msg.type) { case MessageType.text: print((msg as TextMessage).displayName); print((msg).message); // Show message/return state to show text message UI. break; case MessageType.image: print((msg as ImageMessage).displayName); print((msg).link); // Show message/return state to show image message UI. break; case MessageType.file: print((msg as FileMessage).name); print((msg).link); print((msg).size); // Show message/return state to show file message UI. break; } }); } void onNewChatMessage(ChatMessage message) { // your code to handle new chat message } } ``` In this context, `messages` refers to a list of all the chat messages in the meeting. The type of message used is `ChatMessage`, which was introduced earlier in the introduction to Chat topic. Whenever a chat message is received, the `meeting.chat.messages` list is automatically updated. The `meeting.chat` object emits events when new chat messages are received. You can listen for the `chatUpdate` event to log when a new chat message is received. ```jsx meeting.chat.on("chatUpdate", ({ message, messages }) => { console.log(`Received message ${message}`); console.log(`All messages in chat: ${messages.join(", ")}`); }); ``` Here, `message` is of type `Message`, as defined in the introduction. `messages` is a list of all chat messages in the meeting, which is the same as `meeting.chat.messages`. When a chat message is received, the `meeting.chat.messages` list is also updated. ```jsx console.log(JSON.stringify(meeting.chat.messages)); meeting.chat.on("chatUpdate", () => { console.log(JSON.stringify(meeting.chat.messages)); }); ``` ## Editing Chat Messages ### Edit a Text Message There is a method in `meeting.chat` to edit a message of each type. To edit a text message, use the `meeting.chat.editTextMessage()` method. This accepts a `messageId` (type `string`) and a `message` (type `string`). ```js const message = meeting.chat.messages[0]; const messageId = message?.id; const newMessage = "Is this the real life?"; await meeting.chat.editTextMessage(messageId, newMessage); ``` There is a method in `meeting.chat` to edit a message of each type. To edit a text message, use the `meeting.chat.editTextMessage()` method. This accepts a `messageId` (type `string`) and a `message` (type `string`). ```jsx const message = meeting.chat.messages[0]; const messageId = message?.id; const newMessage = "Is this the real life?"; await meeting.chat.editTextMessage(messageId, newMessage); ``` There is a method in `meeting.chat` to edit a message of each type. To edit a text message, use the `meeting.chat.editTextMessage()` method. This accepts a `messageId` (type `string`) and a `message` (type `string`). ```jsx const message = meeting.chat.messages[0]; const messageId = message?.id; const newMessage = "Is this the real life?"; await meeting.chat.editTextMessage(messageId, newMessage); ``` ### Edit an Image You can edit an image with the help of `meeting.chat.editImageMessage()`. This accepts a `messageId` of type `string` and an image of type `File`. ```html ``` ```js async function onEditImage() { const messageId = "..."; const image = document.getElementById("img"); await meeting.chat.editImageMessage(messageId, image.files[0]); } ``` You can edit an image with the help of `meeting.chat.editImageMessage()`. This accepts a `messageId` of type `string` and an image of type `File`. ```jsx import { useRef } from "react"; function ChatComponent() { const imageInputRef = useRef(null); const onEditImage = async () => { const messageId = "..."; const image = imageInputRef.current; if (image && image.files[0]) { await meeting.chat.editImageMessage(messageId, image.files[0]); } }; return ( <> ); } ``` You can edit an image with the help of `meeting.chat.editImageMessage()`. This accepts a `messageId` of type `string` and an image of type File. ```jsx import DocumentPicker from "@react-native-documents/picker"; async function onEditImage() { const messageId = "..."; // Get the image uri and create an object with the following fields const res = await DocumentPicker.pickSingle({ type: [DocumentPicker.types.images], }); const image = { uri: res.uri, name: res.name, size: res.size, type: res.type, }; await meeting.chat.editImageMessage(messageId, image); } ``` ### Edit a File Editing a file is similar to editing an image. To edit a file, use `meeting.chat.editFileMessage()`. ```html ``` ```js async function onEditFile() { const messageId = "..."; const file = document.getElementById("file"); await meeting.chat.editFileMessage(messageId, file.files[0]); } ``` Editing a file is similar to editing an image. To edit a file, use `meeting.chat.editFileMessage()`. ```jsx import { useRef } from "react"; function ChatComponent() { const fileInputRef = useRef(null); const onEditFile = async () => { const messageId = "..."; const file = fileInputRef.current; if (file && file.files[0]) { await meeting.chat.editFileMessage(messageId, file.files[0]); } }; return ( <> ); } ``` Editing a file is similar to editing an image. To edit a file, use `meeting.chat.editFileMessage()`. ```jsx import DocumentPicker from "@react-native-documents/picker"; async function onEditFile() { const messageId = "..."; // Get the file uri and create an object with the following fields const res = await DocumentPicker.pickSingle({ type: [DocumentPicker.types.allFiles], }); const file = { uri: res.uri, name: res.name, size: res.size, type: res.type, }; await meeting.chat.editFileMessage(messageId, file); } ``` ### Edit Any Message Type There is also a common method called `meeting.chat.editMessage()` that can be used to edit any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `editMessage()` method accepts parameters `messageId` and `message` of the following type: ```typescript async function editMessage( messageId: string, message: | { type: "text"; message: string } | { type: "image"; image: File } | { type: "file"; file: File }, ) { // ... } ``` Here is how you would use the `editMessage()` method to edit a text message: ```js const messageId = "..."; const message = "Is this just fantasy?"; await meeting.chat.editMessage(messageId, { type: "text", message }); ``` There is also a common method called `meeting.chat.editMessage()` that can be used to edit any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `editMessage()` method accepts parameters `messageId` and `message` of the following type: ```typescript async function editMessage( messageId: string, message: | { type: "text"; message: string } | { type: "image"; image: File } | { type: "file"; file: File }, ) { // ... } ``` Here is how you would use the `editMessage()` method to edit a text message: ```jsx const messageId = "..."; const message = "Is this just fantasy?"; await meeting.chat.editMessage(messageId, { type: "text", message }); ``` There is also a common method called `meeting.chat.editMessage()` that can be used to edit any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `editMessage()` method accepts parameters `messageId` and `message` of the following type: ```typescript async function editMessage( messageId: string, message: | { type: "text"; message: string } | { type: "image"; image: File } | { type: "file"; file: File }, ) { // ... } ``` Here is how you would use the `editMessage()` method to edit a text message: ```jsx const messageId = "..."; const message = "Is this just fantasy?"; await meeting.chat.editMessage(messageId, { type: "text", message }); ``` ## Other Chat Functions ### Get Messages by a User The `meeting.chat` object exposes certain other methods for convenience when working with chat. You can get messages by a particular user by passing the user's ID to the `meeting.chat.getMessagesByUser()` method. ```js // Find the userId of the user with name "Freddie". const { userId } = meeting.participants.joined .toArray() .find((p) => p.name === "Freddie"); const messages = meeting.chat.getMessagesByUser(userId); ``` The `meeting.chat` object exposes certain other methods for convenience when working with chat. You can get messages by a particular user by passing the user's ID to the `meeting.chat.getMessagesByUser()` method. ```jsx // Find the userId of the user with name "Freddie". const { userId } = meeting.participants.joined .toArray() .find((p) => p.name === "Freddie"); const messages = meeting.chat.getMessagesByUser(userId); ``` The `meeting.chat` object exposes certain other methods for convenience when working with chat. You can get messages by a particular user by passing the user's ID to the `meeting.chat.getMessagesByUser()` method. ```jsx // Find the userId of the user with name "Freddie". const { userId } = meeting.participants.joined .toArray() .find((p) => p.name === "Freddie"); const messages = meeting.chat.getMessagesByUser(userId); ``` ### Get Messages of a Particular Type You can also get messages of a particular type using the `meeting.chat.getMessagesByType()` method. For example, you can get all image messages present in the chat using the following snippet: ```js const imageMessages = meeting.chat.getMessagesByType("image"); ``` You can also get messages of a particular type using the `meeting.chat.getMessagesByType()` method. For example, you can get all image messages present in the chat using the following snippet: ```jsx const imageMessages = meeting.chat.getMessagesByType("image"); ``` You can also get messages of a particular type using the `meeting.chat.getMessagesByType()` method. For example, you can get all image messages present in the chat using the following snippet: ```jsx const imageMessages = meeting.chat.getMessagesByType("image"); ``` ### Pinning a Chat Message You can pin a number of messages to the chat. When you pin a message, the message object will have the attribute `pinned: true`, using which you can identify if a message is pinned. To pin a message: ```js // Pin the first message in the chat (could be text, image, or file). const { id } = meeting.chat.messages[0]; await meeting.chat.pin(id); ``` Once you pin a message, it will be added to `meeting.chat.pinned`. ```js const { id } = meeting.chat.messages[0]; await meeting.chat.pin(id); console.log(meeting.chat.pinned); console.log(meeting.chat.pinned.length > 0); // Should be true ``` You can also unpin a pinned message by using the `meeting.chat.unpin()` method. ```js // Unpin the first pinned message. const { id } = meeting.chat.pinned[0]; await meeting.chat.unpin(id); ``` You can listen for events to know when a message is pinned or unpinned. ```js meeting.chat.on("pinMessage", ({ message }) => { console.log("A message was pinned", JSON.stringify(message)); }); meeting.chat.on("unpinMessage", ({ message }) => { console.log("A message was unpinned", JSON.stringify(message)); }); ``` You can pin a number of messages to the chat. When you pin a message, the message object will have the attribute `pinned: true`, using which you can identify if a message is pinned. To pin a message: ```jsx // Pin the first message in the chat (could be text, image, or file). const { id } = meeting.chat.messages[0]; await meeting.chat.pin(id); ``` Once you pin a message, it will be added to `meeting.chat.pinned`. ```jsx const { id } = meeting.chat.messages[0]; await meeting.chat.pin(id); console.log(meeting.chat.pinned); console.log(meeting.chat.pinned.length > 0); // Should be true ``` You can also unpin a pinned message by using the `meeting.chat.unpin()` method. ```jsx // Unpin the first pinned message. const { id } = meeting.chat.pinned[0]; await meeting.chat.unpin(id); ``` You can listen for events to know when a message is pinned or unpinned. ```jsx meeting.chat.on("pinMessage", ({ message }) => { console.log("A message was pinned", JSON.stringify(message)); }); meeting.chat.on("unpinMessage", ({ message }) => { console.log("A message was unpinned", JSON.stringify(message)); }); ``` You can pin a number of messages to the chat. When you pin a message, the message object will have the attribute `pinned: true`, using which you can identify if a message is pinned. To pin a message: ```jsx // Pin the first message in the chat (could be text, image, or file). const { id } = meeting.chat.messages[0]; await meeting.chat.pin(id); ``` Once you pin a message, it will be added to `meeting.chat.pinned`. ```jsx const { id } = meeting.chat.messages[0]; await meeting.chat.pin(id); console.log(meeting.chat.pinned); console.log(meeting.chat.pinned.length > 0); // Should be true ``` You can also unpin a pinned message by using the `meeting.chat.unpin()` method. ```jsx // Unpin the first pinned message. const { id } = meeting.chat.pinned[0]; await meeting.chat.unpin(id); ``` You can listen for events to know when a message is pinned or unpinned. ```jsx meeting.chat.on("pinMessage", ({ message }) => { console.log("A message was pinned", JSON.stringify(message)); }); meeting.chat.on("unpinMessage", ({ message }) => { console.log("A message was unpinned", JSON.stringify(message)); }); ``` ### Deleting a Chat Message The `meeting.chat` namespace exposes a method called `deleteMessage()`. It takes a parameter `messageId` of type `string`. ```js const messageId = "..."; await meeting.chat.deleteMessage(messageId); ``` The `meeting.chat` namespace exposes a method called `deleteMessage()`. It takes a parameter `messageId` of type `string`. ```jsx const messageId = "..."; await meeting.chat.deleteMessage(messageId); ``` The `meeting.chat` namespace exposes a method called `deleteMessage()`. It takes a parameter `messageId` of type `string`. ```jsx const messageId = "..."; await meeting.chat.deleteMessage(messageId); ``` ## Export chat messages You can programmatically retrieve all chat messages of a RealtimeKit session in the following ways: * Using the Chat Replay API * Setting up webhook for the `meeting.chatSynced` event ### Get chat download URL To get the chat download URL, make an HTTP `GET` request to the [Chat Replay API endpoint](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/sessions/methods/get%5Fsession%5Fchat/). The API returns: ```json { "success": true, "data": { "chat_download_url": "string", "chat_download_url_expiry": "string" } } ``` * **`chat_download_url`** \- A URL that allows you to download the entire chat dump of a session in CSV format from AWS S3 * **`chat_download_url_expiry`** \- The expiry timestamp of the `chat_download_url`. If the URL expires, call this endpoint again to obtain a new download URL For details on the Chat Replay API endpoint, refer to the [Realtime Kit API documentation](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/sessions/methods/get%5Fsession%5Fchat/). ### Download the chat dump file You can download the chat dump file in CSV format by making an HTTP `GET` request to the `chat_download_url` obtained in the previous step. The process of downloading a file from an HTTP URL differs based on whether you are downloading on the client side or server side. #### Download on the client To download at client side: 1. Make a `GET` request to the `chat_download_url` 2. Convert the response to a blob 3. Create an invisible `` HTML element with a `download` attribute and add the blob to its `href` 4. Programmatically click on the `` element so that the browser automatically starts downloading, then remove the `` element #### Download on the server To download on the server using Node.js streams: 1. Create a writable stream for a local file 2. Make a `GET` request to `chat_download_url` 3. Get a readable stream using `res.body` and pipe to the writable stream created in the first step ### CSV chat dump format The CSV file contains all chat messages along with participant information and metadata. It includes the following column headings: * **`id`** \- Unique chat message ID * **`participantId`** \- ID of the participant who sent the message * **`sessionId`** \- The session ID from which the chat message was sent * **`meetingId`** \- The ID of the meeting to which this session belongs * **`displayName`** \- Display name of the participant who sent this message * **`pinned`** \- A boolean that indicates if the current message was pinned * **`isEdited`** \- A boolean that indicates if the current message was edited * **`payloadType`** \- An ENUM that indicates the type of payload sent in the chat message. It can be one of `TEXT_MESSAGE`, `IMAGE_MESSAGE`, `FILE_MESSAGE` * **`payload`** \- The actual payload sent in the chat message * **`createdAt`** \- Timestamp when this chat message was sent Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"WebPage","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/chat/#page","headline":"Chat · Cloudflare Realtime docs","description":"Send and receive chat messages in RealtimeKit meetings using the Core SDK.","url":"https://developers.cloudflare.com/realtime/realtimekit/core/chat/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Detect and display active speakers in RealtimeKit meetings using the Core SDK. title: Display active speakers image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Display active speakers Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/display-active-speakers/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) WebMobile ReactWeb ComponentsAngular RealtimeKit automatically detects and tracks participants who are actively speaking in a meeting. You can display either a single active speaker or multiple active speakers in your application UI, depending on your design requirements. An active speaker in RealtimeKit is a remote participant with prominent audio activity at any given moment. The SDK maintains two types of data to help you build your UI: * **Active speaker** — A single remote participant who is currently speaking most prominently. * **Active participants** — A set of remote participants with the most prominent audio activity. The SDK automatically updates these properties and subscribes to participant media as speaking activity changes. It prioritizes prominent audio activity, so a participant not currently visible in your UI can replace a visible participant if their audio becomes more active. Note The SDK tracks active speakers only when the local participant is viewing or rendering participants in ACTIVE mode (page 0). Refer to the [Remote participants](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#participant-view-modes) page to learn about participant view modes. Active speaker properties contain only remote participants. The local participant is available separately. The maximum number of participants in the `active` map is one less than the grid size configured in the local participant's [Preset](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/). This reserves space for the local participant in your UI. For example, if the grid size is 6, the `active` map contains a maximum of 5 remote participants. ## Display a single active speaker Use `lastActiveSpeaker` to show the most recently active participant in your UI. Access the current active speaker with the `useRealtimeKitSelector` hook: ```ts const activeSpeaker = useRealtimeKitSelector((meeting) => { const activeSpeakerId = meeting.participants.lastActiveSpeaker; return meeting.participants.joined.get(activeSpeakerId); }); if (activeSpeaker) { // Render the active speaker video } ``` The `useRealtimeKitSelector` hook automatically updates your component when the active speaker changes. Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK also emits an `activeSpeaker` event on `meeting.participants` when a different participant becomes the active speaker. For imperative updates or side effects, listen to this event: ```ts meeting.participants.on("activeSpeaker", ({ peerId, volume }) => { const activeSpeaker = meeting.participants.joined.get(peerId); // Update your UI or trigger side effects }); ``` Use `lastActiveSpeaker` to show the most recently active participant in your UI. Access the `lastActiveSpeaker` property to get the participant ID, then retrieve the participant object from the joined participants map: ```ts const activeSpeakerId = meeting.participants.lastActiveSpeaker; const activeSpeaker = meeting.participants.joined.get(activeSpeakerId); if (activeSpeaker) { // Render the active speaker video } ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK emits an `activeSpeaker` event on `meeting.participants` when a different participant becomes the active speaker: ```ts meeting.participants.on("activeSpeaker", ({ peerId, volume }) => { const activeSpeaker = meeting.participants.joined.get(peerId); // Update your UI to display the new active speaker }); ``` Use `lastActiveSpeaker` to show the most recently active participant in your UI. Access the `lastActiveSpeaker` property to get the participant ID, then retrieve the participant object from the joined participants map: ```ts const activeSpeakerId = meeting.participants.lastActiveSpeaker; const activeSpeaker = meeting.participants.joined.get(activeSpeakerId); if (activeSpeaker) { // Render the active speaker video } ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK emits an `activeSpeaker` event on `meeting.participants` when a different participant becomes the active speaker: ```ts meeting.participants.on("activeSpeaker", ({ peerId, volume }) => { const activeSpeaker = meeting.participants.joined.get(peerId); // Update your UI to display the new active speaker }); ``` Use `activeSpeaker` to show the most recently active participant in your UI. Access the `activeSpeaker` property to get the current active speaker: ```kotlin val activeSpeaker = meeting.participants.activeSpeaker if (activeSpeaker != null) { // Render the active speaker video } ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK emits an event when a different participant becomes the active speaker. Listen to this event using `RtkParticipantsEventListener`: ```kotlin meeting.addParticipantsEventListener(object : RtkParticipantsEventListener { override fun onActiveSpeakerChanged(participant: RtkRemoteParticipant?) { // Update your UI to display the new active speaker } }) ``` Use `activeSpeaker` to show the most recently active participant in your UI. Access the `activeSpeaker` property to get the current active speaker: ```swift let activeSpeaker = meeting.participants.activeSpeaker if let activeSpeaker = activeSpeaker { // Render the active speaker video } ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK emits an event when a different participant becomes the active speaker. Listen to this event by implementing `RtkParticipantsEventListener`: ```swift extension MeetingViewModel: RtkParticipantsEventListener { func onActiveSpeakerChanged(participant: RtkRemoteParticipant?) { // Update your UI to display the new active speaker } } meeting.addParticipantsEventListener(self) ``` Use `activeSpeaker` to show the most recently active participant in your UI. The Flutter SDK tracks active speakers through the `onActiveSpeakerChanged` event listener: ```dart class ParticipantsNotifier extends RtkParticipantsEventListener { @override void onActiveSpeakerChanged(RtkRemoteParticipant? participant) { if (participant != null) { // Update your UI to display the new active speaker } } } meeting.addParticipantsEventListener(ParticipantsNotifier()); ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. Use `lastActiveSpeaker` to show the most recently active participant in your UI. Access the current active speaker with the `useRealtimeKitSelector` hook: ```tsx const activeSpeaker = useRealtimeKitSelector((meeting) => { const activeSpeakerId = meeting.participants.lastActiveSpeaker; return meeting.participants.joined.get(activeSpeakerId); }); if (activeSpeaker) { // Render the active speaker video } ``` The `useRealtimeKitSelector` hook automatically updates your component when the active speaker changes. Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK also emits an `activeSpeaker` event on `meeting.participants` when a different participant becomes the active speaker. For imperative updates or side effects, listen to this event: ```tsx meeting.participants.on("activeSpeaker", (participant) => { // Update your UI or trigger side effects }); ``` ## Display multiple active speakers Use the `active` map to show multiple participants with prominent audio activity, typically in a grid layout. Access the current active participants with the `useRealtimeKitSelector` hook: ```ts const activeMap = useRealtimeKitSelector( (meeting) => meeting.participants.active, ); const activeParticipants = activeMap.toArray(); // Render active participants in your grid activeParticipants.forEach((participant) => { // Render participant video tile }); ``` The `useRealtimeKitSelector` hook automatically updates your component when the set of active speakers changes. Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK also emits a `participantsUpdate` event on the `active` map when the set of active speakers changes. For imperative updates or side effects when the `active` map changes, listen to this event: ```ts meeting.participants.active.on("participantsUpdate", () => { const activeParticipants = meeting.participants.active.toArray(); // Perform side effects }); ``` (Optional) If your application needs to respond when a specific participant is added to or removed from the active map, listen for `participantJoined` and `participantLeft` on `meeting.participants.active` map. ```ts meeting.participants.active.on("participantJoined", (participant) => { console.log("Participant added to active map:", participant.id); }); meeting.participants.active.on("participantLeft", (participant) => { console.log("Participant removed from active map:", participant.id); }); ``` Use the `active` map to show multiple participants with prominent audio activity, typically in a grid layout. ```ts const activeParticipants = meeting.participants.active.toArray(); // Render active participants in your grid activeParticipants.forEach((participant) => { // Render participant video tile }); ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK emits a `participantsUpdate` event on the `active` map when the set of active speakers changes. Listen to this event, retrieve the updated array, and re-render your grid: ```ts meeting.participants.active.on("participantsUpdate", () => { const activeParticipants = meeting.participants.active.toArray(); // Update your grid UI with the new active participants }); ``` (Optional) If your application needs to respond when a specific participant is added to or removed from the active map, listen for `participantJoined` and `participantLeft` on `meeting.participants.active` map. ```ts meeting.participants.active.on("participantJoined", (participant) => { console.log("Participant added to active map:", participant.id); }); meeting.participants.active.on("participantLeft", (participant) => { console.log("Participant removed from active map:", participant.id); }); ``` Use the `active` map to show multiple participants with prominent audio activity, typically in a grid layout. ```ts const activeParticipants = meeting.participants.active.toArray(); // Render active participants in your grid activeParticipants.forEach((participant) => { // Render participant video tile }); ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK emits a `participantsUpdate` event on the `active` map when the set of active speakers changes. Listen to this event, retrieve the updated array, and re-render your grid: ```ts meeting.participants.active.on("participantsUpdate", () => { const activeParticipants = meeting.participants.active.toArray(); // Update your grid UI with the new active participants }); ``` (Optional) If your application needs to respond when a specific participant is added to or removed from the active map, listen for `participantJoined` and `participantLeft` on `meeting.participants.active` map. ```ts meeting.participants.active.on("participantJoined", (participant) => { console.log("Participant added to active map:", participant.id); }); meeting.participants.active.on("participantLeft", (participant) => { console.log("Participant removed from active map:", participant.id); }); ``` Use the `active` list to show multiple participants with prominent audio activity, in a grid layout: ```kotlin val activeParticipants = meeting.participants.active // Render active participants in your grid activeParticipants.forEach { participant -> // Render participant video tile } ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK emits an event when the set of active speakers changes. Listen to this event using `RtkParticipantsEventListener`: ```kotlin meeting.addParticipantsEventListener(object : RtkParticipantsEventListener { override fun onActiveParticipantsChanged(active: List) { // Update your grid UI with the new active participants } }) ``` Use the `active` list to show multiple participants with prominent audio activity, typically in a grid layout: ```swift let activeParticipants = meeting.participants.active // Render active participants in your grid for participant in activeParticipants { // Render participant video tile } ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK emits an event when the set of active speakers changes. Listen to this event by implementing `RtkParticipantsEventListener`: ```swift extension MeetingViewModel: RtkParticipantsEventListener { func onActiveParticipantsChanged(active: [RtkRemoteParticipant]) { // Update your grid UI with the new active participants } } meeting.addParticipantsEventListener(self) ``` Use the `active` list to show multiple participants with prominent audio activity, typically in a grid layout: ```dart final activeParticipants = meeting.participants.active; // Render active participants in your grid for (var participant in activeParticipants) { // Render participant video tile } ``` Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK emits an event when the set of active speakers changes. Listen to this event using `RtkParticipantsEventListener`: ```dart class ParticipantsNotifier extends RtkParticipantsEventListener { @override void onActiveParticipantsChanged(List active) { // Update your grid UI with the new active participants } } meeting.addParticipantsEventListener(ParticipantsNotifier()); ``` Use the `active` map to show multiple participants with prominent audio activity, typically in a grid layout. Access the current active participants with the `useRealtimeKitSelector` hook: ```tsx const activeMap = useRealtimeKitSelector( (meeting) => meeting.participants.active, ); const activeParticipants = activeMap.toArray(); // Render active participants in your grid activeParticipants.forEach((participant) => { // Render participant video tile }); ``` The `useRealtimeKitSelector` hook automatically updates your component when the set of active speakers changes. Refer to [Display participant videos](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/#display-participant-videos) to learn how to render the participant video in your UI. The SDK also emits a `participantsUpdate` event on the `active` map when the set of active speakers changes. For imperative updates or side effects when the `active` map changes, listen to this event: ```tsx meeting.participants.active.on("participantsUpdate", () => { const activeParticipants = meeting.participants.active.toArray(); // Perform side effects }); ``` (Optional) If your application needs to respond when a specific participant is added to or removed from the active map, listen for `participantJoined` and `participantLeft` on `meeting.participants.active` map: ```tsx meeting.participants.active.on("participantJoined", (participant) => { console.log("Participant added to active map:", participant.id); }); meeting.participants.active.on("participantLeft", (participant) => { console.log("Participant removed from active map:", participant.id); }); ``` ## Visualize audio activity You can create custom audio visualizations using audio data from a participant's audio track. Extract volume information from the audio track to calculate amplitude series. Use this data to render waveforms, speech indicators, or audio level meters in your UI. You can create custom audio visualizations using audio data from a participant's audio track. Extract volume information from the audio track to calculate amplitude series. Use this data to render waveforms, speech indicators, or audio level meters in your UI. You can create custom audio visualizations using audio data from a participant's audio track. Extract volume information from the audio track to calculate amplitude series. Use this data to render waveforms, speech indicators, or audio level meters in your UI. Audio activity visualization is not supported on Android. Audio activity visualization is not supported on iOS. Audio activity visualization is not supported on Flutter. Audio activity visualization is not supported on React Native. ## Related resources * [Meeting object explained](https://developers.cloudflare.com/realtime/realtimekit/core/meeting-object-explained/) \- Understand the meeting object structure and available properties. * [Remote participant](https://developers.cloudflare.com/realtime/realtimekit/core/remote-participants/) \- Learn more about remote participants in a session and how to display their video. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/display-active-speakers/#page","headline":"Display active speakers · Cloudflare Realtime docs","description":"Detect and display active speakers in RealtimeKit meetings using the Core SDK.","url":"https://developers.cloudflare.com/realtime/realtimekit/core/display-active-speakers/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: End a RealtimeKit session for all participants and stop active recordings. title: End a session image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # End a session Last updated Apr 21, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/end-a-session/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) Prerequisites Ensure your participant's preset has the **Kick Participants** (`kick_participant`) host permission enabled. To end the current [session](https://developers.cloudflare.com/realtime/realtimekit/concepts/meeting/#session/) for all participants, remove all participants using `kickAll()`. This stops any ongoing recording for that session and sets the session status to `ENDED`. Ending a session is different from leaving a meeting. Leaving disconnects only the current participant. The session remains active if other participants are still present. ## Steps WebMobile ReactWeb ComponentsAngular 1. Check that the local participant has permission to remove participants. ```ts const canEndSession = meeting.self.permissions.kickParticipant === true; if (!canEndSession) { // Disable the "End meeting/session" control in your UI. // You can also show a message to explain why the action is not available. } ``` ```ts const canEndSession = meeting.self.permissions.kickParticipant === true; if (!canEndSession) { // Disable the "End meeting/session" control in your UI. // You can also show a message to explain why the action is not available. } ``` ```ts const canEndSession = meeting.self.permissions.kickParticipant === true; if (!canEndSession) { // Disable the "End meeting/session" control in your UI. // You can also show a message to explain why the action is not available. } ``` ```kotlin val canEndSession = meeting.localUser.permissions.host.canKickParticipant if (!canEndSession) { // Disable the "End meeting/session" control in your UI. // You can also show a message to explain why the action is not available. } ``` ```swift let canEndSession = meeting.localUser.permissions.host.canKickParticipant if !canEndSession { // Disable the "End meeting/session" control in your UI. // You can also show a message to explain why the action is not available. } ``` ```dart final canEndSession = meeting.localUser.permissions.host.canKickParticipant; if (!canEndSession) { // Disable the "End meeting/session" control in your UI. // You can also show a message to explain why the action is not available. } ``` ```javascript const canEndSession = meeting.self.permissions.kickParticipant === true; if (!canEndSession) { // Disable the "End meeting/session" control in your UI. // You can also show a message to explain why the action is not available. } ``` 2. End the session by removing all participants. If the participant does not have the required permission, `kickAll()` throws a ClientError with error code `1201`. ```ts try { await meeting.participants.kickAll(); } catch (err) { if (err?.code === 1201) { // The participant does not have permission to end the session. // Update your UI to indicate that the action is not allowed. return; } throw err; } ``` If the participant does not have the required permission, `kickAll()` throws a ClientError with error code `1201`. ```ts try { await meeting.participants.kickAll(); } catch (err) { if (err?.code === 1201) { // The participant does not have permission to end the session. // Update your UI to indicate that the action is not allowed. return; } throw err; } ``` If the participant does not have the required permission, `kickAll()` throws a ClientError with error code `1201`. ```ts try { await meeting.participants.kickAll(); } catch (err) { if (err?.code === 1201) { // The participant does not have permission to end the session. // Update your UI to indicate that the action is not allowed. return; } throw err; } ``` If the participant does not have the required permission, `kickAll()` returns a `HostError`. ```kotlin val error: HostError? = meeting.participants.kickAll() if (error != null) { when (error) { is HostError.KickPermissionDenied -> { // The participant does not have permission to end the session. // Update your UI to indicate that the action is not allowed. } } } else { // Successfully initiated session end } ``` If the participant does not have the required permission, `kickAll()` returns a `HostError`. ```swift let error: HostError? = meeting.participants.kickAll() if let error = error { switch error { case .kickPermissionDenied: // The participant does not have permission to end the session. // Update your UI to indicate that the action is not allowed. break default: break } } else { // Successfully initiated session end } ``` ```dart meeting.participants.kickAll(onResult: (error) { if (error != null) { // The participant does not have permission to end the session. // Update your UI to indicate that the action is not allowed. } else { // Successfully initiated session end } }); ``` If the participant does not have the required permission, `kickAll()` throws a ClientError with error code `1201`. ```javascript try { await meeting.participants.kickAll(); } catch (err) { if (err?.code === 1201) { // The participant does not have permission to end the session. // Update your UI to indicate that the action is not allowed. return; } throw err; } ``` 3. Listen for the session end event. When the session ends, all participants leave the session. The SDK emits a `roomLeft` event with `state` set to `ended`. ```ts meeting.self.on("roomLeft", ({ state }) => { if (state === "ended") { // Update your UI to show that the meeting session has ended. } }); ``` When the session ends, all participants leave the session. The SDK emits a `roomLeft` event with `state` set to `ended`. ```ts meeting.self.on("roomLeft", ({ state }) => { if (state === "ended") { // Update your UI to show that the meeting session has ended. } }); ``` When the session ends, all participants leave the session. The SDK emits a `roomLeft` event with `state` set to `ended`. ```ts meeting.self.on("roomLeft", ({ state }) => { if (state === "ended") { // Update your UI to show that the meeting session has ended. } }); ``` When the session ends, all participants leave the session. You can subscribe to the event listeners to handle the session end. ```kotlin meeting.addMeetingRoomEventListener(object : RtkMeetingRoomEventListener { override fun onMeetingEnded() { // Update your UI to show that the meeting session has ended. } }) ``` When the session ends, all participants leave the session. You can subscribe to the event listeners to handle the session end. ```swift // Implement the delegate method extension MeetingViewModel: RtkMeetingRoomEventListener { func onMeetingEnded() { // Update your UI to show that the meeting session has ended. } } meeting.addMeetingRoomEventListener(self) ``` When the session ends, all participants leave the session. You can subscribe to the event listeners to handle the session end. ```dart class MeetingRoomListener extends RtkMeetingRoomEventListener { @override void onMeetingEnded() { // Update your UI to show that the meeting session has ended. } } // Add the listener meeting.addMeetingRoomEventListener(MeetingRoomListener()); ``` When the session ends, all participants leave the session. The SDK emits a `roomLeft` event with `state` set to `ended`. ```javascript meeting.self.on("roomLeft", ({ state }) => { if (state === "ended") { // Update your UI to show that the meeting session has ended. } }); ``` You can also end a session from your backend by removing all participants using the [Kick all participants](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/active-session/methods/kick%5Fall%5Fparticipants/) API. ## End a session from your backend ### Remove all participants with the API Use the [Kick all participants](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/active-session/methods/kick%5Fall%5Fparticipants/) API method to remove all participants from an active session for a meeting. Required API token permissions At least one of the following [token permissions](https://developers.cloudflare.com/fundamentals/api/reference/permissions/) is required: * `Realtime Admin` * `Realtime` ```bash curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/meetings/$MEETING_ID/active-session/kick-all" \ --request POST \ --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" ``` ### Listen for session end events with webhooks Register a webhook that subscribes to `meeting.ended`. RealtimeKit sends this event when the session ends. You can use it to trigger backend workflows, such as sending a notification, generating a report, or updating session records in your database. Required API token permissions At least one of the following [token permissions](https://developers.cloudflare.com/fundamentals/api/reference/permissions/) is required: * `Realtime Admin` * `Realtime` ```bash curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/webhooks" \ --request POST \ --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ --json '{ "name": "Session ended webhook", "url": "", "events": [ "meeting.ended" ] }' ``` ## Disable a meeting Ending a session does not disable the meeting. Participants can join the meeting again and start a new session. To prevent participants from joining again and starting a new session, set the meeting status to `INACTIVE` using the [Update a meeting](https://developers.cloudflare.com/api/resources/realtime%5Fkit/subresources/meetings/methods/update%5Fmeeting%5Fby%5Fid/) API. Required API token permissions At least one of the following [token permissions](https://developers.cloudflare.com/fundamentals/api/reference/permissions/) is required: * `Realtime Admin` * `Realtime` ```bash curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/realtime/kit/$APP_ID/meetings/$MEETING_ID" \ --request PATCH \ --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ --json '{ "status": "INACTIVE" }' ``` ## Next steps * Review how presets control permissions in [Preset](https://developers.cloudflare.com/realtime/realtimekit/concepts/preset/). * Review the possible values of the local participant room state in [Local Participant](https://developers.cloudflare.com/realtime/realtimekit/core/local-participant/#state-properties/). Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/end-a-session/#page","headline":"End a session · Cloudflare Realtime docs","description":"End a RealtimeKit session for all participants and stop active recordings.","url":"https://developers.cloudflare.com/realtime/realtimekit/core/end-a-session/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: RealtimeKit error codes for identifying and troubleshooting SDK and API issues. title: Error Codes image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Error Codes Last updated May 28, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/error-codes/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) This page describes RealtimeKit error codes to help you identify and troubleshoot issues. WebMobile ReactWeb ComponentsAngular All Web Frameworks (Web Components, React, Angular) share the same error codes. ## RealtimeKitClient #### Error code: 0001 * **Error message**: Failed to initialize * **Possible reason**: RealtimeKitClient is not getting initialized. * **Possible solution**: Verify if you initialized the RealtimeKitClient correctly `await RealtimeKitClient.init({ ... })`. See [RealtimeKitClient ↗](https://docs.realtime.cloudflare.com/web-core/reference/RealtimeKitClient). If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 0002 * **Error message**: Failed to join room * **Possible reason**: Indicates a problem with the RealtimeKitClient. * **Possible solution**: The `join()` method is used to join a meeting room in RealtimeKit. Try calling this method again on the RealtimeKitClient: ```javascript await meeting.join(); ``` Once the join room process completes, you'll see the `roomJoined` event is emitted on the `meeting.self` namespace. #### Error code: 0003 * **Error message**: Failed to leave room * **Possible reason**: Indicates a problem with the RealtimeKitClient. * **Possible solution**: The `leave()` method is used to leave a meeting room in RealtimeKit. Try calling this method again on the RealtimeKitClient: ```javascript await meeting.leave(); ``` #### Error code: 0004 * **Error message**: Invalid auth token * **Possible reason**: Indicates a problem with the passed participant auth token. * **Possible solution**: Ensure that the passed auth token is a valid JWT auth token that is not expired yet. #### Error code: 0010 * **Error message**: Browser not supported * **Possible reason**: Browser is too old and does not support WebRTC * **Possible solution**: Upgrade browser to the latest version. Google Chrome is preferred. #### Error code: 0011 * **Error message**: HTTP Network Error * **Possible reason**: Either internet issues are present or the API requests are failing due to a faulty auth token among other cases * **Possible solution**: Ensure that the internet connection is proper. Speed test can be performed at [fast.com ↗](https://fast.com/). Make sure that the meeting is active and the token has not expired. #### Error code: 0012 * **Error message**: Websocket Network Error * **Possible reason**: Either internet issues are present or the Websocket connection failed due to a faulty auth token among other cases * **Possible solution**: Ensure that the internet connection is proper. Speed test can be performed at [fast.com ↗](https://fast.com/). Make sure that the meeting is active and the token has not expired. #### Error code: 0013 * **Error message**: Rate Limited * **Possible reason**: SDK API that you are calling is being called too often * **Possible solution**: API rate limiting generally occurs when the webpage is making an unusually high number of requests within a short period. To resolve this, analyze your code to determine why so many requests are being sent and implement optimizations to reduce unnecessary calls. #### Error code: 0014 * **Error message**: Media connection failed * **Possible reason**: ICE (Interactive Connectivity Establishment) connection failed * **Possible solution**: Make sure your network allows WebRTC connections and turn.cloudflare.com & stun.cloudflare.com are accessible. ## Controller #### Error code: 0100 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Controller module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 0101 * **Error message**: Permission denied * **Possible reason**: The participant does not have the required permissions to perform the action. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 0102 * **Error message**: Prerequisite module missing * **Possible reason**: A required module such as `self` for the Controller is missing or not initialized. * **Possible solution**: Ensure all prerequisite modules are properly passed in RealtimeKitClient initialization. ## RoomNodeClient #### Error code: 0200 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the RoomNodeClient module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. ## HiveNodeClient #### Error code: 0300 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the HiveNodeClient module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. ## SocketService #### Error code: 0400 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the SocketService module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 0404 * **Error message**: Missing prerequisites to establish a websocket connection * **Possible reason**: Required prerequisites for establishing a WebSocket connection are missing. * **Possible solution**: Ensure all prerequisites (e.g., JWT auth token with meetingId, network connectivity) are met. This is an extremely rare case and usually indicates an issue in RealtimeKit SDK. ## Chat #### Error code: 0500 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Chat module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 0501 * **Error message**: Permission denied * **Possible reason**: The participant does not have the required permissions to perform the action. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 0502 * **Error message**: Invalid message body * **Possible reason**: The message body does not conform to the expected format. * **Possible solution**: The `Message` type is not defined correctly. See [Chat ↗](https://docs.realtime.cloudflare.com/web-core/chat/introduction). #### Error code: 0503 * **Error message**: Text message is too large * **Possible reason**: The message exceeds the allowed character limit. * **Possible solution**: Reduce the message length and try again. #### Error code: 0504 * **Error message**: Message not found by the given ID * **Possible reason**: The message ID provided does not correspond to an existing message. * **Possible solution**: Verify the message ID and ensure the message exists before querying. #### Error code: 0505 * **Error message**: Action not permitted without joining room * **Possible reason**: The participant attempted to perform an action that requires them to join the meeting first. * **Possible solution**: The participant must first join the meeting using `meeting.join()` before attempting the action again. #### Error code: 0506 * **Error message**: Message search is disabled * **Possible reason**: The message search feature is turned off. * **Possible solution**: Please reach out to Cloudflare support for assistance. #### Error code: 0510 * **Error message**: Invalid channel name * **Possible reason**: The channel name provided does not meet the required format or does not exist. * **Possible solution**: Ensure the channel name is correctly formatted and exists in the system. ## Plugin #### Error code: 0600 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Plugin module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 0601 * **Error message**: Permission denied * **Possible reason**: The participant does not have the required permissions to perform the action. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 0602 * **Error message**: Plugin not found * **Possible reason**: The specified plugin does not exist or is not available. * **Possible solution**: Verify the plugin ID and ensure the plugin is enabled for your meeting. #### Error code: 0603 * **Error message**: Action not permitted without joining room * **Possible reason**: The participant attempted to perform an action that requires them to join the meeting first. * **Possible solution**: The participant must first join the meeting using `meeting.join()` before attempting the action again. ## Polls #### Error code: 0700 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Polls module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 0705 * **Error message**: Action not permitted without joining room * **Possible reason**: The participant attempted to perform an action that requires them to join the meeting first. * **Possible solution**: The participant must first join the meeting using `meeting.join()` before attempting the action again. ## Meta #### Error code: 0800 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Meta module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 0801 * **Error message**: Permission denied * **Possible reason**: The participant does not have the required permissions to perform the action. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. ## Preset #### Error code: 0900 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Preset module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 0904 * **Error message**: Invalid preset configuration * **Possible reason**: The preset configuration contains invalid or incompatible settings. * **Possible solution**: Review your preset configuration and ensure all settings are valid. ## Recording #### Error code: 1000 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Recording module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 1001 * **Error message**: Permission denied * **Possible reason**: The participant does not have the required permissions to perform the action. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 1004 * **Error message**: Recording not started * **Possible reason**: Attempted to stop or interact with a recording that hasn't been started. * **Possible solution**: Start a recording before attempting to stop or interact with it. #### Error code: 1005 * **Error message**: Recording already in progress * **Possible reason**: Attempted to start a recording when one is already active. * **Possible solution**: Stop the current recording before starting a new one. ## Self #### Error code: 1100 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Self module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 1101 * **Error message**: Permission denied * **Possible reason**: The participant does not have the required permissions to perform the action. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 1102 * **Error message**: Device not found * **Possible reason**: The specified media device (microphone/camera) is not available. * **Possible solution**: Ensure the device is connected and browser permissions are granted. #### Error code: 1103 * **Error message**: Failed to access media device * **Possible reason**: Browser permissions were denied or device is in use by another application. * **Possible solution**: Grant browser permissions and ensure the device is not being used by another application. #### Error code: 1104 * **Error message**: Invalid device configuration * **Possible reason**: The device configuration provided is invalid or incompatible. * **Possible solution**: Verify the device configuration and ensure it meets the required specifications. #### Error code: 1105 * **Error message**: Screen share not supported * **Possible reason**: Browser does not support screen sharing or permissions were denied. * **Possible solution**: Use a supported browser (Chrome, Edge, Firefox) and grant screen share permissions. #### Error code: 1106 * **Error message**: Action not permitted without joining room * **Possible reason**: The participant attempted to perform an action that requires them to join the meeting first. * **Possible solution**: The participant must first join the meeting using `meeting.join()` before attempting the action again. ## Participant #### Error code: 1200 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Participant module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 1201 * **Error message**: Permission denied * **Possible reason**: The participant does not have the required permissions to perform the action. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 1202 * **Error message**: Participant not found * **Possible reason**: The specified participant ID does not exist in the meeting. * **Possible solution**: Verify the participant ID and ensure they have joined the meeting. #### Error code: 1203 * **Error message**: Cannot perform action on self * **Possible reason**: Attempted to perform an action on yourself that should target other participants. * **Possible solution**: Use the appropriate `meeting.self` methods for self-actions. #### Error code: 1204 * **Error message**: Invalid participant ID * **Possible reason**: The participant ID format is invalid. * **Possible solution**: Ensure you're using a valid participant or peer ID. #### Error code: 1205 * **Error message**: Participant already exists * **Possible reason**: Attempted to add a participant that is already in the meeting. * **Possible solution**: Check if the participant has already joined before attempting to add them. #### Error code: 1206 * **Error message**: Max participants limit reached * **Possible reason**: The meeting has reached its maximum participant capacity. * **Possible solution**: Wait for participants to leave or upgrade your plan for higher limits. #### Error code: 1207 * **Error message**: Participant is in waiting room * **Possible reason**: Attempted to perform an action on a participant who is still in the waiting room. * **Possible solution**: Admit the participant from the waiting room first. #### Error code: 1208 * **Error message**: Action not permitted without joining room * **Possible reason**: The participant attempted to perform an action that requires them to join the meeting first. * **Possible solution**: The participant must first join the meeting using `meeting.join()` before attempting the action again. #### Error code: 1209 * **Error message**: Participant has been removed * **Possible reason**: Attempted to interact with a participant who has been removed from the meeting. * **Possible solution**: Verify participant status before performing actions. ## Spotlight #### Error code: 1300 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Spotlight module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. ## Webinar #### Error code: 1500 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Webinar module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. ## LocalMediaHandler #### Error code: 1601 * **Error message**: Media constraints not supported * **Possible reason**: The media constraints provided are not supported by the browser or device. * **Possible solution**: Adjust media constraints to supported values. #### Error code: 1602 * **Error message**: Failed to get user media * **Possible reason**: Browser could not access camera/microphone due to permissions or hardware issues. * **Possible solution**: Grant browser permissions and ensure devices are properly connected. #### Error code: 1603 * **Error message**: No audio device found * **Possible reason**: No microphone is available or connected. * **Possible solution**: Connect a microphone and ensure it's recognized by the system. #### Error code: 1604 * **Error message**: No video device found * **Possible reason**: No camera is available or connected. * **Possible solution**: Connect a camera and ensure it's recognized by the system. #### Error code: 1605 * **Error message**: Device in use * **Possible reason**: The requested device is already in use by another application. * **Possible solution**: Close other applications using the device and try again. #### Error code: 1606 * **Error message**: Device disconnected * **Possible reason**: The media device was disconnected during the session. * **Possible solution**: Reconnect the device and refresh the connection. #### Error code: 1607 * **Error message**: Track ended unexpectedly * **Possible reason**: The media track ended due to device disconnection or system error. * **Possible solution**: Restart the media track or reconnect the device. #### Error code: 1608 * **Error message**: Failed to switch device * **Possible reason**: Error occurred while switching between media devices. * **Possible solution**: Verify the new device is available and try again. #### Error code: 1609 * **Error message**: Screen share permission denied * **Possible reason**: User denied screen share permission in the browser. * **Possible solution**: Grant screen share permission when prompted. #### Error code: 1610 * **Error message**: Screen share canceled * **Possible reason**: User canceled the screen share selection. * **Possible solution**: Retry screen sharing and select a window/screen to share. #### Error code: 1611 * **Error message**: Invalid media track * **Possible reason**: The provided media track is invalid or has ended. * **Possible solution**: Ensure the media track is active before using it. ## End-to-End Encryption #### Error code: 1701 * **Error message**: E2EE not supported * **Possible reason**: End-to-end encryption is not supported in the current browser or configuration. * **Possible solution**: Use a browser that supports E2EE or check your configuration. ## AI #### Error code: 1800 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the AI module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 1801 * **Error message**: AI feature not enabled * **Possible reason**: The AI feature you're trying to use is not enabled in your preset. * **Possible solution**: Enable the AI feature in your preset configuration. ## Livestream #### Error code: 1900 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Livestream module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 1901 * **Error message**: Livestream not started * **Possible reason**: Attempted to stop or interact with a livestream that hasn't been started. * **Possible solution**: Start a livestream before attempting to stop or interact with it. #### Error code: 1902 * **Error message**: Livestream already in progress * **Possible reason**: Attempted to start a livestream when one is already active. * **Possible solution**: Stop the current livestream before starting a new one. ## Stage #### Error code: 2000 * **Error message**: Internal exception * **Possible reason**: An unexpected error occurred within the Stage module. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 2001 * **Error message**: Permission denied * **Possible reason**: The participant does not have the required permissions to perform the action. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 2002 * **Error message**: Participant not on stage * **Possible reason**: Attempted to perform a stage action on a participant who is not on stage. * **Possible solution**: Ensure the participant is on stage before performing stage-specific actions. #### Error code: 2003 * **Error message**: Stage is full * **Possible reason**: The stage has reached its maximum participant capacity. * **Possible solution**: Remove participants from stage or upgrade your plan for higher limits. #### Error code: 2004 * **Error message**: Participant already on stage * **Possible reason**: Attempted to add a participant to stage who is already on stage. * **Possible solution**: Check stage status before attempting to add participants. #### Error code: 2005 * **Error message**: Invalid stage request * **Possible reason**: The stage request contains invalid parameters or configuration. * **Possible solution**: Review the stage request parameters and ensure they are valid. #### Error code: 2006 * **Error message**: Stage feature not enabled * **Possible reason**: The stage feature is not enabled in your preset or plan. * **Possible solution**: Enable the stage feature in your preset configuration. ## General #### Error code: 9900 * **Error message**: Unknown error * **Possible reason**: An unexpected error occurred that doesn't fall into other categories. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. Note React Native shares the same error codes as Web Frameworks. Refer to the Web Frameworks tab for the complete list of error codes and solutions. All mobile platforms (iOS, Android, Flutter) share the same error codes. All fallible APIs in RealtimeKit mobile SDKs return an operation-specific error type that implements the `RtkError` interface with `code: Int` (or `ErrorCode` for iOS) and `message: String` fields. ## Meeting #### Error code: 1000 * **Error message**: Invalid auth token * **Possible reason**: The authentication token provided is invalid or malformed. * **Possible solution**: Ensure that the passed auth token is a valid JWT auth token that is not expired yet. #### Error code: 1001 * **Error message**: Failed to initialize meeting * **Possible reason**: Meeting initialization encountered an error. * **Possible solution**: Check your initialization parameters and network connectivity. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 1002 * **Error message**: Invalid base URL * **Possible reason**: The base URL provided is invalid or inaccessible. * **Possible solution**: Verify the base URL configuration in your initialization code. #### Error code: 1003 * **Error message**: Failed to join room * **Possible reason**: Indicates a problem joining the meeting room. * **Possible solution**: Ensure the meeting is active and the token has not expired. Check network connectivity and try again. #### Error code: 1004 * **Error message**: Unauthorised participant * **Possible reason**: The participant does not have authorization to join the meeting. * **Possible solution**: Verify the participant's auth token and ensure they have the required permissions. #### Error code: 1005 * **Error message**: Meeting is in the `INACTIVE` state * **Possible reason**: The meeting is not currently active. * **Possible solution**: Ensure the meeting has been started before attempting to join. #### Error code: 1006 * **Error message**: Unknown error * **Possible reason**: An unexpected error occurred. * **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. ## Audio #### Error code: 2100 * **Error message**: No permission to share audio in the meeting * **Possible reason**: The participant does not have permission to enable audio. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 2101 * **Error message**: Microphone permission denied by the OS * **Possible reason**: The operating system has denied microphone access. * **Possible solution**: Grant microphone permissions in your device settings and restart the application. #### Error code: 2102 * **Error message**: Transient error in enabling microphone access * **Possible reason**: A temporary error occurred while enabling the microphone. * **Possible solution**: Try enabling the microphone again. If you continue to experience issues, please reach out to Cloudflare support. ## Video #### Error code: 2200 * **Error message**: No permission to share camera in the meeting * **Possible reason**: The participant does not have permission to enable video. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 2201 * **Error message**: Camera permission denied by the OS * **Possible reason**: The operating system has denied camera access. * **Possible solution**: Grant camera permissions in your device settings and restart the application. #### Error code: 2202 * **Error message**: Transient error in enabling camera access * **Possible reason**: A temporary error occurred while enabling the camera. * **Possible solution**: Try enabling the camera again. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 2203 * **Error message**: Cannot share video in this meeting type * **Possible reason**: Video sharing is not allowed for this meeting type. * **Possible solution**: Check the meeting configuration and preset settings. ## Screen Share #### Error code: 2300 * **Error message**: No permission to enable screen share in the meeting * **Possible reason**: The participant does not have permission to share their screen. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 2301 * **Error message**: Screen share permission denied by the OS * **Possible reason**: The operating system has denied screen share access. * **Possible solution**: Grant screen share permissions in your device settings and restart the application. #### Error code: 2302 * **Error message**: Screen share operation failed * **Possible reason**: An error occurred during screen sharing. * **Possible solution**: Try starting screen share again. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 2303 * **Error message**: Maximum limit of screen shares has been reached * **Possible reason**: The meeting has reached the maximum number of concurrent screen shares. * **Possible solution**: Wait for other participants to stop sharing or upgrade your plan for higher limits. #### Error code: 2304 * **Error message**: Cannot screen share in this meeting type * **Possible reason**: Screen sharing is not allowed for this meeting type. * **Possible solution**: Check the meeting configuration and preset settings. ## Participant Control #### Error code: 3000 * **Error message**: Pin permission denied to host * **Possible reason**: The participant does not have permission to pin other participants. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 3002 * **Error message**: Mute video permission denied to host * **Possible reason**: The participant does not have permission to mute other participants' video. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 3003 * **Error message**: Mute audio permission denied to host * **Possible reason**: The participant does not have permission to mute other participants' audio. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 3006 * **Error message**: Kick permission denied to host * **Possible reason**: The participant does not have permission to remove other participants. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. ## Chat #### Error code: 4000 * **Error message**: No permission to send chat messages in the meeting * **Possible reason**: The participant does not have permission to send chat messages. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 4001 * **Error message**: Host tried to apply an invalid value to the chat configuration * **Possible reason**: The chat configuration contains invalid settings. * **Possible solution**: Review the chat configuration and ensure all settings are valid. #### Error code: 4002 * **Error message**: Rate limit breached for chat messages * **Possible reason**: Too many chat messages were sent in a short period. * **Possible solution**: Reduce the frequency of chat messages and try again. #### Error code: 4101 * **Error message**: Chat message cannot be blank * **Possible reason**: An empty message was sent. * **Possible solution**: Ensure the message contains text before sending. #### Error code: 4102 * **Error message**: Chat message exceeded character limit * **Possible reason**: The message is too long. * **Possible solution**: Reduce the message length and try again. #### Error code: 4201 * **Error message**: File format cannot be sent in chat * **Possible reason**: The file type is not supported. * **Possible solution**: Check the supported file formats and convert the file if necessary. #### Error code: 4202 * **Error message**: Failed to read the file to send in chat * **Possible reason**: The file could not be read from the device. * **Possible solution**: Ensure the file exists and is accessible, then try again. #### Error code: 4203 * **Error message**: Failed to upload the file to chat * **Possible reason**: File upload encountered an error. * **Possible solution**: Check network connectivity and try uploading the file again. ## Polls #### Error code: 5000 * **Error message**: No permission to create polls in the meeting * **Possible reason**: The participant does not have permission to create polls. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 5001 * **Error message**: Poll question cannot be blank * **Possible reason**: An empty poll question was provided. * **Possible solution**: Ensure the poll question contains text before creating the poll. #### Error code: 5002 * **Error message**: Poll does not have enough options * **Possible reason**: The poll needs at least two options. * **Possible solution**: Add more options to the poll before creating it. #### Error code: 5003 * **Error message**: Poll option cannot be blank * **Possible reason**: One or more poll options are empty. * **Possible solution**: Ensure all poll options contain text before creating the poll. #### Error code: 5004 * **Error message**: Cannot vote in this poll * **Possible reason**: The participant does not have permission to vote or the poll is closed. * **Possible solution**: Check the poll status and participant permissions. #### Error code: 5005 * **Error message**: Poll ID was invalid * **Possible reason**: The specified poll does not exist. * **Possible solution**: Verify the poll ID and ensure the poll exists. ## Plugin #### Error code: 6000 * **Error message**: User does not have permissions to launch/close plugins * **Possible reason**: The participant does not have permission to manage plugins. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 6001 * **Error message**: Cannot send data to an inactive plugin * **Possible reason**: The plugin is not currently active. * **Possible solution**: Ensure the plugin is activated before attempting to send data to it. ## Recording #### Error code: 7000 * **Error message**: User does not have permission to start recording * **Possible reason**: The participant does not have permission to start recording. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 7001 * **Error message**: Recording operation failed * **Possible reason**: An error occurred during the recording operation. * **Possible solution**: Try starting the recording again. If you continue to experience issues, please reach out to Cloudflare support. #### Error code: 7002 * **Error message**: There is no recording in progress * **Possible reason**: Attempted to stop a recording that is not active. * **Possible solution**: Verify that a recording is in progress before attempting to stop it. #### Error code: 7003 * **Error message**: Invalid recording state * **Possible reason**: The recording is in an unexpected state. * **Possible solution**: Check the recording status and try again. If you continue to experience issues, please reach out to Cloudflare support. ## Stage #### Error code: 8000 * **Error message**: Stage is disabled for this meeting type * **Possible reason**: The stage feature is not enabled for this meeting. * **Possible solution**: Enable the stage feature in your preset configuration. #### Error code: 8001 * **Error message**: Permission denied for stage operation * **Possible reason**: The participant does not have permission to perform stage operations. * **Possible solution**: Verify the participant's permissions in their respective preset and try again. #### Error code: 8003 * **Error message**: User has not requested stage access * **Possible reason**: The participant has not requested to join the stage. * **Possible solution**: Request stage access before attempting to join. #### Error code: 8004 * **Error message**: Action is invalid for user's current stage status * **Possible reason**: The action cannot be performed in the current stage status. * **Possible solution**: Check the participant's stage status and perform the appropriate action. Was this helpful? YesNo ## On this page [![](https://developers.cloudflare.com/_astro/logo.te5VL_aD.svg)Docs](https://developers.cloudflare.com/) ```json {"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/core/error-codes/#page","headline":"Error Codes · Cloudflare Realtime docs","description":"RealtimeKit error codes for identifying and troubleshooting SDK and API issues.","url":"https://developers.cloudflare.com/realtime/realtimekit/core/error-codes/","inLanguage":"en","image":"https://developers.cloudflare.com/og-docs.png","dateModified":"2026-05-28","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}} ``` --- --- description: Manage local user media devices, audio, video, and screenshare in RealtimeKit meetings. title: Local Participant image: https://developers.cloudflare.com/og-docs.png --- [Skip to content](#main-content) > Documentation Index > Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt > Use this file to discover all available pages before exploring further. # Local Participant Last updated Jul 9, 2026|Copy as Markdown|[View as Markdown](https://developers.cloudflare.com/realtime/realtimekit/core/local-participant/index.md)|[Agent setup](https://developers.cloudflare.com/agent-setup/) Manage local user media devices, control audio, video, and screenshare, and handle events in RealtimeKit meetings. Prerequisites Initialize the SDK and understand the meeting object structure. Refer to [Initialize SDK](https://developers.cloudflare.com/realtime/realtimekit/core/) and [Meeting Object Explained](https://developers.cloudflare.com/realtime/realtimekit/core/meeting-object-explained/). ## Introduction The local user is accessible via `meeting.self` and contains all information and methods related to the current participant. This includes media controls, device management, participant metadata, and state information. ## Properties WebMobile ReactWeb ComponentsAngular ### Metadata Properties Access participant identifiers and display information: ```js // Participant identifiers meeting.self.id; // Peer ID (unique per session) meeting.self.userId; // User ID (persistent across sessions) meeting.self.customParticipantId; // Custom identifier set by developer meeting.self.name; // Display name meeting.self.picture; // Display picture URL ``` ```jsx import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react"; // Participant identifiers const id = useRealtimeKitSelector((m) => m.self.id); const userId = useRealtimeKitSelector((m) => m.self.userId); const customParticipantId = useRealtimeKitSelector( (m) => m.self.customParticipantId, ); const name = useRealtimeKitSelector((m) => m.self.name); const picture = useRealtimeKitSelector((m) => m.self.picture); ``` ```kotlin // Participant identifiers meeting.localUser.id // Peer ID (unique per session) meeting.localUser.userId // User ID (persistent across sessions) meeting.localUser.customParticipantId // Custom identifier set by developer meeting.localUser.name // Display name meeting.localUser.picture // Display picture URL ``` ```swift // Participant identifiers meeting.localUser.id // Peer ID (unique per session) meeting.localUser.userId // User ID (persistent across sessions) meeting.localUser.customParticipantId // Custom identifier set by developer meeting.localUser.name // Display name meeting.localUser.picture // Display picture URL ``` ```jsx import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native"; // Participant identifiers const id = useRealtimeKitSelector((m) => m.self.id); const userId = useRealtimeKitSelector((m) => m.self.userId); const customParticipantId = useRealtimeKitSelector( (m) => m.self.customParticipantId, ); const name = useRealtimeKitSelector((m) => m.self.name); const picture = useRealtimeKitSelector((m) => m.self.picture); ``` ```dart // Participant identifiers meeting.localUser.id // Peer ID (unique per session) meeting.localUser.userId // User ID (persistent across sessions) meeting.localUser.customParticipantId // Custom identifier set by developer meeting.localUser.name // Display name meeting.localUser.picture // Display picture URL ``` ### Media Properties Access the local user's media tracks and states: ```js // Media state flags meeting.self.audioEnabled; // Boolean: Is audio enabled? meeting.self.videoEnabled; // Boolean: Is video enabled? meeting.self.screenShareEnabled; // Boolean: Is screen share active? // Media tracks (MediaStreamTrack objects) meeting.self.audioTrack; // Audio MediaStreamTrack (available when audioEnabled is true) meeting.self.videoTrack; // Video MediaStreamTrack (available when videoEnabled is true) meeting.self.screenShareTracks; // Object: { video: MediaStreamTrack, audio?: MediaStreamTrack } // Permissions granted by user meeting.self.mediaPermissions; // Current audio/video permissions ``` ```jsx // Media state flags const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled); const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled); const screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled, ); // Media tracks (MediaStreamTrack objects) const audioTrack = useRealtimeKitSelector((m) => m.self.audioTrack); const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack); const screenShareTracks = useRealtimeKitSelector( (m) => m.self.screenShareTracks, ); // Permissions granted by user const mediaPermissions = useRealtimeKitSelector((m) => m.self.mediaPermissions); ``` ```kotlin // Media state flags meeting.localUser.audioEnabled // Boolean: Is audio enabled? meeting.localUser.videoEnabled // Boolean: Is video enabled? meeting.localUser.screenShareEnabled // Boolean: Is screen share active? // Permissions granted by user meeting.localUser.isCameraPermissionGranted // Camera permission status meeting.localUser.isMicrophonePermissionGranted // Microphone permission status ``` ```swift // Media state flags meeting.localUser.audioEnabled // Boolean: Is audio enabled? meeting.localUser.videoEnabled // Boolean: Is video enabled? meeting.localUser.screenShareEnabled // Boolean: Is screen share active? // Permissions granted by user meeting.localUser.isCameraPermissionGranted // Camera permission status meeting.localUser.isMicrophonePermissionGranted // Microphone permission status ``` ```jsx // Media state flags const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled); const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled); const screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled, ); // Media tracks (MediaStreamTrack objects) const audioTrack = useRealtimeKitSelector((m) => m.self.audioTrack); const videoTrack = useRealtimeKitSelector((m) => m.self.videoTrack); const screenShareTracks = useRealtimeKitSelector( (m) => m.self.screenShareTracks, ); // Permissions granted by user const mediaPermissions = useRealtimeKitSelector((m) => m.self.mediaPermissions); ``` ```dart // Media state flags meeting.localUser.audioEnabled // Boolean: Is audio enabled? meeting.localUser.videoEnabled // Boolean: Is video enabled? meeting.localUser.screenShareEnabled // Boolean: Is screen share active? // Permissions granted by user meeting.localUser.isCameraPermissionGranted // Camera permission status meeting.localUser.isMicrophonePermissionGranted // Microphone permission status ``` ### State Properties Access room state and participant status: ```js // Room state meeting.self.roomJoined; // Boolean: Has joined the meeting? meeting.self.roomState; // Current room state (see possible values below) meeting.self.isPinned; // Boolean: Is the local user pinned? // Permissions and config meeting.self.permissions; // Capabilities defined by preset meeting.self.config; // Configuration for meeting appearance ``` **Room state values:** * `'init'` \- Initialized but not joined * `'joined'` \- Successfully joined the meeting * `'waitlisted'` \- Waiting in the waiting room * `'rejected'` \- Entry rejected * `'kicked'` \- Removed from meeting * `'left'` \- Left the meeting * `'ended'` \- Meeting has ended * `'disconnected'` \- Disconnected from meeting ```jsx // Room state const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined); const roomState = useRealtimeKitSelector((m) => m.self.roomState); const isPinned = useRealtimeKitSelector((m) => m.self.isPinned); // Permissions and config const permissions = useRealtimeKitSelector((m) => m.self.permissions); const config = useRealtimeKitSelector((m) => m.self.config); ``` **Example: Conditional rendering based on room state** ```jsx const roomState = useRealtimeKitSelector((m) => m.self.roomState); return ( <> {roomState === "disconnected" &&
You are disconnected
} {roomState === "waitlisted" &&
Waiting for host to admit you
} {roomState === "joined" &&
You are in the meeting
} ); ``` **Room state values:** * `'init'` \- Initialized but not joined * `'joined'` \- Successfully joined the meeting * `'waitlisted'` \- Waiting in the waiting room * `'rejected'` \- Entry rejected * `'kicked'` \- Removed from meeting * `'left'` \- Left the meeting * `'ended'` \- Meeting has ended * `'disconnected'` \- Disconnected from meeting ```kotlin // Room state meeting.localUser.roomJoined // Boolean: Has joined the meeting? meeting.localUser.waitListStatus // Waitlist status (None, Waiting, Accepted, Rejected) meeting.localUser.isPinned // Boolean: Is the local user pinned? // Permissions and config meeting.localUser.permissions // Capabilities defined by preset meeting.localUser.presetName // Name of preset for local user meeting.localUser.presetInfo // Typed object representing preset information ``` ```swift // Room state meeting.localUser.roomJoined // Boolean: Has joined the meeting? meeting.localUser.waitListStatus // Waitlist status (None, Waiting, Accepted, Rejected) meeting.localUser.isPinned // Boolean: Is the local user pinned? // Permissions and config meeting.localUser.permissions // Capabilities defined by preset meeting.localUser.presetName // Name of preset for local user meeting.localUser.presetInfo // Typed object representing preset information ``` ```jsx // Room state const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined); const roomState = useRealtimeKitSelector((m) => m.self.roomState); const isPinned = useRealtimeKitSelector((m) => m.self.isPinned); // Permissions and config const permissions = useRealtimeKitSelector((m) => m.self.permissions); const config = useRealtimeKitSelector((m) => m.self.config); ``` **Example: Conditional rendering based on room state** ```jsx const roomState = useRealtimeKitSelector((m) => m.self.roomState); return ( <> {roomState === "disconnected" && You are disconnected} {roomState === "waitlisted" && Waiting for host to admit you} {roomState === "joined" && You are in the meeting} ); ``` **Room state values:** * `'init'` \- Initialized but not joined * `'joined'` \- Successfully joined the meeting * `'waitlisted'` \- Waiting in the waiting room * `'rejected'` \- Entry rejected * `'kicked'` \- Removed from meeting * `'left'` \- Left the meeting * `'ended'` \- Meeting has ended * `'disconnected'` \- Disconnected from meeting ```dart // Room state meeting.localUser.isHost // Boolean: Is the local user a host? meeting.localUser.isPinned // Boolean: Is the local user pinned? meeting.localUser.stageStatus // Stage status of the local user // Permissions and flags meeting.localUser.flags // ParticipantFlags (recorder, hidden) ``` ## Media Controls ### Audio control Mute and unmute the microphone: ```js // Enable audio (unmute) await meeting.self.enableAudio(); // Disable audio (mute) await meeting.self.disableAudio(); // Check current status const isAudioEnabled = meeting.self.audioEnabled; ``` ```jsx import { useRealtimeKitClient } from "@cloudflare/realtimekit-react"; function AudioControls() { const [meeting] = useRealtimeKitClient(); const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled); const toggleAudio = async () => { if (audioEnabled) { await meeting.self.disableAudio(); } else { await meeting.self.enableAudio(); } }; return ( ); } ``` ```kotlin // Enable audio (unmute) meeting.localUser.enableAudio { error: AudioError? -> } // Disable audio (mute) meeting.localUser.disableAudio { error: AudioError? -> } // Check current status val isAudioEnabled = meeting.localUser.audioEnabled ``` ```swift // Enable audio (unmute) meeting.localUser.enableAudio { err in } // Disable audio (mute) meeting.localUser.disableAudio { err in } // Check current status let isAudioEnabled = meeting.localUser.audioEnabled ``` ```jsx import { useRealtimeKitClient, useRealtimeKitSelector, } from "@cloudflare/realtimekit-react-native"; import { TouchableHighlight, Text } from "react-native"; function AudioControls() { const [meeting] = useRealtimeKitClient(); const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled); const toggleAudio = async () => { if (audioEnabled) { await meeting.self.disableAudio(); } else { await meeting.self.enableAudio(); } }; return ( {audioEnabled ? "Mute" : "Unmute"} ); } ``` ```dart // Enable audio (unmute) meeting.localUser.enableAudio(onResult: (e) { // handle error if any }); // Disable audio (mute) meeting.localUser.disableAudio(onResult: (e) { // handle error if any }); // Check current status final isAudioEnabled = meeting.localUser.audioEnabled; ``` ### Video control Enable and disable the camera: ```js // Enable video await meeting.self.enableVideo(); // Disable video await meeting.self.disableVideo(); // Check current status const isVideoEnabled = meeting.self.videoEnabled; ``` ```jsx function VideoControls() { const [meeting] = useRealtimeKitClient(); const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled); const toggleVideo = async () => { if (videoEnabled) { await meeting.self.disableVideo(); } else { await meeting.self.enableVideo(); } }; return ( ); } ``` ```kotlin // Enable video meeting.localUser.enableVideo { error: VideoError? -> } // Disable video meeting.localUser.disableVideo { error: VideoError? -> } // Check current status val isVideoEnabled = meeting.localUser.videoEnabled ``` ```swift // Enable video meeting.localUser.enableVideo { err in } // Disable video meeting.localUser.disableVideo { err in } // Check current status let isVideoEnabled = meeting.localUser.videoEnabled ``` ```jsx function VideoControls() { const [meeting] = useRealtimeKitClient(); const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled); const toggleVideo = async () => { if (videoEnabled) { await meeting.self.disableVideo(); } else { await meeting.self.enableVideo(); } }; return ( {videoEnabled ? "Stop Video" : "Start Video"} ); } ``` ```dart // Enable video meeting.localUser.enableVideo(onResult: (e) { // handle error if any }); // Disable video meeting.localUser.disableVideo(onResult: (e) { // handle error if any }); // Check current status final isVideoEnabled = meeting.localUser.videoEnabled; ``` ### Screen share control Start and stop screen sharing: ```js // Enable screen share await meeting.self.enableScreenShare(); // Disable screen share await meeting.self.disableScreenShare(); // Check current status const isScreenShareEnabled = meeting.self.screenShareEnabled; ``` ```jsx function ScreenShareControls() { const [meeting] = useRealtimeKitClient(); const screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled, ); const toggleScreenShare = async () => { if (screenShareEnabled) { await meeting.self.disableScreenShare(); } else { await meeting.self.enableScreenShare(); } }; return ( ); } ``` ```kotlin // Enable screen share meeting.localUser.enableScreenShare() // Disable screen share meeting.localUser.disableScreenShare() // Check current status val isScreenShareEnabled = meeting.localUser.screenShareEnabled ``` Android API 14 and above Declare the following permission in your app's AndroidManifest.xml to use screenshare on Android devices running Android API 14 and above: ```plaintext ``` Adding this permission requires extra steps on Google Play Console. Refer to [Google's documentation ↗](https://support.google.com/googleplay/android-developer/answer/13392821?hl=en#declare) for more information. ```swift // Enable screen share let err: ScreenShareError? = meeting.localUser.enableScreenShare() // Disable screen share meeting.localUser.disableScreenShare() ``` Refer to the [Screen Share Setup (iOS)](#screen-share-setup-ios) section for platform-specific configuration. ```jsx function ScreenShareControls() { const [meeting] = useRealtimeKitClient(); const screenShareEnabled = useRealtimeKitSelector( (m) => m.self.screenShareEnabled, ); const toggleScreenShare = async () => { if (screenShareEnabled) { await meeting.self.disableScreenShare(); } else { await meeting.self.enableScreenShare(); } }; return ( {screenShareEnabled ? "Stop Sharing" : "Share Screen"} ); } ``` ```dart // Enable screen share meeting.localUser.enableScreenShare(); // Disable screen share meeting.localUser.disableScreenShare(); ``` Platform-specific setup **Android:** Declare the following permission in your app's AndroidManifest.xml to use screenshare on Android devices running Android API 14 and above: ```xml ``` **iOS:** Refer to the [Screen Share Setup (iOS)](#screen-share-setup-ios) section for additional configuration. ### Change display name Update the display name before joining the meeting: ```js await meeting.self.setName("New Name"); ``` Note Name changes only reflect across all participants if done before joining the meeting. ```jsx await meeting.self.setName("New Name"); ``` Note Name changes only reflect across all participants if done before joining the meeting. ```kotlin meeting.localUser.setDisplayName("New Name") ``` Note Name changes only reflect across all participants if done before joining the meeting. ```swift meeting.localUser.setDisplayName(name: "New Name") ``` Note Name changes only reflect across all participants if done before joining the meeting. ```jsx await meeting.self.setName("New Name"); ``` Note Name changes only reflect across all participants if done before joining the meeting. ```dart if (meeting.permissions.miscellaneous.canEditDisplayName) { meeting.localUser.setDisplayName("New Name"); } ``` Note Name changes only reflect across all participants if done before joining the meeting and the local user has preset permission to change the name. ## Manage media devices ### Get available devices ```js // Get all media devices const devices = await meeting.self.getAllDevices(); // Get all audio input devices (microphones) const audioDevices = await meeting.self.getAudioDevices(); // Get all video input devices (cameras) const videoDevices = await meeting.self.getVideoDevices(); // Get all audio output devices (speakers) const speakerDevices = await meeting.self.getSpeakerDevices(); // Get device by ID const device = await meeting.self.getDeviceById("device-id", "audio"); // Get current devices being used const currentDevices = meeting.self.getCurrentDevices(); // Returns: { audio: MediaDeviceInfo, video: MediaDeviceInfo, speaker: MediaDeviceInfo } ``` ```jsx import { useRealtimeKitClient } from "@cloudflare/realtimekit-react"; import { useState, useEffect } from "react"; function DeviceSelector() { const [meeting] = useRealtimeKitClient(); const [audioDevices, setAudioDevices] = useState([]); const [videoDevices, setVideoDevices] = useState([]); useEffect(() => { if (!meeting) return; const loadDevices = async () => { const audio = await meeting.self.getAudioDevices(); const video = await meeting.self.getVideoDevices(); setAudioDevices(audio); setVideoDevices(video); }; loadDevices(); }, [meeting]); const handleDeviceChange = async (device) => { await meeting.self.setDevice(device); }; return (
); } ``` Get current devices being used: ```jsx const currentDevices = meeting.self.getCurrentDevices(); // Returns: { audio: MediaDeviceInfo, video: MediaDeviceInfo, speaker: MediaDeviceInfo } ``` ```kotlin // Get all audio devices val audioDevices: List = meeting.localUser.getAudioDevices() // Get all video devices val videoDevices: List = meeting.localUser.getVideoDevices() // Get currently selected audio device val selectedAudioDevice: AudioDevice = meeting.localUser.getSelectedAudioDevice() // Get currently selected video device val selectedVideoDevice: VideoDevice = meeting.localUser.getSelectedVideoDevice() ``` ```swift // Get all audio devices let audioDevices = meeting.localUser.getAudioDevices() // Get all video devices let videoDevices = meeting.localUser.getVideoDevices() // Get currently selected audio device let selectedAudioDevice = meeting.localUser.getSelectedAudioDevice() // Get currently selected video device let selectedVideoDevice = meeting.localUser.getSelectedVideoDevice() ``` ```jsx import { useRealtimeKitClient } from "@cloudflare/realtimekit-react-native"; import { useState, useEffect } from "react"; import { FlatList, TouchableHighlight, Text, View } from "react-native"; function DeviceSelector() { const [meeting] = useRealtimeKitClient(); const [audioDevices, setAudioDevices] = useState([]); const [videoDevices, setVideoDevices] = useState([]); useEffect(() => { if (!meeting) return; const loadDevices = async () => { const audio = await meeting.self.getAudioDevices(); const video = await meeting.self.getVideoDevices(); setAudioDevices(audio); setVideoDevices(video); }; loadDevices(); }, [meeting]); const handleDeviceChange = async (device) => { await meeting.self.setDevice(device); }; return ( ( handleDeviceChange(item)}> {item.label} )} keyExtractor={(item) => item.deviceId} /> ); } ``` Get current devices being used: ```jsx const currentDevices = meeting.self.getCurrentDevices(); // Returns: { audio: MediaDeviceInfo, video: MediaDeviceInfo, speaker: MediaDeviceInfo } ``` ```dart // Get all audio devices final audioDevices = await meeting.localUser.getAudioDevices(); // Get all video devices final videoDevices = await meeting.localUser.getVideoDevices(); // Get currently selected audio device final selectedAudioDevice = meeting.localUser.getSelectedAudioDevice(); // Get currently selected video device final selectedVideoDevice = meeting.localUser.getSelectedVideoDevice(); ``` ### Change device Switch to a different media device: ```js // Get all devices const devices = await meeting.self.getAllDevices(); // Set a specific device (replaces device of the same kind) await meeting.self.setDevice(devices[0]); ``` Use the device selector example from the previous section. The `handleDeviceChange` function demonstrates how to switch devices. ```kotlin // Get all audio devices val audioDevices = meeting.localUser.getAudioDevices() // Set audio device meeting.localUser.setAudioDevice(audioDevices[0]) // Get all video devices val videoDevices = meeting.localUser.getVideoDevices() // Set video device meeting.localUser.setVideoDevice(videoDevices[0]) // Switch between front and back camera on devices with 2 cameras meeting.localUser.switchCamera() ``` ```swift // Set audio device meeting.localUser.setAudioDevice(device) // Set video device meeting.localUser.setVideoDevice(videoDevice: device) // Switch between front and back camera meeting.localUser.switchCamera() ``` Use the device selector example from the previous section. The `handleDeviceChange` function demonstrates how to switch devices. ```js const handleDeviceChange = async (device) => { await meeting.self.setDevice(device); }; ``` ```dart // Get all available audio devices final audioDevices = await meeting.localUser.getAudioDevices(); // Switch audio device await meeting.localUser.setAudioDevice(audioDevices[1]); // Get all available video devices final videoDevices = await meeting.localUser.getVideoDevices(); // Switch video device await meeting.localUser.setVideoDevice(videoDevices[1]); // Switch between available camera sources meeting.localUser.switchCamera(); ``` ## Display local video ### Register video element Attach the local video track to a `