Copy-Paste Guide

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.

Open API reference

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.

npm install @ringnity/server-api express
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.

Paste before </body>
<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.

npm install @ringnity/web-sdk
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.

flutter pub add ringnity_flutter_sdk
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.

Download the Kotlin SDK source zip, extract it into your Android workspace, then include it as a local Gradle project during pilots.
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.

Download the Swift SDK source zip, then add Package.swift through Swift Package Manager. Product: RingnitySDK.
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.

npm install @ringnity/react-native-sdk
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>.