Managed UI and Headless SDK
Ringnity supports two customer styles. Managed UI is for teams that want the fastest setup. Headless SDK is for teams that need their own CRM, mobile app, or product UI.
Managed UI
Ringnity draws the launcher, chat, incoming call, active call, AI handoff, ringtone state, and waiting UI. This is the best path for quick implementation.
- Best for websites, pilots, and small teams.
- Client writes less code.
- Ringnity controls UI behavior and edge states.
Headless SDK
The customer draws the UI. Ringnity manages tokens, sessions, entitlement, realtime events, call state, media adapters, push token registration, and ringtone.
- Best for CRM, enterprise apps, and custom product UX.
- Client owns layout and navigation.
- Ringnity hides transport and backend complexity.
Both modes use the same token rule
Browser and mobile apps request short-lived runtime tokens from the customer backend. Only the customer backend stores the Server API key.
import express from "express";
import { RingnityServerApi } from "@ringnity/server-api";
const app = express();
app.use(express.json());
const ringnity = RingnityServerApi.create({
apiKey: process.env.RINGNITY_SERVER_API_KEY
});
app.post("/ringnity/runtime-token", async (req, res) => {
const token = await ringnity.tokens.createVisitorContextToken({
visitor: {
externalId: req.body.userId,
name: req.body.name,
email: req.body.email
},
metadata: { source: "customer-app" },
expiresIn: 900
});
res.json(token);
});
app.listen(3000);Website Examples
Managed UI website
Available now through the hosted launcher and support window.
<script src="https://api.ringnity.com/sdk/ringnity.js"></script>
<script>
Ringnity.init({
slug: "your-tenant-slug",
visitor: {
name: "Demo Customer"
},
notifications: {
incomingCall: {
soundUrl: "/sounds/ringnity-default.mp3",
vibrate: true
}
}
});
</script>Headless website or CRM buttons
Available now for custom buttons while Ringnity owns the runtime.
import { Ringnity } from "@ringnity/web-sdk";
const ringnity = await Ringnity.create({
slug: "your-tenant-slug",
tokenProvider: async () => {
const response = await fetch("/ringnity/runtime-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
userId: "customer-123",
name: "Demo Customer"
})
});
return response.json();
}
});
document.querySelector("#chat")?.addEventListener("click", () => {
void ringnity.openChat();
});
document.querySelector("#voice")?.addEventListener("click", () => {
void ringnity.openVoice();
});
document.querySelector("#video")?.addEventListener("click", () => {
void ringnity.openVideo();
});Mobile Examples
Current mobile status
| SDK | Status | Available foundation |
|---|---|---|
| Flutter | Managed UI preview + Headless | Managed chat, AI chat, call controls, video shell, push token, foreground ringtone, Android/iOS adapter. |
| React Native | Managed UI preview + Headless | Managed chat, AI chat, call controls, video shell, push token, foreground ringtone, Android/iOS bridge. |
| Swift iOS | Managed UI preview + Headless | SwiftUI managed screen, SPM package, URLSession object model, APNs token registration, AVAudioSession adapter. |
| Kotlin Android | Managed UI preview + Headless | Native Android managed view, Gradle package, OkHttp object model, FCM token registration, Android audio adapter. |
Flutter Managed UI preview
Available for fast Flutter installs.
final callAdapter = RingnityPlatformCallAdapter(); final ringnityConfig = RingnityConfig( tokenProvider: fetchRingnityToken, callMode: RingnityCallMode.basic, mediaAdapter: callAdapter, notificationAdapter: callAdapter, ); return RingnityWidget( config: ringnityConfig, mode: RingnityWidgetMode.full, conversationSubject: 'Mobile customer support', );
Flutter Headless SDK
Available foundation for custom Flutter screens.
final callAdapter = RingnityPlatformCallAdapter();
final ringnity = await Ringnity.create(
RingnityConfig(
tokenProvider: fetchRingnityToken,
callMode: RingnityCallMode.basic,
mediaAdapter: callAdapter,
notificationAdapter: callAdapter,
notifications: RingnityNotificationPreferences(
incomingCall: RingnityRingtoneOptions(
soundName: 'ringnity_default',
vibrate: true,
),
),
),
);
final conversation = await ringnity.chat.createConversation(
subject: 'Need help',
);
final conversationId = conversation['conversationId'] as String;
await ringnity.notifications.previewRingtone();
final call = await ringnity.calls.startVideo(
conversationId: conversationId,
);
await ringnity.notifications.stopRingtone();
await call.mute();
await call.setVideoEnabled(false);
await call.end();React Native Managed UI preview
Available for fast React Native installs.
import {
RingnityWidget,
createRingnityReactNativeCallAdapter,
} from "@ringnity/react-native-sdk";
const mediaAdapter = createRingnityReactNativeCallAdapter();
const config = {
tokenProvider: fetchRingnityToken,
callMode: "basic",
mediaAdapter,
notificationAdapter: mediaAdapter
};
export function SupportScreen() {
return (
<RingnityWidget
config={config}
mode="full"
conversationSubject="Mobile customer support"
/>
);
}React Native Headless SDK
Available foundation for custom React Native screens.
const mediaAdapter = createRingnityReactNativeCallAdapter();
const ringnity = await Ringnity.create({
tokenProvider: fetchRingnityToken,
callMode: "basic",
mediaAdapter,
notificationAdapter: mediaAdapter,
notifications: {
incomingCall: {
soundName: "ringnity_default",
vibrate: true
}
}
});
const conversation = await ringnity.chat.createConversation({
subject: "Need help"
});
const conversationId = readConversationId(conversation);
await ringnity.notifications.previewRingtone();
const call = await ringnity.calls.startVideo({ conversationId });
await ringnity.notifications.stopRingtone();
await call.mute();
await call.setVideoEnabled(false);
await call.end();Native Managed UI preview
SwiftUI and Android native Managed UI are available as preview surfaces for device QA.
import RingnitySDK
import SwiftUI
struct SupportView: View {
let callAdapter = RingnityAppleCallAdapter()
var body: some View {
RingnityView(
config: RingnityConfig(
tokenProvider: fetchRingnityToken,
callMode: .basic,
mediaAdapter: callAdapter,
notificationAdapter: callAdapter
),
mode: .full,
conversationSubject: "iOS customer support"
)
}
}
val callAdapter = RingnityAndroidCallAdapter(applicationContext)
val ringnity = Ringnity.create(
RingnityConfig(
tokenProvider = ::fetchRingnityToken,
callMode = RingnityCallMode.BASIC,
mediaAdapter = callAdapter,
notificationAdapter = callAdapter,
)
)
val supportView = RingnityAndroidManagedView(this)
supportView.bind(
ringnity = ringnity,
conversationSubject = "Android customer support",
)
setContentView(supportView)Video rendering contract
Managed UI draws local preview, remote video, and controls. Headless SDK lets the customer place video views in their own layout.
// Available renderer injection primitives.
RingnityWidget(
config: ringnityConfig,
mode: RingnityWidgetMode.call,
remoteVideo: YourRemoteVideoView(),
localPreview: YourLocalPreviewView(),
);
<RingnityWidget
config={config}
mode="call"
remoteVideo={<YourRemoteVideoView />}
localPreview={<YourLocalPreviewView />}
/>
await call.mute();
await call.unmute();
await call.setVideoEnabled(false);
await call.end();Recommended build order
- Keep Web Managed UI and Web Headless stable.
- Polish CRM panel mounting for Web Headless and Managed UI.
- Polish Flutter and React Native Managed UI through device QA.
- Run SwiftUI and Android native Managed UI preview through device QA.
- Add deeper native media renderer adapters after device QA.
- Formalize CRM Agent widget for web.
- Add deeper CallKit and Android Telecom support for background or killed-state calls.
Step-by-step setup
Copy-paste setup guide for each SDK.
Flutter SDK
Current Flutter foundation and examples.
React Native SDK
Current React Native foundation and examples.