Web Visitor SDK Implementation

Add visitor chat, AI, voice, and video support to any website

This guide is for customer-facing websites, web apps, CRM panels, WordPress pages, and ecommerce storefronts. It covers the secure context token flow, script tag install, NPM install, Managed UI, Headless UI, browser permissions, and visitor features.

Use the Web SDK for public websites, SaaS apps, marketplaces, banking portals, hotel and travel booking pages, tourism sites, customer portals, or internal CRM panels. It includes a Managed UI for fast installs and a Headless object model for custom screens while platform-specific transport details stay internal.

client-website.com
1

Website loads SDK

2

SDK validates origin

3

Support window opens

Support

Ringnity Support

Online now

Hi, how can we help today?
I need help with my order.
You can continue by chat, voice, or video.
Type a message...

Overview

What this SDK is for

Use this Web SDK when a customer-facing website needs Ringnity support experiences for visitors, customers, members, patients, students, buyers, travelers, or other end users.

Visitor / Client Website

This page covers customer-facing websites: chat, AI chat, browser voice call, browser video call, web push, ringtone, Managed UI, and Headless UI.

Backend Optional, Recommended

Anonymous websites can bootstrap with a slug. Trusted identity, customer metadata, and private user data should come from a customer backend that issues a short-lived context token.

Not Agent / Admin SDK

Agent inbox, agent presence, admin readiness, billing, and reports belong to separate Agent/Admin SDK documentation. Do not use this page for internal workspace apps.

Server and SDK topology

Architecture that must be set up

The SDK is embedded in the website, but secret-bearing work belongs to the customer backend. Simple anonymous installs can use a tenant slug; trusted customer identity should use a short-lived context token.

Website

Visitor site with Ringnity Web SDK

No Server API key
Uses slug/apiKey/contextToken

Customer Backend

Only needed for trusted visitor identity

Validates app user
Creates context token

Ringnity Cloud

Tenant, chat, calls, AI, realtime

Validates origin
Issues SDK session
1. Website loads SDK
2. SDK bootstraps tenant
3. Optional backend context
4. SDK session returns
5. Chat/call opens

Do not put the Server API key in browser code

Browser code may contain the tenant slug or public widget key, but it must not contain the Server API key, platform owner credentials, or long-lived admin credentials.

Overview

Visitor capability map

These are the visitor/client website capabilities currently documented for the Web SDK. They are customer-facing features, not agent/admin workspace features.

Managed Support UI

Ready

Render the floating support launcher and iframe support window from a simple script tag.

Headless Chat

Ready

Create conversations and messages from your own website or CRM panel.

AI Chat

Progressive

Ask AI over HTTP and render the completed response through progressive chunk callbacks.

Voice Call

Ready

Managed UI opens the production voice flow; Headless createSession records lifecycle metadata only.

Video Call

Ready

Managed UI opens the production video flow; Headless createSession does not provide WebRTC signalling.

Web Push and Ringtone

Ready

Register browser push tokens and play foreground incoming call ringtone behavior.

Package shape

The Web SDK package includes browser runtime, managed launcher, iframe support window, object APIs, diagnostics, and generated REST operation wiring.

text
@ringnity/web-sdk
  TypeScript object model, managed launcher, managed iframe window,
  Headless SDK objects, generated operation layer, and browser bundle.

browser runtime
  Tenant bootstrap, domain validation, SDK session exchange,
  chat/AI/call methods, push registration, ringtone, and diagnostics.

Scalar API Reference

Use Scalar when backend developers need exact endpoint fields, schemas, auth requirements, status codes, or generated client details.

Open Scalar

Server

Server requirements

The backend endpoint is the trust boundary when the website needs verified customer identity. It authenticates the website user, creates Ringnity context, and returns only a short-lived context token to browser code.

Backend checklist

Ringnity tenant slug, for example your-slug.
Allowed website domain configured for the tenant.
Server API key created from the tenant dashboard when trusted visitor identity is needed.
Customer backend endpoint, for example POST /ringnity/context-token.
HTTPS in staging and production.
No Server API key inside JavaScript bundles, HTML, browser storage, logs, or tag manager snippets.

Web compatibility

