ChatWidgetSDK

A framework-agnostic JavaScript SDK that handles all chat communication (HTTP bot + WebSocket live-agent) so you can focus purely on building the Ul.

Installation

Use in a webpage via script tag:

<script src="https://https://dc-sdk.cloudagent.ozonetel.com/chat-widget-sdk.js"></script>
<!-- ChatWidgetSDK is now available as a global variable →→ >

Initialisation

const sdk = ChatWidgetSDK.init ({
// Required
DID: 'YOUR_DID',           // Digital campaign number
API_KEY: 'YOUR_API_KEY',   // Authentication key

// Optional - caller info sent in every request
userDetails: {
id:
phone_number:
email:
name:
},
// Optional - arbitrary key/value pairs forwarded to the bot on every request
additionalData: I},
});

ChatWidgetSDK.init() returns a configured SDK instance. Keep it in a module-level variable.

Configuration reference

User-facing (pass to ChatWidgetSDK.init())

KeyTypeRequiredDescription
DIDstringyesDigital campaign number
API_KEYstringyesAuthentication key
userDetails.idstringnoCaller ID
userDetails. phone_numberstringnoCaller phone
userDetails. emailstringnoCaller email
userDetails.namestringnoCaller name
additionalDataobjectnoArbitrary data forwarded t o bot on every request

Sending messages

All send methods are async and return Promise<void>.

Plain text

await sdk.sendMessage('Hello, I need help');

Quick reply

/ Used to answer a 'question' message - pass any text as id and title
await sdk.sendReply({ id: 'user answer', title: 'user answer' });

Menu item selection

await sdk.sendSelectedMenu({ nodeId: 'node_42', description: 'Check my order status

Form submission

await sdk.submitForm(
{ name: 'Alice' , email: '[email protected]' }, // key/value pairs
['Name: Alice', Email: [email protected]'l // display labels shown as use
) ;

File / media upload

 'file' is a browser File object (from <input type="file"> or drag-and-drop)
await sdk.sendMutilMediaMessage(file);
sdk.on( 'uploadProgress', ({ inProgress }) => showSpinner(inProgress));

Fallback message
Send a plain-text fallback when no structured input fits (e.g. unrecognised command):

await sdk.sendFallback('I did not understand that');

End chat (user-initiated disconnect)

await sdk.endChat() ;

Events reference

Subscribe with sdk.on(event, handler), unsubscribe with sdk.off(event, handler).

Event

Payload

When fired

message

Message object

New message from bot or agent

userMessage

Message object

Message sent b y the local user

agentConnected

{ agentName: string }

Live agent joined via WebSocket (after CCTransfer)

agentTyping

{ typing: boolean }

Agent started or stopped typing

agentDisconnected

{ agentClosed, data }

Agent ended the chat from their side

disconnect

(none)

Chat session fully ended

sessionStarted

{ sessionId: string }

Brand-new session created

sessionRestored

{ sessionId: string, conversation: Messagel] }

Existing session found in localStorage

sessionClosed

(none)

Session cleaned u p

menuItems

{ items: [{ nodeId, description }] }

Bot returned a persistent menu

uploadProgress

{ inProgress: boolean }

File upload started / finished

info

{ infoType, content }

Status messages (e.g. "Getting agent for you...")

error

{ message: string }

Network o r parsing error

settingsLoaded

{ WEB_HOOK, WS_URL, UPLOAD_MEDIA }

Server settings applied successfully

debug

{ action: 'request'|'response', data }

Raw HTT request sent / response received - intended for dev tooling only

debug event usage:

sdk.on(' debug', ({ action, data}) => {
   if (action === 'request') console.log('->", data);
   if (action === 'request') console.log('<-", data);
                                         });

Message object shapes

Every message arrives through the 'message' o r 'userMessage' event. The type field tells you exactly how to render it.

Common fields (all messages)

{
author:          'bot' | 'agent' | 'user',
type:            string, // see types below
content:         any, // string for most types; object for interactive
source:          string, // media URL - empty string when not applicable
timestamp:       string, // locale time string, e.g. "10:32:15 AM"

// bot / agent only
agentName: string,    // agent display name (agent messages only, othe
showActionControls: boolean,   // true → render action UI (text input, choices,

}

type: 'text'

Plain text message from bot or agent.

{
author:      'bot' 'agent'
type:        'text'
content:     'Hello! How can I help you today?', // may contain HTML
source:      '',
timestamp: "10:32:15 AM'
showActionControls: false,
}

Render:

// content may contain HTML - use innerHTML
element.innerHTML = msg.content;

type: 'question'

Bot asks a question. May include predefined reply options. Your Ul should disable the main text input while this message is shown — re-enable i t after the user taps a reply button or submits a free-text answer.

{
author:      'bot'
type:        'question',
content:     'Please enter your order number',
source:
timestamp: "10:32:20 AM'
showActionControls: true,
}

Render:

// 1. Show the question text
renderText (msg.content);
// 2. Show a free-text input for the user's answer
if (msg.showActionControls) {
const input = createTextInput('Type your answer...');
input.onsubmit = async (text) = > {
await sdk.sendReply({ id: text, title: text });
;}
}

type: 'interactive'

Rich interactive message - button list, scrollable menu, or carousel. Your Ul should disable the main text input until the user makes a selection.

{
author: 'bot',
type: 'interactive'
content: { interactive: {/* see sub-types below */ } },
source:
timestamp: '10:32:25 AM',
showActionControls: true,
}

Access the interactive data via msg.content.interactive.

Sub-type: button

msg.content.interactive = {
type:      'button'
header:    { type: 'text', body: 'Choose an option' },   // optional
title:     'What would you like to do?',                 // optional body text
choices:  [
{ id: 'track', title: 'Track my order' },
{ id: 'return', , title: 'Return an item' },
{ id: 'support', title: 'Talk to support' },
],
}

Render:

const { header, title, choices } = msg.content.interactive;
if (header?.type === 'text') renderHeader (header.body);
if (title) renderBodyText (title);
choices. forEach(choice => {
const btn = createButton(choice.title);
btn.onclick = async () = > await sdk.sendReply(choice);
});

Sub-type: list

msg.content.interactive = {
  type:   'list',
  header: { type: 'text', body: 'Main Menu' },  // optional
  title: 'Select a category',                   // optional
  sections: [
    {
      title: 'Orders',
      choices: [
        { id: 'track'   title: 'Track Order', description: 'Check delivery state' },
        { id: 'cancel'  title: 'Cancel Order', description: 'Cancel a recent order' },
      ],
    },
    {
       title: 'Support',
       choices: [
          { id: "faq', title: 'FAQs',         description: 'Common questions' },
          ( id: 'agent', title: 'Live Agent', description:  'Chat with a person' },
     ],
   },
  ],
}

Render:

sections.forEach(section => {
  rendersectionheader (section.title);
  section.choices.forEach(choice => {
    const item = createListItem(choice.title, choice.description);
    item.onclick = async () => await sdk.sendReply(choice);
  }) ;
}) ;

Sub-type: carousel

Cards that share a common set of action buttons.

msg.content.interactive ={
  type: 'carousel',
  title: 'Choose a plan',      // optional heading
  data: {
    data: {
      options: [              // shared buttons shown on every card
        { id: 'select_plan', title: 'Select this plan' },
      ],
      values: [               // the cards
        { title: 'Starter', description: '$9/mo', image: 'https://...' },
        { title: 'Growth', description: '$29/mo' , image: 'https://..'},
        { title: 'Enterprise', description: 'Custom' },
      ],
    },
  },
}

Render:

const { options, values } = msg.content.interactive.data.data;
let currentIndex = 0;

function showCard (i) {
   const card = values [i];
   renderCardContent(card.image, card.title, card.description);

   options.forEach(opt = > {
      addButton(opt.title, async () = {
         await sdk.sendReply({ id: opt.id, title: card.title || opt.title });
      }) ;
   }) ;
}
showCard(currentIndex);
// prev/next controls to change currentIndex and re-render

type: "form'

Bot requests structured data from the user.

msg.content is the servers body object — form definition lives under msg.content.data

{
   author:    'bot',
   type:      "form',
   content: {
     data: {
       title: 'Please Enter Details', // form heading
       subtitle: 'sub',
       label: 'FormNode',
       type: 'Form',
       fields: [
         { id: '×6497', key: 'custName', label: "Name',     type: 'dropdown', required
         { id: 'd@jk', key: 'Id', label: 'City',            type: 'dropdown', required
         { id: 'ronli', key: 'custPhonel', label: 'Phone',  type: "phone',    reruired
         { id: 'em9b7', key: 'custmail', label: 'Email',    type: 'email',    reruired
       ],
       responseVariable: "',
     },
     source: '',
     timestamp: '10:32:30 AM',
     showActionControls: false,
}

Field types: textemailphone (render as tel) • dropdown (options = comma-separated string)

Render:

const { title, fields } = msg.content.data;
const formData = {};

// render title
renderHeading(title);

fields.forEach (field) => {
  let input;
  if (field.type === 'dropdown') {
    const opts = field.options.split(',') map(s = > s.trim());
    input = createSelect (field.label, opts, field.required);
  } else {
    const inputType = field.type === 'phone' ? 'tel' : field.type;
    input = createInput (field.label, inputType, field.required);
  }
input.onchange = (val) = formData[field.key] = val; // key is field.key, NOT f
}) ;

submitBtn.onclick = async () => {
  const labels = fields.map((f) = > '${f.label}: ${formData[f.key]}');
  await sdk.submitForm(formData, labels);
};

type: 'image'

Image message from bot or agent.

{
  author: 'bot' | 'agent',
  type: 'image'
  content: 'Optional caption text'   // may be empty
  source: 'https://example.com/photo.jpg',
  timestamp: "10:32:35 AM',
  showActionControls: false,
}

Render: <img src={msg.source} alt={msg.content} />

type: 'audio'

Audio message from bot or agent.

{
  author:   'bot' | 'agent',
  type:     'audio',
  content:  '',
  source: 'https://example.com/clip.mp3',
  timestamp: '10:32:40 AM',
  showActionControls: false,
}

**Render: ** <audio src={msg. source} controls />

type: 'video'

Video message from bot or agent.

{
  author:  'bot' | 'agent',
  type:    'video',
  content: 'Optional caption',
  source: 'https://example.com/cl1p.mp4',
  timestamp: '10:32:45 AM',
  showActionControls: false,
}

Render: <video src={msg. source} controls />

type: 'document'

File attachment from bot or agent.

{
  author: 'bot' | 'agent',
  type: 'document',
  content: 'filename.pdf', // display name
  source: 'https://example.com/filename.pdf',
  timestamp: "10:32:50 AM',
  showActionControls: false,
}

Render: <a href={msg. source} target="_blank"> {msg.content}</a>

MIME-typed media (agent messages via WebSocket)

When an agent sends a file, the type field is the raw MIME type string, not a simplified label.

{
  author:   'agent'
  type:     'image/jpeg' | 'image/png' | 'audio/mpeg' | 'video/mp4'
          | 'application/pdf' | 'application/sword' | ...,
  content: 'original-filename.jpg', // file name
  source: 'https://cdn.example.com/file.jpg',
  agentName: 'John',
  timestamp: "10:33:00 AM',
  showActionControls: false,
}

Detection pattern:

if (msg.type.startsWith('image/')) renderImage (msg.source, msg.content);
else if (msg.type.startswith('audio/')) renderAudio(msg.source) ;
else if (msg.type.startsWith('video/')) renderVideo(msg.source) ;
else if (msg.type.startsWith('application/')) renderDocument(msg.source, msg.content) ;

User message (from 'userMessage' event)

{ 
  author:    'user',
  type:      'text' | 'image' | 'audio' | 'video' | 'document',
  content:   'Hello there',    // text content or filename for media
  source:    '',              // uploaded file URL (media only)
  timestamp: '10:33:05 AM',
}

Info message (from 'info' event)

System status messages — not part of the chat message list but useful for banners/toasts.

{
  infoType: 'success' | 'error' | 'info' | 'warning',
  content: 'Getting agent for you...',
}

Common content values:

contentwhen
'Getting agent for you...'CCTransfer received - WebSocket connecting
'Agent connected: <name>' Agent joined the WebSocket
'Agent ended Chat'Agent disconnected from their side
'Chat Disconnected!!'Server sent disconnect signal via HTTP
'Chat Disconnected'Server sent disconnect signal via WebSocket