WebRTC – Kandy


Enable point-to-point voice and video calling through simple webRTC APIs

Offer high quality and low-latency communication to your customers and colleagues

  • Communication

API Button


The Kandy WebRTC API helps you implement WebRTC in your application for safe and stable video calls.


RTC stands for Real-Time Communication, and WebRTC is an industry-standard technology that provides secure, reliable and high-definition realtime voice and video to application developers. WebRTC is designed to tolerate uncontrolled networks so you can focus on your application rather than worrying about how to get voice and video across the internet. WebRTC is made freely available as a reference implementation and is included in many popular modern web browsers. Click here for more information on WebRTC.

Kandy is a platform designed to help developers who are using WebRTC for their applications. It provides you with a simple model for connecting two parties into a call and simplifies the developer experience with its SDKs. While many WebRTC scenarios simply involve two applications communicating directly with each others, this connectivity isn't always possible, for example due to Network Address Translation (NAT) or Firewall policies. To overcome this problem, Kandy provides you with WebRTC-standard network resources to make sure you can still make a connection. Kandy also helps you out when you find that you need to have your application cross over between WebRTC and an existing phone system. In short, Kandy has got you covered.

Conceptual model

Conceptual model

General picture


Kandy WebRTC Workflow Diagram


This API provides a simple facade on the Kandy Business Services (KBS) provisioning interface for developers using Kandy services in their application.

In order to use this API, the developer should understand the terms subscribers, products and orders.


Your application will need a subscriber to use the Kandy platform. The subscriber is provisioned in the Kandy platform and then your application can use the Kandy SDK to authenticate as this subscriber. One subscriber can make calls to another subscriber using its subscriber identity.

In some use-cases, subscribers are "persistent" - there is a one-to-one relationship between a real end-user in your application and a subscriber defined in the Kandy platform. In many use-cases two temporary subscribers can be used to setup one-time calls (e.g. between a patient and a doctor).


Subscribers have to be assigned with some capabilities, called "products". Products are used to account for usage on the Kandy platform. Only one product, called "Cube-Develop", is defined in the API Store which provides the capability to make or receive a voice and/or video call. Each developer must establish an inventory of "Cube-Develop" products.

Two subscribers with "Cube-Develop" products are required for two users of your application to establish a voice and/or video call. Subscribers cannot be created unless there is a product available in the inventory.


The developer's product inventory is managed through orders. An order can be placed to increment or decrement the number of products in the inventory.

Orders that increment the inventory are automatically accepted ("fulfilled" status) up to a defined monthly limit. Beyond this limit, the orders stay at the "submitted" status until an administrator reviews the customer limits and determines if the order should be accepted.

Provisioning Steps

A developer will typically need to perform the following provisioning steps:

  • Create an order to increment the product inventory by the required number of products. An order number is returned.

  • Check the status of the order to check that it has been "fulfilled". If not, contact the administrator.

  • Check the product inventory to ensure you have the expected number of products. (API coming soon)

  • Create subscribers. An error will occur if you don't have sufficient products.

  • Use the subscribers in your application.

  • Delete the subscribers. The product assigned to the subscriber will be returned to the inventory.

  • Keep creating and deleting subscribers as needed. Order more products if needed.

Provisioning Schema

Sequence Diagram

You can try out the provisioning process in Postman.



WebRTC is a technology that has been developed with security as a high priority. Browser applications that wish to use WebRTC must be operating from secure sources so that the media can be secured between the two parties in the call.

Browser support

The Kandy JavaScript SDK currently only supports Google Chrome and Mozilla Firefox.


OAuth 2.0

For accessing and/or manipulating the resources, the client application (your application) needs to be granted permission to do so. The OAuth 2.0 standard defines a protocol that allows such third-party authorization through the use of access tokens. Access tokens are central in the protocol: those tokens, in the form of strings, are delivered by an authorization server (our authentication server) and they enable the client application to securely access protected data on behalf of the resource owner (the end-user).

We use Client Credentials Grant which means the application makes the request to the authentication service by sending authorization credentials and the service responds with an access token among other useful information

Get Access Token

Copy your app's credentials and replace APP_CONSUMER_KEY and APP_CONSUMER_SECRET with the copied values, then execute the below cURL command to receive an access token.

curl -X POST  ''  -H 'content-type: application/x-www-form-urlencoded'  -d 'client_id=APP_CONSUMER_KEY&client_secret=APP_CONSUMER_SECRET'

Note: If you are using cURL for Windows then please use the below command.

