WebRTC – Kandy


Enable voice and video calling from your website or application

High-quality and low-latency real-time web communication

  • Communication

API reference on SwaggerHub API reference on Postman


The Kandy WebRTC API helps you implement WebRTC (web real-time communication) in your application for safe and stable video calls. Kandy is a platform designed to help developers who want to use WebRTC in their applications. It provides you with a simple model for connecting two parties into a call and simplifies the developer experience with its SDKs.

Conceptual model

Conceptual model



Products are used to account for usage of the WebRTC API. It provides the capability to make or receive a voice or video call. In order for two users to communicate with each other, they need to be subscribed to the same product.


Users can make orders on available products in order to become subscribers.


A subscriber is a provisioned user of the WebRTC API. This means that the user has an identity in the API. To have an identity, means other users can find you and contact you via the API.

The platform accepts 2 types of subscribers: temporary and persistent. Temporary subscribers get a new identity with every use. Persistent ones always have the same identity in the WebRTC API.

API workflow

Sequence Diagram

WebRTC workflow

API workflow



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.js currently only supports Google Chrome and Mozilla Firefox.

Getting started


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  'https://api-prd.kpn.com/oauth/client_credential/accesstoken?grant_type=client_credentials'  -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 "https://api-prd.kpn.com/oauth/client_credential/accesstoken?grant_type=client_credentials" -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",
    "developer_email": "demo123@kpn.com",
    "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"

How to...


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

Follow these steps to get provisioned for the WebRTC API:

  1. Create an order for the required number of products. An order number is returned.
  2. Check the status of the order to ensure that it has been fulfilled. If not, contact us.
  3. Check the product inventory to ensure you have the expected number of products.
  4. Create subscribers. An error will occur if you don't have sufficient products.
  5. Use the subscribers credentials in your application.

Configure the SDK

To begin, you will need to include the Kandy SDK in your application. After including the library in your application you can create a Kandy instance. You need to input a configuration object, which will be explained under configuring the instance.

For example:

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

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

Configuring the instance

Since there are different Kandy instances around the globe, 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.

For example:

Configuration parameter Value Description
authentication.subscription.server "spidr-em.genband.com" Address for REST signalling requests to the WebRTC API
authentication.websocket.server "spidr-em.genband.com" Address for WebSocket connections to the WebRTC API
call.iceserver [ {"url": "turns:turn-em-1.genband.com:443?transport=tcp","credential": ""}, {"url": "turns:turn-em-2.genband.com:443?transport=tcp","credential": ""}, {"url": "stun:turn-em-1.genband.com:3478?transport=udp"}, {"url": "stun:turn-em-2.genband.com:3478?transport=udp"} ] 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 SDK configuration documentation.

Connect to the Kandy platform

For connecting or disconnecting to the Kandy platform, the first thing you need to do is initialize the SDK. For this, you need the server information of the Kandy platform. Depending on the used 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);

The HTML for this example is:

    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>

The example has text boxes for username and password, buttons for login and logout and an element for logging messages you receive from the platform.

The function that appends logs to the div element provided is:

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

Once you're provisioned with a username and password, you can connect to the platform. 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

The kandy.connect() function does not return a value. The SDK uses events to tell you when something has changed. Check out the list of the authentication events.

Subscribe to these events, with the kandy.on() function. For example:

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

With the previous example, whenever Kandy fires off an auth:change event, the function will be called.

If something goes wrong when connecting, you can add the following event:

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

To learn more about authentication responses, checkout the documentation for getConnection.

Try out this example in CodePen:

Disconnect from the Kandy platform

To disconnect, 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.

Configure the user interface for voice and video calls

To make voice and video calls, you need to configure your UI (user interface). Here is an example of basic UI that helps to make, respond or end calls:

    <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();' />
    <!-- Media containers. -->
    Remote video: <div id="remote-container"></div>
    Local video: <div id="local-container"></div>

The last fieldset defines the media that is displayed during 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.

Make a call

Users needs to be connected to the Kandy platform in order to make calls. To connect follow the section 'Connecting and disconnecting'.

When you make a call, you need to specify the location of your media containers as you configured in the UI section.

For example:

 *  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 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 our example, it is stored in a global variable.

Call Events

Kandy emits events that provide feedback about the changes in call state. To gather information of these events, you need to set up listeners.

call:start: This event informs you that your outgoing call has successfully initialized. The callee will receive an event 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: This event informs you that a problem was encountered with the call.

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

media:error: This event is more specialized in that it indicates that the call could not be made because webRTC media could not be initialized.

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

call:stateChange: When the call is answered or rejected, its state will change.

// 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 === kandy.call.states.ENDED) {
        callId = null;

call:receive: This event informs you of an incoming call. The event provides the ID of the call. To get more information about the call, check the SDK instance.

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

Try out this example in CodePen:

Respond to a call

Either answer or reject a call by following this example:

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


Note that callId has not been defined above when receiving a call. Find out the callId by listening to the call:receive event.

End a call

End a call by providing the call ID to the call.end SDK function as follows:

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


API versions

Version   Description
3.0.0   Initial version