Virtual Events and Webinars - React
Virtual events let speakers present to attendees in town halls, workshops, product launches, creator streams and audio rooms. Attendees react in real time and come on stage for questions, and the event can be recorded and transcribed for a replay. The event is built on Interactive Live Streaming (ILS). An audio room is the same event with cameras off.
This guide also covers audiences of thousands who mostly watch. Follow the event flow as written, then see Scale to Thousands of Viewers with HLS for the steps HLS adds.
If you are new to VideoSDK, start with Introduction and Integrate React SDK, and check Supported Browsers for the devices your attendees use.
VideoSDK runs the live video, audio, chat, recording and HLS delivery for the event. Your application owns the registration list, the tickets, the agenda, and the experience around the event.
Set Up the Event Session
Prepare the room and generate participant tokens on your backend before the host goes live.
- Keep registration and the agenda in your backend: VideoSDK does not keep the registration list, the tickets or the agenda, so keep them in your event platform. Create the room for each session, issue the tokens when it is due, and open the event in the application when the host is ready to go live.
- Create a room: Use the Create a Room API to create a room for each event. Use the returned
roomIdas the event ID and attach it to the event in your registration system. - Validate the room: Before issuing tokens, use the Validate a Room API to ensure the room is valid and available for the event. If validation fails, do not issue the tokens, and report the failure to your event platform.
- Set participant modes: Use
SEND_AND_RECVmode for the host, speakers and panelists so they publish their camera and microphone and see each other. UseRECV_ONLYmode for attendees so they receive the stage in real time without publishing and take part through chat, polls, reactions and raised hands. See Host vs Audience Modes, Manage Roles and Initialize Live Stream. - Configure participant permissions: Set
allow_joinandallow_modin the host's token so the host joins the event directly and can turn off the camera or microphone of a speaker or a guest on stage. Setallow_joinin speaker and attendee tokens so they join directly. IncluderoomIdandparticipantIdin every token payload, withversionset to2. See Authentication and Tokens. - Gate controls by role: Features such as recording, bringing an attendee on stage and streaming out to social platforms are available to all participants by default. Show these controls only in the host's console to keep the program under the host's control.
Configure the Attendee's Devices and Network
Attendees join from laptops and phones, often over corporate or mobile networks and often part way through the program, and most of them only watch. Keep their path to the event short.
Check the Network Before the Event
- Assess network quality: Use
runPreCallTest()to evaluate uplink and downlink quality on a scale from 1 (BAD) to 5 (EXCELLENT), along with the factors affecting each score. The downlink score matters most for an attendee who only watches. Use the results to select an appropriate starting media configuration and tell the attendee what to expect from their connection. See Precall Setup. - Join the event directly: Set
joinWithoutUserInteractiontotrueonMeetingProviderso an attendee who opens the event from their ticket joins at once inRECV_ONLYmode, with no second screen in the way. See Initialize Live Stream. - Hold attendees on a waiting screen: Show attendees who arrive early a waiting screen until a host is present, rather than an empty room. Switch to the stage when the first
SEND_AND_RECVparticipant joins. See Waiting Screen.
Configure the Host's Console
The host runs the program from a console, presenting with the speakers, managing who is on stage and taking questions from the audience. The console should provide controls for engaging attendees, bringing them on stage, connecting with other streams and controlling the event.
Prepare the Speakers' Stage Before Going Live
- Check devices and permissions before going live: A broadcast goes out as it happens and a speaker cannot see what attendees see, so give each speaker a green room screen that confirms a working setup before going live. Use
checkPermissions()andrequestPermission()from theuseMediaDevicehook to check and request camera and microphone access,getCameras()andgetMicrophones()to list devices, andonDeviceChangedto refresh the list when a device is connected. Complete the check with a camera preview and a microphone and speaker test. See Precall Setup. - Assess network quality: Use
runPreCallTest()to evaluate each speaker's uplink and downlink quality on a scale from 1 (BAD) to 5 (EXCELLENT), along with the factors affecting each score. Use the results to select an appropriate starting media configuration before the speaker goes live. See Precall Setup. - Apply the verified setup: Pass the selected camera and microphone devices, their enabled states, and the chosen media configuration to
MeetingProvider, so each speaker goes live with the setup they verified. The configuration can include a custom camera track created withcreateCameraVideoTrack(). See Precall Setup for recommended configurations based on the test results and Optimize Video Tracks. - Apply a virtual background: Use the Virtual Background plugin to give every speaker the same branded backdrop, or to blur their surroundings, so the stage looks consistent wherever each speaker joins from. Virtual backgrounds are supported on desktop browsers.
- Suppress background noise: Use the AI Noise Suppression plugin to filter background noise from a speaker joining from a hotel room or a conference floor, so attendees hear every word.
- Brand the stage: Use a custom template to compose the stage into a produced picture, with lower thirds, a logo, a sponsor strip or an agenda, rather than drawing overlays on each device. See Customized Live Stream.
Control Your Own Media and Devices
- Give hosts microphone and camera controls: Provide microphone and camera controls for the host and the speakers, and in the attendee's application for guests on stage, using
muteMic()andunmuteMic()andenableWebcam()anddisableWebcam()from theuseMeetinghook. In an audio room, leave the camera controls out. See Mute / Unmute Mic and On / Off Camera. - Allow hosts to change devices: Provide a device picker on stage so a speaker can change their microphone or camera mid-session without leaving the stage. Use
getMics()andgetWebcams()from theuseMeetinghook to list available devices and theirdeviceIdandlabel. Pass the selecteddeviceIdtochangeMic()orchangeWebcam(). See Manage Audio and Video Devices.
Engage Attendees in Real Time
- Use chat: Chat built on PubSub lets attendees ask questions at event scale. Use a group chat to reach everyone, and pass the attendee's
participantIdin thesendOnlyoption ofpublish()for a private chat, so the host or a moderator can answer one attendee directly. Setpersisttotruewhen publishing messages to retain them for the session. Persisted messages can be downloaded as CSV from the VideoSDK session dashboard for the event record. See Chat during Live Stream and Private Chat. - Show reactions: Publish reactions as emojis over PubSub and animate them on every screen, so speakers get instant feedback from an audience that would otherwise be silent. See Reactions.
- Run a poll: Publish a poll over PubSub to give the host a read on the room mid-session, and aggregate the results on every screen as votes arrive. See Audience Polls.
- Indicate who is speaking: Use
onSpeakerChangedfrom theuseMeetinghook to show who is speaking, which helps a panel discussion follow the conversation. See Active Speaker Indication.
Bring Attendees on Stage
- Let an attendee raise a hand: Use a dedicated PubSub topic so an attendee who wants to ask can raise a hand, and queue them in the host's console. See Raise Hand.
- Invite the attendee on stage: Publish the invitation on a PubSub topic addressed to that participant, since
changeMode()only changes the mode of the participant who calls it. When the attendee accepts, have their application switch toSEND_AND_RECVwithchangeMode()so their camera and microphone go live. See Invite Guest on Stage and Change Mode. - Pin the guest on stage: Handle
onParticipantModeChangedfrom theuseMeetinghook in the host's application to learn that the attendee is on stage, and callpin()to put them next to the speaker. After the answer, have the attendee switch back toRECV_ONLYand callunpin(). See Pin/Unpin Participant.
Connect with Other Streams
For collaborative sessions, cross-promotions and multi-session conferences, connect your stream with other streams and platforms.
- Relay a co-host or panel into another stream: Use
requestMediaRelay()from theuseMeetinghook to relay a host's audio, video or screen share into another live stream, for a collaborative session or a cross-promotion. See Relay Media (PK Host). - Move attendees between sessions: Have each attendee's application call
switchTo()from theuseMeetinghook with the next meeting's ID to move from one session of a multi-session conference to the next, so attendees switch without leaving and rejoining. See Switch Live Stream. - Stream out to social platforms: Use
startLivestream()from theuseMeetinghook, with the platform's stream URL and key, to send the event out to YouTube, LinkedIn or any RTMP platform while the interactive event keeps running in your application. UsestopLivestream()to end it, and handleonLivestreamStateChangedto learn when the stream-out starts and stops. See RTMP Livestream.
Control the Event
- Control a speaker's media: When a speaker or a guest on stage leaves their microphone open or their camera on after their turn, call
disableMic()ordisableWebcam()from theuseParticipanthook in the host's console, which requiresallow_modin the host's token. To ask them to turn a device back on, useenableMic()orenableWebcam(), which send a request they accept throughonMicRequestedoronWebcamRequestedin their application. See Toggle Remote Participant Media. - Remove a disruptive attendee: Use
remove()from theuseParticipanthook so the host can remove an attendee who disrupts the event. See Remove Participant. - End the event: When the program is over, call
end()from theuseMeetinghook to end the event for every participant. See Leave or End Meeting.
Configure the Event Screen on Both Sides
These components are built into both the attendee's application and the host's console.
Display the Stage
- Render only the hosts: Render audio and video for
SEND_AND_RECVparticipants only and nothing for the audience, since only hosts publish media, which keeps every attendee's screen clean and their device's load low. See Rendering Host and Audience Views. - Render each host's video: Use the
VideoPlayercomponent to render each host's video. It requests the video quality that matches the tile size, so the speaker fills the attendee's screen while the rest of the panel stays in smaller tiles, and it mirrors the local preview. See Display Audio and Video. - Keep the stage visible with Picture-in-Picture: Use Picture-in-Picture to keep the stage visible in a floating window when an attendee opens the agenda or a sponsor page, or the host opens the run of show. See Picture-in-Picture Mode.
- Plan for a large audience: An audience of thousands calls for a deliberate choice of which streams each screen renders and subscribes to. Follow the guidance for large rooms on layout, pagination and subscriptions at that size. See Handle Large Rooms.
Show Participant Status
- Show attendance: Use the
participantsmap from theuseMeetinghook to show attendance live on every screen. See Display Attendees Count. - Track participant joins and leaves: Use
onParticipantJoinedandonParticipantLeftfrom theuseMeetinghook to keep the attendance count and the list of people on stage current as attendees come and go. See Participant Events. - Show the hosts' microphone and camera status: Use
micOnandwebcamOnfrom theuseParticipanthook to indicate whether each speaker's microphone and camera are enabled. See Rendering Host and Audience Views. - Monitor the incoming video stream: Use
onStreamStateChangedfrom theuseStreamhook to detect when a speaker's video stream becomes active, stuck, or frozen on an attendee's screen, so the application can tell the attendee to check their connection rather than leave. See Monitoring Remote Streams.
Keep the Session Reliable
These controls apply to both the attendee's application and the host's console.
Handle Network Changes During the Event
Network conditions can change during an event. Attendees often join from corporate or mobile networks, while speakers may present from a venue, a hotel or a home office. VideoSDK adapts video quality to available bandwidth and provides network and media events that help your application respond to changing conditions.
- Keep both sides informed: Use the relevant quality events to notify attendees and speakers when network conditions affect event quality and when conditions improve. See Monitoring Network Quality for available events and Optimize for Low Bandwidth Networks for recommendations before and during the event.
- Show the connection state: Use
onMeetingStateChangedfrom theuseMeetinghook to follow the connection throughCONNECTING,CONNECTED,RECONNECTINGandDISCONNECTED. Show a reconnecting indicator on both screens while the state isRECONNECTING, so attendees know the event is recovering rather than over, and return to the agenda screen when it reachesDISCONNECTED. See Meeting Connection State Events.
Handle Errors and Follow Best Practices
- Handle SDK errors: Use
onErrorfrom theuseMeetinghook to receive error information when a meeting operation fails. Display actionable feedback in the attendee's application and the host's console, and send relevant diagnostic details to your monitoring service so failures across events stay visible to your team. Avoid including attendee data in logs. See Error Events. - Follow integration guidelines: Review the User Experience Guidelines and Developer Experience Guidelines to build a reliable, accessible event experience.
Recording
Configure recording, storage delivery, and encryption based on what the replay needs. You can start recording when the host goes live and configure transcription alongside it. If recording is mandatory, verify that it has started before proceeding with the event. The recording file, the transcript and the summary are delivered once the event ends, so the replay can go to registrants who missed it and the summary can follow as the event notes. The recording's delivery destination and encryption depend on your configured storage and encryption settings.
Choose the Recording Type
VideoSDK offers three types of recording. Choose the one that matches what your replay needs. See Recording Overview.
- Meeting recording: Records the entire event, every host and shared content, into one file with a composed layout. Record it with the same custom template as the live stage, so the replay carries the produced picture attendees saw live. Set
modetoaudioin the recordingconfigfor an audio room, so the file is smaller. See Record Live Stream and Customized Live Stream. - Participant recording: Records one participant's audio and video into a single file, such as the keynote speaker alone for a session library. Start it with the Start Participant Recording API, and use the matching participant recording API to stop it. See Participant Recording (Individual).
- Participant track recording: Records a single audio or video track of one participant as its own file. Start it with the Start Track Recording API, and use the matching track recording API to stop it. See Participant Track Recording.
Record the Event
- Start recording automatically: If the event must be recorded from the beginning, show attendees a consent notice before the event starts, then add a
recordingentry toautoStartConfigwhen creating the room with the Create a Room API. Use this entry to configure transcription, recordingconfig, and the custom recording templatetemplateUrlas needed. See Create a Room. - Wait for recording confirmation: Show a loader in the host's console until the recording starts. The backend receives the
recording-startedwebhook, while the applications receiveonRecordingStateChangedwithRECORDING_STARTED. If the host starts recording manually usingstartRecording()from theuseMeetinghook, show the same loader until the started status arrives. See Record Live Stream and Webhook and Events. - Handle recording failures: VideoSDK sends the
recording-failedwebhook if recording fails while starting, during the event, or while stopping. If recording fails at the start and recording is mandatory, do not start the event. If recording is optional, you can continue without it. If recording fails during the event, you can retry or end the session. To close the room automatically when recording fails, configure theonFailureoption as described below. See Recording Failed Webhook. - Close the room automatically: If recording is mandatory, configure VideoSDK to close the room when recording fails. Add an
onFailureentry to therecordingsection ofautoStartConfig, or include it in the request body when starting recording from your backend. Setactiontoclose-roomand configurewaitTimein seconds. We recommend30seconds. See theonFailureparameter and example request body in Create a Room and Start Recording. - Stop the recording and store the file: The recording stops automatically when the event ends. To stop it earlier, call
stopRecording()from theuseMeetinghook or the Stop Recording API from your backend. Your backend receives therecording-stoppedwebhook with thefileUrl,filePathandsessionId, so it can attach the replay to the event in your registration system. To look the file up later, use the Fetch Recordings API to list the recordings of a room or the Fetch a Recording API to retrieve one by its id. See Webhook and Events.
Store Recordings in Your Own Storage
- Configure your own storage: Use Configure Recording Storage in the dashboard to deliver recordings to your own AWS S3, Azure Blob or GCP bucket. It is set per API key and includes a test step.
- Upload with a presigned URL: When stored credentials are not allowed, generate a presigned URL on your backend and pass it to the Start Recording API, so the recording is uploaded to one object for a limited time.
VideoSDK uploads recordings to your storage with the credentials you set up. If your storage only accepts uploads from approved IP addresses, you will need to approve VideoSDK's static IP addresses. Contact us to request them.
Encrypt and Decrypt Recordings
- Encrypt recordings at rest: Turn on Recording Encryption for the API key in the dashboard, under Cloud Storage Configuration, and provide an RSA public key. Each recording is encrypted with its own AES key before upload.
AES-256-GCMis recommended. - Decrypt with your private key: Each recording arrives as three files: the encrypted recording, the wrapped AES key and a metadata file. Decrypt them on your side with your private key. The guide includes a Node.js example.
Recording Encryption and transcription cannot be enabled together on one API key, since transcription runs on unencrypted recordings. Decide per API key which your replay needs before configuring either.
Transcription
Live captions meet accessibility requirements and help attendees following in a second language, while the post-event transcript and summary become the event notes. Transcription is not available for an API key with Recording Encryption enabled.
Show Live Captions
- Display live captions: Use
startTranscription()from theuseTranscriptionhook to turn each speaker's words into captions. Each line is delivered throughonTranscriptionTextto every attendee. CallstopTranscription()when captions are no longer needed. See Live Captioning.
Generate the Transcript and Summary
- Generate a post-event transcript and summary: Pass a
transcriptionconfiguration tostartRecording(), setenabledtotrue, and provide asummary.promptthat specifies the sections you need, such as agenda, speakers and action items. Retrieve the transcript and summary using the Fetch Post Transcriptions & Summary API or the webhook URL provided tostartRecording(). See Post Transcription & Summary.
Choose the Language
- Set the spoken language: Pass the appropriate language code to
startTranscription(). The default isen. See Supported Languages for available codes, including regional variants such asen-IN,hi, andes-419. - Translate captions when needed: For an international audience, use Realtime Translation to caption each speaker in the attendee's own language.
Scale to Thousands of Viewers with HLS
When most of your audience watches rather than interacts, use HLS to deliver the stage through a player instead of the room. HLS adds a typical delay of 15 to 30 seconds. Everything above, from session setup to the host's console, recording, transcription, compliance and firewall, applies as written.
- Start and stop HLS: Call
startHls()from theuseMeetinghook to start the HLS output with your chosen layout, quality and orientation, andstopHls()to end it. HandleonHlsStateChangedto show a loader while the state isHLS_STARTINGorHLS_STARTED. AtHLS_PLAYABLE, the event carries theplaybackHlsUrlandlivestreamUrl. See Start HLS and Stop HLS. - Play the stream: Play the
playbackHlsUrlfor viewers with an HLS player built on hls.js. This URL is the live stream with playback support, so viewers can go back in the stream. ThelivestreamUrlis the live stream without playback support. See Setup HLS Player. - Show a preview of the stream: While HLS runs alongside the interactive stream, call the Fetch an HLS Thumbnail API from your backend with the room's
roomId. Show the returned image as a preview card in your listing of live events, so attendees can see what is on air before they open it. See Generate HLS Thumbnail. - Track HLS from your backend: Your backend receives the
hls-starting,hls-started,hls-playable,hls-stoppingandhls-stoppedwebhooks, so you can mark the event live in your listing and show it as ended afterwards. Thehls-playablewebhook carries theplaybackHlsUrlandlivestreamUrl, so your backend can hand the playback URL to attendees who only watch and never join the room. See Webhooks List. - Control HLS from your backend: To run HLS without a console, use the Start HLS API and the Stop HLS API. Check whether an event's stream is live with the Fetch Active HLS API, retrieve one by its id with the Fetch an HLS API, and remove one you no longer need with the Delete an HLS API. Pair the live check with the thumbnail API to build a listing of live events with preview cards.
- Let HLS viewers chat: Have viewers join the room in
SIGNALLING_ONLYmode, which carries messages and presence without media, so they can chat and still be counted in attendance. Chat, reactions and polls work for these viewers. Bringing a viewer on stage is not possible over HLS. See Chat using PubSub.
Meet Compliance Requirements
Configure these session-wide controls to align your event with your organization's data protection policies and regional requirements.
| Requirement | Control |
|---|---|
| Keep media within a region | Use Geo Fencing to restrict connections to VideoSDK servers in your specified regions, regardless of the attendee's location. See Geo Fencing. |
| Allow or restrict access by location | Use Geo IP Restriction to allow or deny access based on the attendee's IP address and your geographic rules, such as an event licensed for one region. See Geo IP Restriction. |
| Encrypt media end to end | Use E2EE to encrypt media on the sender's device and decrypt it only on the receiver's device, preventing intermediaries, including VideoSDK servers, from accessing the media. Your application is responsible for generating and distributing encryption keys. See E2EE Setup. |
| Meet regulatory requirements | VideoSDK offers GDPR compliance with EU data residency and a Data Processing Agreement for attendees in the EU, and SOC 2 Type II, ISO 27001 and CERT-IN for corporate procurement and vendor reviews. GDPR compliance and the Data Processing Agreement are available as add-ons. |
If E2EE is enabled and you want to record or transcribe the event, use a custom recording template. See Customized Live Stream.
- Recording: Build the template with the React or JavaScript SDK and enable E2EE in it using the same key provider and shared key as your application, so the template can decrypt the media for recording. Deliver the key securely from your backend to the template over HTTPS rather than including it in the
templateUrl, then start the recording using the template'stemplateUrl. - Transcription: Use the same template recording for the post-event transcript and summary, since post transcription runs on the recording file. Realtime transcription is not available when E2EE is enabled.
Firewall and Debugging
Speakers often present from a venue or office network behind a firewall, and many attendees watch from corporate networks with their own restrictions. Configure firewall rules before the first event to ensure the required VideoSDK services are reachable. If a session fails to connect, use the checks below to identify potential network restrictions.
Allow VideoSDK Traffic Through Your Firewall
-
Allow VideoSDK domains: The simplest approach is to allow all subdomains of
*.videosdk.liveover TCP and UDP on all ports. This covers the API, signaling, media, and TURN servers. See Firewall Setup. -
Allow specific hosts and ports: If your network policy requires explicit firewall rules, allow the following hosts and ports. VideoSDK can use the fallback transport when the primary connection is unavailable.
Server Host Primary Fallback API api.videosdk.liveTCP 443 — Signaling *.rm.videosdk.live, or the regional hosts listed in the Firewall Setup guideTCP 443 — Media media.videosdk.liveUDP 40000–60000 TCP 40000–60000 TURN turn.videosdk.liveUDP and TCP 3478 TCP 443 Also allow
sdk.videosdk.liveover TCP 443 if your console loads the SDK or the PubSub worker from the VideoSDK CDN. The full-access policy already covers it. -
Allow the PubSub worker in your Content Security Policy: PubSub runs in a web worker created from a
blob:URL. If the host's console runs under a strict Content Security Policy, allowblob:in theworker-srcdirective, or passpubsubWorkerUrltoMeetingProviderto load a pre-built worker from the VideoSDK CDN or your own domain. See CSP policy for PubSub. -
Use Cloud Proxy for restricted networks: If the required ports cannot be opened, use Cloud Proxy to route media through the proxy servers you specify. Choose UDP over TCP (the default), Force UDP, or Force TCP according to your network restrictions.
Debug Connection Issues
- Test the network before the first event: Run
runPreCallTest()from the venue's network after configuring the firewall. The test checks whether a call can transmit media in both directions. If it fails despite internet access, review the required hosts and ports above. See Precall Setup. - Check UDP connectivity: Set
preferredProtocoltoTCP_ONLYinMeetingProviderand try connecting again. If the stream succeeds over TCP but fails with the defaultUDP_OVER_TCPconfiguration, investigate whether UDP traffic is blocked on the media or TURN ports. You can also configure Cloud Proxy for the affected network. - Inspect connection errors: Use
onErrorfrom theuseMeetinghook to capture the error code and message when a connection fails. Use this information to investigate whether the failure occurred during the API, signaling, or media stage, and forward relevant diagnostics to your monitoring service to identify affected networks. See Error Events. - Verify media flow: Use
getTransportStats()from theuseParticipanthook to inspect transport statistics and check whether traffic is flowing in both directions. If one direction shows no bitrate, investigate the corresponding network path and firewall rules. See Monitoring Transport Stats. - Collect connection logs: Call
setLogLevel()withConstants.LogLevel.DEBUGto collect detailed diagnostics while reproducing the issue. After troubleshooting, restore the log level toINFOorNONEas appropriate. See Log Level.
API Reference
The API references for all the methods and events utilized in this guide are provided below.
- runPreCallTest()
- setLogLevel()
- MeetingProvider
- joinWithoutUserInteraction
- createCameraVideoTrack()
- VideoPlayer
- checkPermissions()
- requestPermission()
- getCameras()
- getMicrophones()
- onDeviceChanged
- end()
- muteMic()
- unmuteMic()
- enableWebcam()
- disableWebcam()
- getMics()
- changeMic()
- getWebcams()
- changeWebcam()
- changeMode()
- switchTo()
- requestMediaRelay()
- startLivestream()
- stopLivestream()
- startHls()
- stopHls()
- participants
- enableMic()
- disableMic()
- enableWebcam()
- disableWebcam()
- pin()
- unpin()
- remove()
- getTransportStats()
- micOn
- webcamOn
- publish()
- onParticipantJoined
- onParticipantLeft
- onParticipantModeChanged
- onSpeakerChanged
- onMeetingStateChanged
- onError
- onMicRequested
- onWebcamRequested
- onLivestreamStateChanged
- onHlsStateChanged
- onStreamStateChanged
- startRecording()
- stopRecording()
- onRecordingStateChanged
- startTranscription()
- stopTranscription()
- onTranscriptionText
- Create a Room
- Validate a Room
- Start Participant Recording
- Start Track Recording
- Start Recording
- Stop Recording
- Fetch Recordings
- Fetch a Recording
- Fetch an HLS Thumbnail
- Fetch Post Transcriptions & Summary
- Start HLS
- Stop HLS
- Fetch Active HLS
- Fetch an HLS
- Delete an HLS
- Webhooks List
Got a Question? Ask us on discord

