Implement Ringnity SDK Step by Step
Start with the customer backend token endpoint, then add the SDK for the customer surface. Use the examples below as a first working integration.
Server keys stay private
Only the customer backend uses RINGNITY_SERVER_API_KEY.
Runtime tokens are short-lived
Web and mobile apps receive temporary tokens from the customer backend.
Mobile readiness is gated
CI validates Android 16 KB page-size policy and Swift Package Manager support.
Your install variables
If you are logged in, tenant slug and public widget key are filled from your account. Server API keys are never auto-rendered in the page; generate or renew them from the SDK Variables dialog and copy them once.
RINGNITY_TENANT_SLUG=your-tenant-slug RINGNITY_PUBLIC_WIDGET_KEY=PUBLIC_WIDGET_KEY RINGNITY_SERVER_API_KEY=generate_in_sdk_variables_dialog
<script src="https://api.ringnity.com/widget/widget.js?apiKey=PUBLIC_WIDGET_KEY"></script> https://visitor.ringnity.com/?apiKey=PUBLIC_WIDGET_KEY
1. Customer Backend
Create token endpoints first. Browser and mobile SDKs call this backend; this backend calls Ringnity with 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.post("/ringnity/agent-token", async (req, res) => {
const token = await ringnity.tokens.createAgentToken({
username: req.body.username,
expiresIn: 900,
});
res.json(token);
});
app.post("/ringnity/admin-token", async (req, res) => {
const token = await ringnity.tokens.createAdminToken({
username: req.body.username,
expiresIn: 900,
});
res.json(token);
});
app.listen(3000);Keep RINGNITY_SERVER_API_KEY in server env only.
2. Website Script Tag
Use this when the customer only needs the default visitor launcher.
<script src="https://api.ringnity.com/sdk/ringnity.js"></script>
<script>
Ringnity.init({
slug: "your-tenant-slug",
managedUi: {
defaultService: "chat",
launcherText: "Support",
launcherLabel: "Open Ringnity support"
},
onStatusChange: function (status) {
console.log("Ringnity status:", status);
}
});
</script>3. Web SDK Custom UI
Use this for custom website or CRM buttons while Ringnity handles runtime session and entitlement checks.
import { Ringnity } from "@ringnity/web-sdk";
const ringnity = await Ringnity.create({
slug: "your-tenant-slug",
notifications: {
incomingCall: {
soundUrl: "/sounds/ringnity-default.mp3",
vibrate: true
}
},
managedUi: {
showLauncher: false,
closeOnEscape: true
},
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",
email: "customer@example.com",
}),
});
return response.json();
},
});
document.querySelector("#chat").addEventListener("click", () => {
ringnity.openChat();
});
document.querySelector("#voice").addEventListener("click", () => {
ringnity.openVoice();
});
document.querySelector("#video").addEventListener("click", () => {
ringnity.openVideo();
});4. Flutter SDK
Use this for Flutter apps on Android, iOS, web, and desktop.
import 'package:ringnity_flutter_sdk/ringnity_flutter_sdk.dart';
final callAdapter = RingnityPlatformCallAdapter();
final ringnity = await Ringnity.create(
RingnityConfig(
apiBaseUrl: 'https://api.ringnity.com',
tokenProvider: () async {
final response = await yourBackend.post('/ringnity/runtime-token');
return RingnityRuntimeToken(
token: response.data['token'] as String,
expiresIn: response.data['expiresIn'] as int,
);
},
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 with my order',
);
final conversationId = conversation['conversationId'] as String;
final subscription = await ringnity.chat.subscribe(conversationId);
await ringnity.chat.sendMessage(
conversationId: conversationId,
body: 'Hi, I need help.',
);
await for (final event in ringnity.ai.streamChat(
conversationId: conversationId,
message: 'Summarize this issue.',
)) {
if (event['type'] == 'ai.message.delta') {
final data = event['data'] as Map<String, Object?>;
print(data['delta']);
}
}
final permissionState = await callAdapter.permissions(video: true);
if (permissionState.canStartVideo) {
await ringnity.notifications.previewRingtone();
final call = await ringnity.calls.startVideo(
conversationId: conversationId,
);
await ringnity.notifications.stopRingtone();
await call.mute();
await call.end();
}
subscription.unsubscribe();
await ringnity.dispose();Includes Android/iOS call adapter bridge and bundles no Android native shared libraries, so Android 16 KB page-size support is clean for this SDK layer.
5. Android Kotlin SDK
Use this for native Android apps with Kotlin coroutines.
import com.ringnity.sdk.Ringnity
import com.ringnity.sdk.RingnityConfig
import com.ringnity.sdk.RingnityCallMode
import com.ringnity.sdk.RingnityNotificationPreferences
import com.ringnity.sdk.RingnityRingtoneOptions
import com.ringnity.sdk.RingnityRuntimeToken
import com.ringnity.sdk.android.RingnityAndroidCallAdapter
import com.ringnity.sdk.android.RingnityAndroidManagedView
val callAdapter = RingnityAndroidCallAdapter(applicationContext)
val ringnity = Ringnity.create(
RingnityConfig(
tokenProvider = {
val token = customerBackend.fetchRuntimeToken()
RingnityRuntimeToken(
token = token.token,
expiresIn = token.expiresIn
)
},
callMode = RingnityCallMode.BASIC,
mediaAdapter = callAdapter,
notificationAdapter = callAdapter,
notifications = RingnityNotificationPreferences(
incomingCall = RingnityRingtoneOptions(
soundName = "ringnity_default",
vibrate = true
)
)
)
)
// Easy / Managed UI:
val supportView = RingnityAndroidManagedView(this)
supportView.setRemoteVideoView(remoteVideoView)
supportView.setLocalPreviewView(localPreviewView)
supportView.bind(
ringnity = ringnity,
conversationSubject = "Android support"
)
setContentView(supportView)
// Headless:
val readiness = ringnity.account.readiness()
val conversation = ringnity.chat.createConversation(
subject = "Android support"
)
val conversationId = conversation["conversationId"] as String
ringnity.chat.sendMessage(
conversationId = conversationId,
body = "Hi, I need help."
)
ringnity.ai.streamChat(
message = "Summarize this issue.",
conversationId = conversationId
).collect { event ->
println(event)
}
val permissionState = callAdapter.permissions(video = true)
if (permissionState.canStartVideo) {
ringnity.notifications.previewRingtone()
val call = ringnity.calls.startVideo(conversationId = conversationId)
ringnity.notifications.stopRingtone()
call.mute()
call.end()
}Includes the Android call adapter module and ships no prebuilt .so files, so Android 16 KB page-size support remains clean.
6. iOS Swift SDK
Use this for native iOS apps through Swift Package Manager.
import RingnitySDK
import SwiftUI
// Easy / Managed UI:
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 support",
remoteVideo: { YourRemoteVideoView() },
localPreview: { YourLocalPreviewView() }
)
}
}
let callAdapter = RingnityAppleCallAdapter()
// Headless:
let ringnity = try await Ringnity.create(
RingnityConfig(
tokenProvider: {
let token = try await customerBackend.fetchRuntimeToken()
return RingnityRuntimeToken(
token: token.token,
expiresIn: token.expiresIn
)
},
callMode: .basic,
notifications: RingnityNotificationPreferences(
incomingCall: RingnityRingtoneOptions(
soundName: "ringnity_default",
vibrate: true
)
),
mediaAdapter: callAdapter,
notificationAdapter: callAdapter
)
)
let readiness = try await ringnity.account.readiness()
let conversation = try await ringnity.chat.createConversation(
subject: "iOS support"
)
let conversationId = conversation["conversationId"] as! String
try await ringnity.chat.sendMessage(
conversationId: conversationId,
body: "Hi, I need help."
)
for try await event in ringnity.ai.streamChat(
message: "Summarize this issue.",
conversationId: conversationId
) {
print(event)
}
let microphoneGranted = await RingnityAppleCallAdapter.requestMicrophonePermission()
let cameraGranted = await RingnityAppleCallAdapter.requestCameraPermission()
if microphoneGranted && cameraGranted {
try await ringnity.notifications.previewRingtone()
let call = try await ringnity.calls.startVideo(conversationId: conversationId)
try await ringnity.notifications.stopRingtone()
try await call.mute()
try await call.end()
}Swift Package Manager is supported through Package.swift, with the iOS AVAudioSession call adapter included.
7. React Native SDK
Use this for cross-platform apps that share TypeScript logic.
import {
Ringnity,
createRingnityReactNativeCallAdapter,
} from "@ringnity/react-native-sdk";
const mediaAdapter = createRingnityReactNativeCallAdapter();
const ringnity = await Ringnity.create({
tokenProvider: async () => {
const response = await fetch("https://your-backend.example.com/ringnity/runtime-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
userId: "customer-123",
name: "Demo Customer",
email: "customer@example.com",
}),
});
return response.json();
},
callMode: "basic",
mediaAdapter,
notificationAdapter: mediaAdapter,
notifications: {
incomingCall: {
soundName: "ringnity_default",
vibrate: true,
},
},
});
const conversation = await ringnity.chat.createConversation({
subject: "React Native support",
});
const conversationId = readConversationId(conversation);
const subscription = await ringnity.chat.subscribe(conversationId);
await ringnity.chat.sendMessage({
conversationId,
body: "Hi, I need help.",
});
await ringnity.ai.streamChat(
{
conversationId,
message: "Summarize this issue.",
},
{
onDelta: (event) => console.log(event.data.delta),
onDone: (event) => console.log(event.data.message),
},
);
const permissionState = await mediaAdapter.permissions({ video: true });
if (permissionState.canStartVideo) {
await ringnity.notifications.previewRingtone();
const call = await ringnity.calls.startVideo({ conversationId });
await ringnity.notifications.stopRingtone();
await call.mute();
await call.end();
}
subscription.unsubscribe();
function readConversationId(value: unknown): string {
const data = value as {
conversationId?: string;
conversation?: { conversationId?: string };
};
const conversationId = data.conversationId ?? data.conversation?.conversationId;
if (!conversationId) throw new Error("Ringnity conversation id is missing.");
return conversationId;
}Includes Android/iOS native call module source and bundles no Android native shared libraries.
Before going live
Add the production domain in the Ringnity Developer section, confirm subscription and service status, then run the launch checklist.
Turn Your Website Into a Real-Time Call Center
Let customers call your team directly from your website, no phone numbers and no apps required. Just add one <script>.
