The modern workplace is all about video calls and real-time chat. But what happens when those meetings require a server you do not own? It raises security concerns and lacks data control.
So, the answer for most enterprises is to build your own communication platform. This helps businesses subject to strict data regulations maintain complete control and avoid relying on a vendor.
This guide shows you how to build a self-hosted app like Zoom, from architecture to features to the tech stack.
What is a Zoom App?
Zoom is a popular video conferencing app that boomed during the pandemic as work from home turned normal. It lets people talk, meet, and work online in HD video and audio.
This tool usually includes meeting rooms, chat rooms, screen sharing, file sharing and more. Think of it as a virtual meeting room that is always open, just without the commute.
Common Pain Points or Challenges Enterprises Face When Using Zoom
In many cases, enterprises using ready-made tools like Zoom hit brick walls. Here are the challenges they face regularly:
- Privacy Concerns - Sensitive meeting data is stored on third-party servers that the enterprise has no direct access to.
- Rising Subscription Cost - Per-user, per-month pricing gets expensive as teams grow.
- Limited Branding - Not all SaaS tools support full white label customization and can be very limited.
- Compliance Gaps - Industries like healthcare and finance require higher standards for data handling than generic platforms provide.
- Dependency on Third-Party Uptime - When the vendor's servers fail, your business communications go down as well.
Why Build a Self-Hosted Zoom App?
A self-hosted communication tool changes the game entirely. Instead of renting infrastructure, enterprises own it outright. Here's why more businesses are building their own Zoom app:
- Full Source Code Ownership - Modify and expand the platform as your business grows.
- 100% Data Ownership - Your data stays on your servers, not on the vendors.
- Complete White Label - Make it look and feel like your own product, not just a rented tool.
- Private Deployment - Host on your own cloud or on-premises infrastructure.
- Better Security - You have full control over end-to-end encryption, access and storage.
- Deeper Customization - Add/remove features according to your business needs.
- No Recurring Cost - Pay a one-time license fee and scale up without additional costs.
- Enterprise Integrations - Connect to your existing CRM, HRMS and internal tools.
- Better Compliance - You can meet industry standards like HIPAA, SOC 2 or GDPR more easily.
- Multi-Tenant Support - Serve multiple business units/clients from a single deployment.
- Enterprise-Level Security - Built-in end-to-end encryption and access control.
How Does Custom Zoom App Architecture Work?
Most of these custom video conferencing apps are client-server based with WebRTC for live audio and video, signaling servers for connection coordination, media servers for multi-party calls, and a backend for user authentication, chat and file storage. Combined, these layers allow for low latency meetings.
Top 10 Features of a Custom App Like Zoom
Building a communication app like Zoom for your enterprise is not about video calls, it’s all about creating a complete workspace for your team. Here are the essential features that make a video conferencing tool work for teams.
- Team Workspace -Dedicated spaces for departments and projects to collaborate without clutter.
- Channels - Organized and topical conversations that are easy to follow and manage.
- Direct Messaging - One-to-one chats for instant private conversations.
- Voice Calling - When video is not needed, instant audio calls are made using voice calling.
- HD Meetings - Crystal clear video conferencing that can handle larger teams.
- Screen Sharing - Present documents, slides, product demos and more on your screen in real time.
- Calendar - Meetings can be scheduled and managed using a calendar app.
- File Sharing - Share documents, images and files in chat or meetings.
- Admin Dashboard - Access all your users, permissions and settings from one control panel.
- Enterprise Security - SSO, MFA, end-to-end encryption and compliance to protect every conversation.
Recommended Technology Stack
Picking the right stack affects how your app performs in real-life scenarios. Here’s the recommendations for building a scalable and secure video conferencing app:
How To Build An App Like Zoom?
When I was searching for better ways to build a secure messaging app, I found MirrorFly IM solution and Apphitect. Where MirrorFly offered a self-hosted solution with source code access and real-time communication built in.
Let’s see how we developed an app like Zoom for our enterprise using MirrorFly SDK on JAVA.
Step 1: Initial Setup
Install the dependencies and get your license key.
Requirements (Android, iOS, Web):
Android Lollipop 5.0 and above (API Level 21)
Java 7 and above
Gradle 4.1.0 and above
targetSdkVersion / compileSdk 34 and above
Get your license key:
If you’re already a registered user? Find it under Application Info in your account.
If not registered? contact the MirrorFly Team to set up your account.
1. Set up your project - Open a new or existing project in Android Studio.
2. Add the repository
For Gradle 6.8 and above, add to settings.gradle
For Gradle 6.7 or lower, add to root build.gradle
(Check the release notes for Gradle-specific details.)
dependencyResolutionManagement {
repositories {
mavenCentral()
google()
jcenter()
maven {
url "https://repo.mirrorfly.com/release"
}
}
}
3. Add dependencies - Add the required dependencies to app/build.gradle file.
dependencies {
implementation 'com.mirrorfly.sdk:mirrorflysdk:7.13.7'
}
4. Update Gradle properties - Add the specified line to gradle.properties to avoid library conflicts.
android.enableJetifier=truedssdcs
5. Add permissions - Open AndroidManifest.xml and add the required permissions for calls.
6. Initialize the Chat SDK for Calls
Make sure all requirements are met, then call ChatManager inside onCreate() in your Application class to initialize the SDK.
ChatManager.initializeSDK("LICENSE_KEY", (isSuccess, throwable, data) -> {
if(isSuccess){
Log.d("TAG", "initializeSDK success ");
}else {
Log.d("TAG", "initializeSDK failed with reason "+data.get("message"));
}
});
ChatManager initializeSDK Function Description
Step 2: Add MyApplication
Register the MyApplication class in your AndroidManifest.xml file.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.uikitapplication">
<application
android:name=".MyApplication" // Add this line.
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
...
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Step 3: Register User
Register a user in Sandbox or Live mode, depending on your setIsTrialLicenceKey setting.
FlyCore.registerUser(USER_IDENTIFIER, (isSuccess, throwable, data ) -> {
if(isSuccess) {
Boolean isNewUser = (Boolean) data.get("is_new_user"); // true - if the current user is different from the previous session's logged-in user, false - if the same user is logging in again
String userJid = (String) data.get("userJid"); //Ex. 12345678@xmpp-preprod-sandbox.mirrorfly.com (USER_IDENTIFIER+@+domain of the chat server)
JSONObject responseObject = (JSONObject) data.get("data");
String username = responseObject.getString("username");
} else {
// Register user failed print throwable to find the exception details.
}
});
Step 4: Connect To Chat Server
Once registration succeeds, the SDK auto-connects to the chat server and manages the connection based on your app's lifecycle.
Observe Connection Events:
Set ChatConnectionListener to receive connection updates.
ChatManager.setConnectionListener(new ChatConnectionListener() {
@Override
public void onConnected() {
// Write your success logic here to navigate Profile Page or
// To Start your one-one chat with your friends
}
@Override
public void onDisconnected() {
// Connection disconnected
}
@Override
public void onConnectionFailed(@NonNull FlyException e) {
// Connection Not authorized or Unable to establish connection with server
}
@Override
public void onReconnecting() {
// Automatic reconnection enabled
}
});
Step 5: Initialize Call SDK
Add the below line inside OnCreate() to initialize the call SDK:
@Override
public void onCreate() {
super.onCreate();
//set your call activity
CallManager.setCallActivityClass(CALL_UI_ACTIVITY.class);
CallManager.setMissedCallListener((isOneToOneCall, userJid, groupId, callType, userList,CallMetaData[] callMetaDataArray) -> {
//show missed call notification
});
CallManager.setCallHelper(new CallHelper() {
@NonNull
@Override
public String getNotificationContent(@NonNull String callDirection,CallMetaData[] callMetaDataArray) {
return CallNotificationHelper.getNotificationMessage();
}
});
CallManager.setCallNameHelper(new CallNameHelper() {
@NonNull
@Override
public String getDisplayName(@NonNull String jid,CallMetaData[] callMetaDataArray) {
return ContactManager.getDisplayName(jid);
}
});
}
Setup your call activity
Add this to OnCreate() to configure call activity
<activity
android:name="YOUR_CALL_ACTIVITY"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:resizeableActivity="false"
android:screenOrientation="portrait"
android:supportsPictureInPicture="true"
android:showOnLockScreen="true"
android:turnScreenOn="true"
android:taskAffinity="call.video"
tools:targetApi="o_mr1" />
CallManager.configureCallActivity(ACTIVITY);
To remove the ongoing call notification, add this to onStart()
CallManager.bindCallService();
Add this to show the ongoing call notification
CallManager.unbindCallService();
Now generate User JID, using the following code
FlyUtils.getJid(USER_NAME)
Step 6: Making a Audio call
Required runtime permissions for audio Call
Manifest.permission.RECORD_AUDIO
Manifest.permission.READ_PHONE_STATE
Check audio call permissions
CallManager.isAudioCallPermissionsGranted();
Add below permission to show ongoing call notification
Manifest.permission.POST_NOTIFICATIONS
Make a Voice Call
Allow users to make one-to-one audio call with call metadata.
CallManager.makeVoiceCall("TO_JID",CALL_METADATA, (isSuccess, flyException) -> {
if(isSuccess){
//SDK will take care of presenting the Call UI. It will present the activity that is passed using the method `CallManager.setCallActivityClass()`
Log.d("MakeCall","call success");
}else{
if(flyException!=null){
String errorMessage = flyException.getMessage();
Log.d("MakeCall","Call failed with error: "+errorMessage);
//toast error message
}
}
});
Make a Group Voice Call
Allow users to make group video calls with call metadata.
CallManager.makeGroupVoiceCall(JID_LIST, GROUP_ID,CALL_METADATA, new CallActionListener() {
@Override
public void onResponse(boolean isSuccess, @Nullable FlyException flyException) {
if (isSuccess) {
//SDK will take care of presenting the Call UI. It will present the activity that is passed using the method `CallManager.setCallActivityClass()`
Log.d("MakeCall", "call success");
} else {
if (flyException != null) {
String errorMessage = flyException.getMessage();
Log.d("MakeCall", "Call failed with error: " + errorMessage);
//toast error message
}
}
}
});
Step 8: Adding Other Calling Features
Add participants to the call
Using the below method, you can enable to add users to the ongoing calls.
CallManager.inviteUsersToOngoingCall(JID_LIST, new CallActionListener() {
@Override
public void onResponse(boolean isSuccess, @Nullable FlyException flyException) {
}
});
Answer the incoming call
Add the below method, so whenever the user presses the accept button from your call UI it answers the call and notifies the caller.
CallManager.answerCall((isSuccess, flyException) -> {
if(isSuccess){
Log.d("AnswerCall","call answered success");
}else{
if(flyException!=null){
String errorMessage = flyException.getMessage();
Log.d("AnswerCall","Call answered failed with error: "+errorMessage);
//toast error message
}
}
});
Decline the incoming call
Add the method below to decline the incoming call.
CallManager.declineCall();
Disconnect the ongoing call
Add the below method to disconnect the ongoing call.
CallManager.disconnectCall();
Step 9: Send/Receive Message
How to Send a One-to-One Message
Use the provided code to send a one-to-one text message.
TextMessage textMessage = new TextMessage();
textMessage.setToId(TO_JID);
textMessage.setMessageText(TEXT);
FlyMessenger.sendTextMessage(textMessage, (isSuccess, error, chatMessage) -> {
if (isSuccess) {
// you will get the message sent success response
}
});
How to Receive A One-to-One Message
Set up MessageEventsListener to receive and observe incoming messages.
ChatEventsManager.setupMessageEventListener(new MessageEventsListener() {
@Override
public void onMessageReceived(@NotNull ChatMessage message) {
//called when a new message is received
}
});
That's it, your chat app can now send and receive messages. From here, you can add more chat features as needed.
Final Verdict
No longer do enterprises have to choose between convenience and control. With the right chat SDK, enterprises can build their own Zoom app that is secure, branded and tailored to your workflow, all without recurring subscription fees or compliance headaches




Top comments (0)