curl -X POST "" -H "content-type: application/x-www-form-urlencoded" -d "client_id=APP_CONSUMER_KEY&client_secret=APP_CONSUMER_SECRET"

The authorization service returns a JSON message that contains the access token field.

    "refresh_token_expires_in": "0",
    "api_product_list": "[xxxxxxx]",
    "api_product_list_json": [
        " xxxxxxx"
    "organization_name": "kpn",
    "": "",
    "token_type": "Token",
    "issued_at": "1521039195424",
    "client_id": "APP_CONSUMER_KEY",
    "access_token": "haf2SDl07E9N7RluNQ4kJ1TkGgso",
    "application_name": "6e38ed2d-48b1-4362-97d6-04254065d79c",
    "scope": "",
    "expires_in": "3599",
    "refresh_count": "0",
    "status": "approved"



Using the Kandy.js library

To begin, you will need to include the Kandy JavaScript library in your application. The Kandy.js library can be found here: Kandy.js. After including the library in your application you can create a Kandy instance. You'll need to pass in a configuration object, which will be explained below.

// Instantiate the SDK.
const { create } = Kandy;
const kandy = create(kandyConfig);

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

Configuration object

Since there are different Kandy instances around the globe (and some private Kandy instances), the SDK is instructed about how to communicate with the Kandy platform through a configuration object. This object consists of: * The signalling interface which is based on REST and uses HTTPS as a transport protocol. * A notification interface which uses secure WebSockets. * Addresses of STUN and TURN servers that are used in the WebRTC ICE procedure.

In the table below we provide an example:

Configuration parameter Value Description
authentication.subscription.server "" Address for REST signalling requests to the Kandy platform
authentication.websocket.server "" Address for WebSocket connections to the Kandy platform
call.iceserver [
     {url: "","credential": ""},
     {url: "","credential": ""},
     {url: ""},
     {url: ""}
Addresses for TURN and STUN servers on the Kandy platform

In most cases, the default values will suffice for an application. For a full list of the possible configurations, see the Kandy Configuration Documentation.

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

Simplest 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 { create } = Kandy;
const kandyConfig={
    // Required: Server connection configs.
    authentication: {
        subscription: {...},
        websocket: {...}
    // Other feature configs.
const kandy = create(kandyConfig);

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

    Username <input type="text" id="user-id-textbox" />
    Password <input type="password" id="password-textbox" />
    <input type="submit" value="Login" onclick="login();" />
    <input type="submit" value="Logout" onclick="logout();" />
<div id="messages"> </div>

What we have is some text boxes for username and password, buttons for login and logout, and an element for logging messages to.

And, to help our understanding of what is happening in the application, we have a function that appends logs to the div element provided.

function log(message) {
    document.getElementById('messages').innerHTML += '<div>' + message + '</div>';

Step 1: Connecting

When you have a username and password, then you can try to connect. An event handler can be provided for the login button as follows.

    function login() {
        username: document.getElementById("user-id-textbox").value,
        password: document.getElementById("password-textbox").value

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 connection = kandy.getConnection();
   // check we are transitioning states or if we have completed connection/disconnection
   if(!connection.isPending) {
      if(connection.isConnected) {
         log("Connected as "+document.getElementById("user-id-textbox").value);
      } else {
   } else {
      // connection state is changing... maybe let the user know
      if(connection.isConnected) {
      } else {
         log("Connecting as "+document.getElementById("user-id-textbox").value);

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

If something goes wrong when we try to connect (invalid credentials maybe), 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 + ')');

To learn more about the response from this API checkout the documentation for getConnection here.

Step 3: Disconnecting

To disconnect, you simply call disconnect.

function logout() {

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.

Click the button below to try out this quickstart in CodePen:

Simplest Voice and Video

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.

Call Configs

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.

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.

        <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' />

        <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();' />

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

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

    <!-- 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 =, {
        sendInitialVideo: withVideo,
        remoteVideoContainer: remoteContainer,
        localVideoContainer: localContainer,
        normalizeAddress: true

Kandy’s call.make 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. In this simple example, it is stored in a global variable.

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 =;
    log('Answering call from ' + call.from);, {
        sendInitialVideo: withVideo,
        remoteVideoContainer: remoteContainer,
        localVideoContainer: localContainer

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

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 =;
    log('Ending call with ' + call.from);;

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.


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);


As the call is acted upon (such as 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 === {
        callId = null;


The call:receive event informs us that we have received an incoming call. The event provides the ID of the call, 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 =;
    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.

Click the button below to try out this quickstart in CodePen: