Go beyond traditional communication

Enhance your communication with voice, video and messaging to create a truly outstanding experience for customers.


Getting Started

In this quickstart, we will help you dip your toes into Kandy before you dive in. This guide will help you get started with the Kandy JavaScript SDK.

Using Kandy.js

To begin, you will need to include the Kandy JavaScript library in your application. The Kandy.js library can be found here: Kandy.js.

Kandy.js will expose a factory function to your page called createKandy. This function is used to create an instance of the SDK, as well as to configure that instance.

// Instantiate the SDK.
const kandy = createKandy(configs);

// Use the Kandy API.
kandy.on( ... );

After you’ve created your instance of Kandy, you can begin playing around with it to learn its functionality and see how it fits in your application. The Kandy API reference documentation will help to explain the details of the features available.

Configurations

An important part of Kandy configurations is the server information. You will need to provide the server information that is part of your Kandy package. This can be done by providing a configuration object to the Kandy factory as shown below.

// Instantiate the SDK.
const kandy = createKandy({
    // Required: Server connection configs.
    authentication: {
        subscription: {
            // Specify the connection information for REST requests.
        },
        websocket: {
            // Specify the connection information for websockets.
        }
    }
    // Other feature configs.
    ...
});

To learn more about configuring Kandy, please see the section Configurations.

Further Reading

The best way to learn is usually by example and the best way to learn Kandy is by going through quickstarts. At the end of each quickstart, there is a link to view working examples in CodePen. These examples are great resources with which to experiment and start your own code base.

Browser Support

Browser Version
Chrome Latest 3 Major Versions
Firefox Latest 3 Major Versions
IE 11
Safari Latest Major Version

Configurations

The first step for any application is to initialize Kandy.js. When doing this, you can customize certain features by providing a configurations object. Kandy’s configuration object is separated by feature, and is provided to the Kandy Factory as seen in the example below.

// Initialize an instance of Kandy.js.
const kandy = createKandy({
    authentication: {
        // Server connection configs.
    },
    logs: {
        // Log output configs.
    },
    // Other feature configs.
    ...
});

In most cases, the default values will suffice for an application, but specifying your own configurations allows you to customize certain behaviors. The exception is the authentication configurations, which are always required. This quickstart will showcase a few samples of why you may want to use certain configurations. For a full list of the possible configurations, see the Configuration Documentation .

Example Configurations

Logs

The Logs configs are used to change the severity of logging output from Kandy.js. This allows for more logged messages, such as debug information, warnings and errors, which can help to explain what Kandy is doing.

logs: {
    // Set the log level to 'debug' to output more detailed logs. Default is 'warn'.
    logLevel: 'debug'
}

Call

The Call configs are used to initialize call/network settings and to set the starting behavior of a call. They are split into call behavior configs (under a callDefaults object), and call initialization configs. These configs will be used as the default for a call if they are not provided when the call is made.

call: {
    callDefaults: {
        // Set the default behavior for a call to an audio call where video
        //      is enabled, so can be used later on.
        isVideoEnabled: true,
        sendInitialVideo: false,
        // Set the default HTML elements that should be used to display call media.
        remoteVideoContainer: document.getElementById('remote-media'),
        localVideoContainer: document.getElementById('local-media')
    },
    // Specify the browser extension to use (for Chrome) when doing screensharing.
    chromeExtensionId: 'abc123...'
}

Authentication

The Authentication configs are used to specify the backend service that Kandy.js should connect to. It is important to always include these configurations.

authentication: {
    subscription: {
        // Specify the connection information for REST requests.
    },
    websocket: {
        // Specify the connection information for websockets.
    }
}

Connectivity

The Connectivity configs are used to customize the behavior of the websocket and connectivity checks. These settings should only be needed if the default configs are not sufficient, and you want to tweak the behavior for your application’s scenario.

connectivity: {
    // Specify that a ping should be sent every 15 seconds, and an error should
    //      be reported after 3 missed pong responses.
    method: 'pingPong',
    pingInterval: 15000,
    maxMissedPings: 3
}