RuntimeModern browser with fetch, Promise, and iframe support
Install modeScript tag, NPM package, or universal widget
Package@ringnity/web-sdk for apps with a build step
DomainOrigin must match tenant allowed domains
Managed UIFloating launcher or mounted iframe support window
Headless UICustom buttons and product screens using SDK objects
CallsBrowser microphone and camera permission for voice/video
PushOptional web push token registration when browser push is configured
RingtoneOptional browser tone, vibration, or custom soundUrl

Server

Create the context token endpoint

Use this route when the website needs trusted customer identity or private metadata. The browser receives a contextToken, then the Web SDK exchanges it for a short-lived SDK session internally.

.env
RINGNITY_API_BASE_URL=https://api.ringnity.com
RINGNITY_TENANT_SLUG=your-slug
RINGNITY_SERVER_API_KEY=sk_live_or_test_from_ringnity_dashboard
server.js
import express from "express";
import { RingnityServerApi } from "@ringnity/server-api";

const app = express();
app.use(express.json());

const ringnity = RingnityServerApi.create({
  apiBaseUrl: process.env.RINGNITY_API_BASE_URL,
  apiKey: process.env.RINGNITY_SERVER_API_KEY,
});

async function requireCustomer(req) {
  // Replace this with your own session, JWT, or account validation.
  return {
    id: req.body.userId,
    name: req.body.name,
    email: req.body.email,
  };
}

app.post("/ringnity/context-token", async (req, res) => {
  const customer = await requireCustomer(req);

  const context = await ringnity.tokens.visitorContext({
    visitor: {
      externalId: customer.id,
      name: customer.name,
      email: customer.email,
    },
    metadata: {
      source: "web-app",
      page: req.body.page,
      plan: req.body.plan,
    },
    expiresIn: 900,
  });

  res.json({
    contextToken: context.contextToken,
    expiresIn: 900,
    tokenType: "Ringnity-Context",
  });
});

app.listen(3000);

Website receives

json
{
  "contextToken": "eyJhbGciOi...",
  "expiresIn": 900,
  "tokenType": "Ringnity-Context"
}

Use contextToken in browser

TypeScript
async function fetchRingnityContextToken() {
  const response = await fetch('/ringnity/context-token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    credentials: 'include',
    body: JSON.stringify({
      userId: currentUser.id,
      name: currentUser.name,
      email: currentUser.email,
      page: window.location.pathname
    })
  });

  if (!response.ok) {
    throw new Error('Ringnity context token request failed');
  }

  return response.json();
}

const { contextToken } = await fetchRingnityContextToken();

const ringnity = await Ringnity.create({
  slug: 'your-tenant-slug',
  contextToken,
  visitor: {
    externalId: currentUser.id,
    name: currentUser.name,
    email: currentUser.email
  },
  managedUi: {
    showLauncher: false,
    closeOnEscape: true
  }
});

Server

Token lifecycle

Move from page load to support session with a predictable flow. The website can create a new SDK client or refresh state when identity, tenant config, or support state changes.

Bootstrap

SDK loads tenant branding, services, plan availability, and origin rules.

Session

SDK creates a short-lived visitor session using slug, apiKey, visitor data, and optional contextToken.

Refresh

Call refresh or recreate the client after token expiry, login/logout, tenant config changes, or app navigation.

Server

Security checklist

Use this checklist before adding Ringnity to a real customer website.

Allowed domain is configured for the tenant before production traffic starts.
Server API key is stored only in backend environment variables or secret manager.
Backend verifies the website user before issuing a context token.
Token endpoint is protected by HTTPS, authentication, CSRF strategy, and rate limiting.
Logs do not print full context token, SDK session token, Server API key, or Ringnity credential values.
Production snippets point to the production customer backend and production Ringnity API.

Web Setup

Download what you need

Current downloadable SDK bundle: 0.2.0-beta.0. Use the Web SDK for browser integration and the Server API SDK for the customer backend context-token endpoint.

Web Setup

Script tag install

Use this when the customer wants the fastest install on plain HTML, WordPress, Shopify, static sites, or pages without a frontend build step. Ringnity renders the default floating support launcher.

HTML
<script src="https://api.ringnity.com/sdk/ringnity.js"></script>
<script>
  Ringnity.init({
    slug: 'your-tenant-slug',
    visitor: {
      name: 'Website Visitor'
    },
    managedUi: {
      defaultService: 'chat',
      launcherText: 'Support',
      launcherLabel: 'Open Ringnity support',
      position: 'bottom-right',
      closeOnEscape: true
    },
    notifications: {
      incomingCall: {
        soundUrl: '/sounds/ringnity-default.mp3',
        vibrate: true
      }
    },
    onStatusChange: function (status) {
      console.log('Ringnity status:', status);
    },
    onReady: function (context) {
      console.log('Ringnity is ready', context.services);
    },
    onError: function (error) {
      console.warn(error.code, error.message);
    }
  });
