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


The Kandy WebRTC API helps you implement web real-time communication (WebRTC) 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, Mozilla Firefox and Safari (MacOs only).

Getting started


To authenticate you'll need to request an access token. Use your API Store app's credentials (Consumer Key and Consumer Secret) to make an authentication request. The authorization service returns a JSON message that contains the access_token field.

Use one of the following 3 options:


Execute below cURL command to receive an access token. Replace APP_CONSUMER_KEY and APP_CONSUMER_SECRET with your app's credentials.

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'

If you are using cURL for Windows, please use the command below instead.

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 authentication 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": "Bearer",
    "issued_at": "1521039195424",
    "client_id": "APP_CONSUMER_KEY",
    "access_token": "haf2SDl07E9N7RluNQ4kJ1TkGgso",
    "application_name": "6e38edxxxxxxxxxxxxxxxx4065d79c",
    "scope": "",
    "expires_in": "3599",
    "refresh_count": "0",
    "status": "approved"


  1. Click on the Authorize button on the top right.
  2. In the form, fill in client_id and client_secret, using your app's credentials.
  3. Click Authorize.


When using Postman, you will have to import the Swagger file into a Postman collection as follows:

  1. Open the API reference on SwaggerHub.
  2. On the top right, click Export, click Download API and click 'YAML Unresolved'.
  3. In Postman from the menu click File and click Import... Choose the YAML file you downloaded in the previous step. A new collection will be added.
  4. Select Get Access Token from the collection.
  5. Make sure the right environment is selected, corresponding to the API.
  6. Edit the environment variables client_id and client_secret, using your app's credentials.
  7. Check the response code and message.
  8. Press the Send button to get an access token.

Note: Request variables are no longer linked to an environment, but to the collection.

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



Address for REST signalling requests to the WebRTC API

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