User Connect

Connecting and Disconnecting

In this quickstart we will cover how to connect and disconnect to the Kandy Platform using Kandy.js. We will provide snippets of code below, which together will form a working demo application.

The first step with Kandy.js is always to initialize it. You will need to know the server information for the Kandy platform that you are using for initialization. Depending on your platform, the only required configuration is the server address, as the others have generic defaults.

// Instantiate the SDK.
const kandy = createKandy({
    // Required: Server connection configs.
    authentication: {
        subscription: {
            // Specify the connection information for REST requests.
        },
        websocket: {
            // Specify the connection information for websockets.
        }
    }
    // Other feature configs.
    ...
});

To learn more about initializing Kandy, see our Configuration Quickstart. This example does not provide data for authentication but it is required.

Since we’re going to be making a working demo, we also need some HTML. The HTML for this demo is quite simple.

<div id='auth-state'>Connected: false</div>

<input type="submit" value="Login" onclick="login();">
<input type="submit" value="Logout" onclick="logout();">

<div id="messages"> </div>

What we have is a simple <div> containing the connected state of our app, two buttons, and an element for logging messages to.

Step 1: Connecting

To connect using Kandy, you will need two things:

  • A username. This is the full username of a user on your domain. (Example: your-user@your-domain.kandy.io)
  • A password. Don’t worry, its safe with us.

With these three things, you can call the connect function on Kandy.

    function login() {
      kandy.connect({
        username: username,
        password: password
      });
    }

Step 2: Connection Events

The kandy.connect() function does not return a value. Instead, Kandy.js uses events to tell you when something has changed. You can find a list of the authentication events here.

To subscribe to these events, you use kandy.on(). Here is the example for our demo app:

kandy.on('auth:change', function() {
    let isConnected = kandy.getConnection().isConnected;
    document.getElementById('auth-state').innerHTML = 'Connected: ' + isConnected;
    log('Connection state changed.');
});

If something goes wrong when we try to connect (for example invalid credentials), we want to know. Kandy.js has an auth:error event to support this.

// Listen for authentication errors.
kandy.on('auth:error', function(params) {
    log('Connect error: ' + params.error.message + ' (' + params.error.code + ')');
});

In the above piece of code we subscribe an anonymous function to the auth:change event. Now, whenever Kandy fires off an auth:change event, that function will be called. Inside this function we call kandy.getConnection(). This function returns an object that looks like so:

{ isConnected: true, isPending: false, error: undefined }

To learn more about the response from this API, check out the documentation for getConnection here.

Step 3: Disconnecting

To disconnect, you simply call disconnect.

function logout() {
    kandy.disconnect();
}

Calling this function will trigger a change in the connection state, which in turn will trigger any listeners to the auth:change event. You can therefore use your auth:change listener to detect if the disconnect was successful.

Messaging

Messaging

In this quickstart we will cover how to send and receive text based messages using Kandy.js. We will provide snippets of code below, which together will form a working demo application.

The first step with Kandy.js is always to initialize it. In this quickstart, the default config values will work just fine, so this step is simple.

// Instantiate the SDK.
const kandy = createKandy({
    // Required: Server connection configs.
    authentication: {
        subscription: {
            // Specify the connection information for REST requests.
        },
        websocket: {
            // Specify the connection information for websockets.
        }
    }
    // Other feature configs.
    ...
});

To learn more about initializing Kandy, see our Configuration Quickstart. This example does not provide data for authentication but it is required.

HTML

Since we’re going to be making a working demo, we also need some HTML. The HTML for this demo is quite simple.

First we have a <div> to show if we are connected or not.

<div id='auth-state'>Connected: false</div>

Next, we have a fieldset that contains all the actions we will perform for messaging. We have a button and input field to create conversations, as well as a button and input field to create and send messages. We also have a radio button to select what type of conversation we want to create: an IM conversation or an SMS conversation. Note: not all backends support SMS messaging.

<fieldset>
    <legend>Conversations</legend>
    <p>Step 1: Select what type of conversation you want to create.</p>
    <div>
    <input type="radio" id="convoType1"
        name="convo" value="im" checked="checked">
    <label for="convoType1">IM</label>

    <input type="radio" id="convoType2"
        name="convo" value="sms">
    <label for="convoType2">SMS</label>
    </div>
    <br/>
    Step 2: Enter their contact information (full user ID or ten digit phone number):
    <input type='text' id='convo-participant' />
    <br/><br/>

    Step 3: Create!
    <input type='button' value='Create' onclick='createConvo();' />
    <br/><hr>

    <input type='button' value='Send' onclick='sendMessage();' />
    message to conversation:
    <input type='text' value='Test message' id='message-text' />

</fieldset>

Below that is a fieldset to hold the incoming and outgoing conversation messages.

<fieldset>
    <legend>Messages</legend>
    <div id='convo-messages'></div>
</fieldset>

Finally, we have a <div> to display general Kandy messages (such as any errors that may occur).

<div id="messages"> </div>

Connection

To send messages, we first must be connected. For this section we can reuse the code from the Connection Quickstart.

Step 1: Creating a Conversation

In Kandy.js, there is the concept of a Conversation as an object. A Conversation object keeps track of the messaging state between the participants of that conversation, as well as information and utilities for the conversation itself. When you send or receive a message with another user, it is sent or received through the conversation with that user. To start messaging with a user, you need to create a Conversation with them first.

/*
    *  Basic Chat functionality.
    */

// We will only track one conversation in this demo.
var currentConvo;

// Create a new conversation with another user.
function createConvo() {
    var participant = document.getElementById('convo-participant').value;

    // Pass in the full username of a user to create a conversation with them.
    currentConvo = kandy.conversation.get(participant);

    log('Conversation created with: ' + participant)
}

A Conversation has a few functions on it, such as getMessages(). You can learn more about these functions here. An important thing to note is that conversation.get() will create a conversation in the state of Kandy if it doesn’t already exist. If the conversation does already exist, Kandy will simply return that object.

Step 2: Creating and Sending a Message

From that Conversation object, you can create a Message object. A Message object represents the message being sent/received, which, for this quickstart, will be a basic text message. To send the message, you simply call send() on the Message object.

// Create and send a message to the current conversation.
function sendMessage() {
    if(currentConvo) {
    var text = document.getElementById('message-text').value;

    // Create the message object, passing in the text for the message.
    var message = currentConvo.createMessage(text);

    // Send the message!
    message.send();
    } else {
    log('No current conversation to send message to.');
    }
}

Step 3: Messaging Events

There are a few messaging events we care about. We will go over two such events below.

messages:change

One such event is messages:change. This event is fired whenever a message is added to a conversation that is present in Kandy.js’s state (including outgoing messages). Any subscribers to this event will receive the participant for which there is a new message. You can read more about this event here.

/*
    * Listen for new messages sent or received.
    * This event occurs when a new message is added to a conversation.
    */
kandy.on('messages:change', function(params) {
    log('New message in conversation with ' + params.conversationId);

    // If the message is in the current conversation, render it.
    if(currentConvo.destination === params.conversationId) {
    renderLatestMessage(currentConvo);
    }
});

conversations:change

This event is fired whenever a new conversation is added to the conversation list in the Kandy store. One such example of this occurring is when Kandy.js receives a message from a conversation it does not yet have a record for. In this instance, Kandy.js will create a representation of the new convo in the store, and emit this event. Any subscribers to this event will receive a conversation ID. You can read more about this event here.

/*
    * Listen for a change in the list of conversations.
    * In our case, it will occur when we receive a message from a user that
    * we do not have a conversation created with.
    */
kandy.on('conversations:change', function(params) {
    log('New conversation with ' + params.conversationId);

    // If we don't have a current conversation, assign the new one and render it.
    if(!currentConvo) {
    currentConvo = kandy.conversation.get(params.conversationId);
    renderLatestMessage(currentConvo);
    }
});

When our event listeners receive an event, meaning our conversation has a new message, we want to display that message to the user. In the demo, our listeners do this by calling a renderLatestMessage function, which adds the message to our interface, as can be seen below.

    // Display the latest message in the provided conversation.
    function renderLatestMessage(convo) {
      // Retrieve the latest message from the conversation.
      var messages = convo.getMessages();
      var message = messages[messages.length - 1];

      // Construct the text of the message.
      var text = message.sender + ': ' + message.parts[0].text;

      // Display the message.
      var convoDiv = document.getElementById('convo-messages');
      convoDiv.innerHTML += '<div>' + text + '</div>';
    }

This function receives the conversation object as input. It then grabs the messages via convo.getMessages() and grabs the last message. From this message it grabs message.parts[0]. Messages can have multiple parts, such as a text part and an image part. Finally it formats and prints the message.

Voice & Video Calls

In this quickstart, we will cover the basics of making IP calls with Kandy. Code snippets will be used to demonstrate call usage of Kandy.js, and together these snippets will form a working demo application that can be viewed at the end.

For information about other call features, such as mid-call operations or screensharing, please refer to their respective quickstarts.

Call Configs

When initializing Kandy.js, call configurations can be provided that act as a default if they are not provided when making a call. For this quickstart, we will specify that we want calls to default to being audio-only. This can be overridden by providing these options when making or answering the call. To learn more about initializing Kandy, see our Configuration Quickstart. This example does not provide data for authentication but it is required.

// Instantiate the SDK.
const kandy = createKandy({
    // Required: Server connection configs.
    authentication: {
        subscription: {
            // Specify the connection information for REST requests.
        },
        websocket: {
            // Specify the connection information for websockets.
        }
    }
    // Other feature configs.
    ...
});

After that, the user will need to connect to Kandy. We won’t cover authentication in this quickstart, so we’ll take a shortcut and steal the connect code from the User Connection Quickstart, except for the button part. This way the user will automatically login after initialization.

User interface

To interact with our demo application, we will have a basic UI that allows us to make outgoing calls and respond to incoming calls. The UI will be kept very simple, as it is not the focus of this quickstart, so it will be a straightforward set of elements for user input.

<div>
    <fieldset>
        <legend>Make a Call</legend>
        <!-- User input for making a call. -->
        <input type='button' value='Make Call' onclick='makeCall();' />
        to <input type='text' id='callee' />
        with video <input type='checkbox' id='make-with-video' />
    </fieldset>

    <fieldset>
        <legend>Respond to a Call</legend>
        <!-- User input for responding to an incoming call. -->
        <input type='button' value='Answer Call' onclick='answerCall();' />
        with video <input type='checkbox' id='answer-with-video' />
        <input type='button' value='Reject Call' onclick='rejectCall();' />
    </fieldset>

    <fieldset>
        <legend>End a Call</legend>
        <!-- User input for ending an ongoing call. -->
        <input type='button' value='End Call' onclick='endCall();' />
    </fieldset>

    <fieldset>
        <!-- Message output container. -->
        <legend>Messages</legend>
        <div id='messages'></div>
    </fieldset>
</div>

To display information to the user, a log function will be used to append new messages to the “messages” element shown above.

An important part of the UI for calls are the media containers. These containers will be used to hold the media from both sides of the call. A remote media container will always be needed for a call (both voice and video), and a local media container will be needed if you would like to display the local video of the call. The HTML elements that Kandy.js will use as media containers are empty <div>s.

<!-- Media containers. -->
Remote video: <div id="remote-container"></div>

Local video: <div id="local-container"></div>

With that, there is nothing more needed for the user interface.

Step 1: Making a Call

When the user clicks on the ‘Make Call’ button, we want our makeCall function to retrieve the information needed for the call, then make the call. Since we did not specify default media containers on initialization, we will specify them as we make the call.

/*
    *  Voice & Video Call functionality.
    */

// Variable to keep track of the call.
let callId;

// Get user input and make a call to the callee.
function makeCall() {
    // Gather call options.
    let callee = document.getElementById('callee').value;
    let withVideo = document.getElementById('make-with-video').checked;

    // Gather media containers to be used for the call.
    let remoteContainer = document.getElementById('remote-container');
    let localContainer = document.getElementById('local-container');

    log('Making call to ' + callee);
    callId = kandy.call.make(callee, {
        sendInitialVideo: withVideo,
        remoteVideoContainer: remoteContainer,
        localVideoContainer: localContainer,
        normalizeAddress: true
    });
}

Kandy’s makeCall will return a unique ID that is used to keep track of the call. This ID will be used to perform operations involving the call.

Step 2: Responding to a Call

If our user receives an incoming call, they will be able to either answer or reject it with our demo application. Our demo’s answerCall and rejectCall functions will invoke Kandy’s functions of the same names.

// Answer an incoming call.
function answerCall() {
    // Gather call options.
    let withVideo = document.getElementById('answer-with-video').checked;

    // Gather media containers to be used for the call.
    let remoteContainer = document.getElementById('remote-container');
    let localContainer = document.getElementById('local-container');

    // Retrieve call state.
    let call = kandy.call.getById(callId);
    log('Answering call from ' + call.from);

    kandy.call.answer(callId, {
        sendInitialVideo: withVideo,
        remoteVideoContainer: remoteContainer,
        localVideoContainer: localContainer
    });
}

// Reject an incoming call.
function rejectCall() {
    // Retrieve call state.
    let call = kandy.call.getById(callId);
    log('Rejecting call from ' + call.from);

    kandy.call.reject(callId);
}

Note that callId has not been defined above when receiving a call. The user will receive the callId from an event, which will be covered in Step 4.

Step 3: Ending a Call

If our user has an ongoing call, they can end it by providing the call’s ID to Kandy’s call.end function, which is what our demo application will do.

// End an ongoing call.
function endCall() {
    // Retrieve call state.
    let call = kandy.call.getById(callId);
    log('Ending call with ' + call.from);

    kandy.call.end(callId);
}

Step 4: Call Events

As we use Kandy’s call functions, Kandy will emit events that provide feedback about the changes in call state. We will set listeners for these events to keep our demo application informed about Kandy state.

call:start

The call:start event informs us that an outgoing call that we made has successfully been initialized, and the callee should receive a notification about the incoming call.

// Set listener for successful call starts.
kandy.on('call:start', function(params) {
    log('Call successfully started. Waiting for response.');
});

call:error and media:error

The call:error event informs us that a problem was encountered with the call. The media:error event is more specialized in that it indicates that the call could not be made because webRTC media could not be initialized. Both events provide information about the error that occured.

// Set listener for generic call errors.
kandy.on('call:error', function(params) {
    log('Encountered error on call: ' + params.error.message);
});

// Set listener for call media errors.
kandy.on('media:error', function(params) {
    log('Call encountered media error: ' + params.error.message);
});

call:stateChange

As the call is acted upon (for example answered or rejected), its state will change. We can react to changes in the call by listening for the call:stateChange event. For our demo application, we will only act if the call was ended.

// Set listener for changes in a call's state.
kandy.on('call:stateChange', function(params) {
    log('Call state changed to: ' + params.state);

    // If the call ended, stop tracking the callId.
    if(params.state === 'ENDED') {
        callId = null;
    }
});

call:receive

The call:receive event informs us that we have received an incoming call. The event provides the ID of the call, and then we can get more information about it from Kandy state.

// Set listener for incoming calls.
kandy.on('call:receive', function(params) {
    // Keep track of the callId.
    callId = params.callId;

    // Retrieve call information.
    call = kandy.call.getById(params.callId);
    log('Received incoming call from ' + call.from);
});

We can now call the demo application done. We’ve covered the basics of what is needed to allow a user to use call functionality.

Anonymous Calls

Anonymous Calls is a Kandy feature that enables a user to make a call without needing their own user account. It allows for scenarios such as a visitor on a website making a call to a support line.

This quickstart will outline the differences between a Voice & Video Call scenario and an Anonymous Call scenario. Since Anonymous Calls are an extension of Voice & Video calls, it is expected that the reader understands [Voice & Video calls](#Voice and Video calls) fully.

Prerequisites

  1. Anonymous Calls require that the Kandy domain has the CallMe service enabled for their users as part of their configurations.
  2. Anonymous Calls require the Anonymous Call version of the Kandy SDK.
  3. Anonymous Calls require a server component for the generation of secure tokens.

Types of Anonymous Calls

There are two types of Anonymous Calls: time-limited and unrestricted.

  • Time-Limited Anonymous Call

    A “time-limited” Anonymous Call is the proper name for what “Anonymous Call” generally refers to. An Anonymous Call does not require the caller to have a user account, so instead of authentication, the user requires time-limited tokens to verify the call. It is recommended that these tokens are generated with a server component, which allows you to moderate the Anonymous calls being made. More about the tokens are explained below.

  • Unrestricted Anonymous Call

    An “unrestricted” Anonymous Call does not require any verification for the call. This allows for a much simpler flow, but is unsecure, since it is more difficult to moderate the calls being made.

Making a Call

The only difference in making a call between regular Calls and Anonymous Calls is the “make call” API itself. Other mid-call operation APIs, events, etc. are the same as for regular Calls.

    // regular call API.
    kandy.call.make(callee, callOptions);

    // anonymous call API.
    kandy.call.makeAnonymous(callee, credentials, callOptions);

For a time-limited call, you provide the tokens and the “realm” (see below) as part of the API call. For an unrestricted call, you simply omit the credentials by providing a null parameter.

Both types of calls can also include an optional from property in the callOptions, to indicate the caller.

    let callOptions = {
        // Same call options as a regular call.
        isVideoEnabled: true,
        remoteVideoContainer: ...,
        ...
        // A from property.
        from: 'debbie@anon.callMe'
    };

    // Time-limited call.
    let credentials = {
        accountToken: 'abc123...',
        fromToken: 'def456...',
        toToken: 'ghi789...',
        realm: 'kandyRealm123'
    };
    kandy.call.makeAnonymous(callee, credentials, callOptions)

    // Unrestricted call.
    kandy.call.makeAnonymous(callee, {}, callOptions);

Receiving a Call

It should be noted that Anonymous Calls are meant as outgoing-only calls. The Anonymous Call version of the Kandy SDK does not support receiving calls.

Generating Tokens

The tokens used for an Anonymous Call act as verification that the call is valid, and that it should be allowed. There are two security features encoded in the tokens for this: a timestamp and a secret key.

Token generation is done in two steps:

  1. Creating the token, then
  2. Encrypting the token

Token Creation

A token itself is simply a string constructed in a certain format. The formats for creating the tokens are as shown below:

Account Token

userID;timestamp, eg. user1@kandy.io;1234567890

The user used for this token is the user that is configured as the “anonymous” user; the account is pre-setup for the Anonymous service. This user is considered the callee of the call.

From & To Tokens

sip:userID;timestamp, eg. sip:user1@kandy.io;1234567890

The from and to tokens are used to identify the users that the call is between. For the from token, the userID should be the same user as was used for the account token. For the to token, the userID should be the user receiving the call.

All three tokens are time-limited, meaning they will expire after a set amount of time. The expiry period is configured as a setting on the Kandy domain that the users belong to.

Token Encryption

The tokens then need to be encrypted using a secret key.

Key / Realm

As a prerequisite of using time-limited calls, Kandy server configuration will be needed to create a “realm” that corresponds to the Kandy domain that the users belong to. This realm defines the expiry period for the tokens and the key that should be used to encrypt and decrypt the tokens. When the application provides the tokens as part of the call.makeAnonymous API, it will also need to provide the name of this realm (see the credentials object in the ‘Making a Call’ section).

Algorithm

The algorithm that should be used to encrypt the token is the AES/ECB/PKCS5Padding cipher. The key used for it, which is configured as part of the realm, should be 128 bits (16 characters).

Live Demo

Want to play around with this example for yourself? Feel free to edit this code at