</script>

Web Setup

NPM install

Use this in React, Next.js, Vue, Nuxt, Svelte, or any frontend app with a build step. During preview, install the downloaded local package.

bash
npm install ./ringnity-web-sdk

# or, after public package publishing
npm install @ringnity/web-sdk
TypeScript
import { Ringnity } from '@ringnity/web-sdk';

const ringnity = await Ringnity.create({
  slug: 'your-tenant-slug',
  visitor: {
    externalId: 'customer-123',
    name: 'Aira Kirana'
  },
  managedUi: {
    showLauncher: false,
    closeOnEscape: true
  }
});

document.querySelector('#chatButton')?.addEventListener('click', () => {
  void ringnity.openChat();
});

await ringnity.devices.registerPushToken({
  platform: 'web',
  provider: 'fcm',
  token: webPushToken,
  audience: 'customer',
  externalId: 'customer-123'
});

Managed UI

Managed UI overview

Launch a complete Ringnity support window from the website. Use Managed UI when the website wants Ringnity to render the support launcher and window.

Fast install

Use Ringnity.init with autoRender enabled and showLauncher true or default.

Controlled launch

Use Ringnity.create with showLauncher false, then call openChat, openVoice, or openVideo from your own buttons.

Managed UI

CRM panel mounting

Use this style when Ringnity should live inside a CRM sidebar instead of a floating launcher. The customer app controls where the support window appears.

HTML
<aside id="support-panel"></aside>

<script type="module">
  import { Ringnity } from '@ringnity/web-sdk';

  const ringnity = await Ringnity.create({
    slug: 'your-tenant-slug',
    contextToken,
    managedUi: {
      showLauncher: false,
      defaultService: 'chat',
      container: '#support-panel'
    }
  });

  ringnity.mount('#support-panel');
  await ringnity.openChat();

  // When the CRM tab closes:
  ringnity.unmount();
</script>

Managed UI

Included features

With Managed UI, feature implementation is bundled into the Ringnity launcher and support iframe. The host website still owns domain configuration, optional context token flow, browser push setup, and page lifecycle.

Chat

Managed UI renders the visitor conversation, message list, composer, and basic chat state.

AI Chat

AI surface is available when the tenant plan and AI readiness allow it.

Voice Call

Managed support window can expose visitor-facing browser voice call controls.

Video Call

Managed support window can expose visitor-facing browser video call controls.

Web Push

Host website still owns service worker and push token setup; SDK registers the token.

Ringtone

Managed foreground call experience can use configured browser notification preferences.

Headless UI

Headless UI overview

Use Ringnity as the support engine behind your own website screens. Headless UI is for websites that own buttons, menus, message rendering, call controls, and analytics.

You own UI

Render support buttons, message bubbles, composer, loading states, call buttons, AI response UI, and empty states in your site design.

SDK owns product logic

Use Ringnity for tenant bootstrap, session exchange, conversation creation, message send, AI, calls, push token, ringtone, and diagnostics.

Headless UI

Feature APIs

Each visitor feature can be used from custom website screens. The same SDK client powers every feature.

The examples below are for Headless UI. Managed UI uses the same product capabilities through the Ringnity support window.

Visitor Feature

Chat

Create a visitor conversation, send messages, and keep your custom website UI updated while support is active.

Managed UI

Use Ringnity.init or openChat when Ringnity should render the conversation screen.

Headless UI

Use chat.createConversation, chat.sendMessage, onMessage, and onConversationUpdated when your website owns the entire chat UI.

TypeScript
const conversation = await ringnity.chat.createConversation({
  subject: 'Need help with my order',
  metadata: {
    page: window.location.pathname
  }
});

const conversationId = conversation.conversationId;

const messageSubscription = ringnity.chat.onMessage((message) => {
  renderMessage(message);
});

const conversationSubscription = ringnity.chat.onConversationUpdated((conversation) => {
  renderConversation(conversation);
});

const message = await ringnity.chat.sendMessage({
  conversationId,
  body: 'Hi, I need help.'
});

await ringnity.chat.markRead({
  conversationId,
  messageId: message.messageId
});

// Also available: availability, activeConversation, getConversation,
// closeConversation, updateProfile, editMessage, deleteMessage, createAttachment,
// uploadAttachment, requestCall, and finalizeCallRequest. AI also exposes routing and searchKnowledge.

// Call this when the screen is destroyed or the conversation is closed.
messageSubscription.unsubscribe();
conversationSubscription.unsubscribe();

Visitor Feature

AI Chat

Ask AI and render the answer in your own component, while plan readiness and tenant configuration stay controlled by Ringnity.

Managed UI

Use Managed UI when the AI surface should appear inside the Ringnity support window.

Headless UI

Use ai.streamChat for progressive rendering after the HTTP response completes; it is not transport streaming.

TypeScript
await ringnity.ai.streamChat(
  {
    conversationId,
    message: 'Summarize the customer issue.'
  },
  {
    onStart: (event) => showAiThinking(event.data.messageId),
    onDelta: (event) => appendToChat(event.data.delta),
    onDone: (event) => setAiAnswer(event.data.message),
    onError: (event) => showAiError(event.data.message)
  }
);

Visitor Feature

Voice Call

Managed UI opens the production browser call. Headless calls.createSession records lifecycle metadata and does not implement WebRTC signalling.

Managed UI

Use openVoice when Ringnity should render call controls.

Headless UI

Use calls.createSession for metadata only; use Managed UI or provide a separate WebRTC signalling transport for live media.

TypeScript
// Managed call window
await ringnity.openVoice();

// Or create a call session for a custom flow
const call = await ringnity.calls.createSession({
  conversationId,
  type: 'audio',
  direction: 'inbound',
  metadata: {
    source: 'website-support-button'
  }
});

Visitor Feature

Video Call

Managed UI opens the production browser video flow. Headless createSession records metadata only.

Managed UI

Use openVideo when Ringnity should render video call controls.

Headless UI

Use calls.createSession with type video for metadata only; it does not negotiate WebRTC media.

TypeScript
// Managed call window
await ringnity.openVideo();

// Or create a video call session for a custom flow
const call = await ringnity.calls.createSession({
  conversationId,
  type: 'video',
  direction: 'inbound'
});

await ringnity.calls.endSession(call.id, {
  reason: 'visitor_closed_page'
});

Visitor Feature

Web Push Notification

Register the browser push token so Ringnity can target the visitor when background delivery is needed.

Managed UI

Managed UI benefits from the same registered web push token but the host website still owns service worker and permission setup.

Headless UI

Use devices.registerPushToken after the website obtains a browser push token from FCM or another push provider.

TypeScript
await ringnity.devices.registerPushToken({
  platform: 'web',
  provider: 'fcm',
  token: webPushToken,
  audience: 'customer',
  externalId: 'customer-123',
  appId: 'com.example.website'
});

// Later, if the browser token is revoked:
await ringnity.devices.unregisterPushToken({
  token: webPushToken,
  provider: 'fcm'
});

Visitor Feature

Ringtone

Play or preview incoming call sounds while the page is active, then stop when the call is answered, declined, or expired.

Managed UI

Managed foreground calls can use configured notification preferences.

Headless UI

Use notifications.previewRingtone, playIncomingRingtone, and stopRingtone when your website owns the call invitation UI.

TypeScript
ringnity.notifications.updatePreferences({
  incomingCall: {
    enabled: true,
    soundUrl: '/sounds/ringnity-default.mp3',
    volume: 0.85,
    vibrate: true
  }
});

await ringnity.notifications.previewRingtone({ durationMs: 2500 });
await ringnity.notifications.playIncomingRingtone();

// Stop when the visitor answers, declines, or the invitation expires.
ringnity.notifications.stopRingtone();

Operations

Lifecycle

Keep setup predictable by treating the SDK as a runtime service owned by a page, app shell, CRM panel, or support provider.

1

Website loads the SDK by script tag or NPM import.

2

SDK bootstraps tenant branding, services, and allowed domain state.

3

If trusted identity is needed, website fetches a context token from the customer backend.

4

SDK creates a short-lived visitor session using slug, apiKey, visitor data, and optional contextToken.

5

Website chooses Managed UI or Headless UI.

6

Visitor opens chat, AI, voice, or video.

7

Website subscribes to callbacks only while its UI needs them.

8

Website refreshes SDK state after tenant config changes or when a session expires.

9

Website closes/unmounts/destroys the SDK when the current page no longer needs it.

Command cheat sheet

Ringnity.init(options)

Create the SDK and render the default managed launcher.

await Ringnity.init({ slug: 'your-slug' });
Ringnity.create(options)

Create the SDK without auto-rendering UI.

const ringnity = await Ringnity.create({ slug: 'your-slug' });
openChat()

Open the chat or AI chat support window.

chatButton.onclick = () => ringnity.openChat();
openVoice()

Open the voice call support window.

voiceButton.onclick = () => ringnity.openVoice();
openVideo()

Open the video call support window.

videoButton.onclick = () => ringnity.openVideo();
chat.createConversation()

Create a headless support conversation for your own UI.

const conversation = await ringnity.chat.createConversation({ subject: 'Need help' });
chat.sendMessage()

Send a visitor message from your custom chat screen.

await ringnity.chat.sendMessage({ conversationId, body: 'Hi' });
ai.streamChat()

Receive AI response events through callbacks or async iteration.

await ringnity.ai.streamChat({ message }, { onDelta: event => append(event.data.delta) });
calls.createSession()

Create an audio or video call session for custom flows.

const call = await ringnity.calls.createSession({ conversationId, type: 'audio' });
devices.registerPushToken()

Register an optional browser push token.

await ringnity.devices.registerPushToken({ platform: 'web', token: webPushToken });
mount(target)

Render the managed support window inside a CRM panel or page element.

ringnity.mount('#support-panel');
unmount() / destroy()

Remove mounted UI or fully tear down the SDK instance.

ringnity.unmount(); ringnity.destroy();

Operations

Troubleshooting

Most website failures come from allowed-domain mismatch, missing tenant context, stale snippets, or browser permission setup.

SDK_ORIGIN_NOT_ALLOWED

Check that the website origin is included in tenant allowed domains. Remember that http, https, root domain, and subdomain are different origins.

MISSING_TENANT

Check that slug or apiKey is present and points to the intended tenant.

401 or expired session

Fetch a fresh contextToken when using trusted identity, then recreate the SDK client or call refresh.

No call audio/video

Check browser microphone/camera permissions, iframe allow attributes, and whether the feature is available in the tenant plan.

No push notification

Confirm service worker registration, browser permission, FCM token, appId, audience customer, and externalId.

Operations

Origin check

If the Web SDK works in dashboard preview but not on the customer website, check whether the website domain is saved in tenant allowed domains.

bash
curl -H "Origin: https://client-domain.com" \
  "https://api.ringnity.com/api/sdk/bootstrap?slug=your-tenant-slug&debug=true"

Expected healthy event order

text
onReady
onServiceSelected chat
onOpen chat

Operations

Full example

Use Managed UI for the fastest install. Use Headless UI when the product screen must follow the host website design system.

HTML
<button id="chatButton">Chat with us</button>
<button id="voiceButton">Start voice call</button>
<button id="videoButton">Start video call</button>

<script src="https://api.ringnity.com/sdk/ringnity.js"></script>
<script>
  async function fetchRingnityContextToken() {
    const response = await fetch('/ringnity/context-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include',
      body: JSON.stringify({
        page: window.location.pathname
      })
    });
    return response.json();
  }

  async function setupRingnity() {
    const context = await fetchRingnityContextToken();

    const ringnity = await Ringnity.create({
      slug: 'your-tenant-slug',
      contextToken: context.contextToken,
      visitor: {
        externalId: 'crm-customer-123',
        name: 'Aira Kirana',
        email: 'aira@example.com'
      },
      metadata: {
        source: 'pricing-page'
      },
      managedUi: {
        showLauncher: false,
        closeOnEscape: true
      },
      onStatusChange: function (status) {
        console.log('Ringnity status:', status);
      },
      onError: function (error) {
        console.warn(error.code, error.message);
      }
    });

    document.querySelector('#chatButton').onclick = function () {
      ringnity.openChat();
    };

    document.querySelector('#voiceButton').onclick = function () {
      ringnity.openVoice();
    };

    document.querySelector('#videoButton').onclick = function () {
      ringnity.openVideo();
    };
  }

  setupRingnity();
</script>

Last updated: June 30, 2026.

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>.