Quick Start Guide

Follow this tutorial to build a simple app in less than 15 minutes.
  1. Get an API key
  2. Create a user
  3. Sign in & get a user object
  4. Load & Initialize TenHands video service
  5. Add a call button
  6. Make a call
You will build a simple application that allows you to make/receive audio/video calls.

1. Get an API key

Sign up for a TenHands account if you don't already have one. After you get an account, create an app to get your API key. The application API key identifies your application to TenHands service. Note: Your API Key is private and should not be shared with anyone nor used directly from the browser to make API calls. You should use the REST APIs that require an API key from your backend server and only use the APIs that take authToken as a parameter directly from the browser.

2. Create a new user

TenHands has a simple model for keeping track of users of your application. You are required to register each user of your application with TenHands using the /user/create REST API. Typically, we recommend that you make this call as part of your application's user creation work flow. For this tutorial, we create two new users, one for the caller and the other for the callee, using a curl script.

                curl https://tenhands.net/api/v1/user/create --data "apiKey=${API_KEY}&email=bob.caller@tenhands.net&name=Bob Caller"

                curl https://tenhands.net/api/v1/user/create --data "apiKey=${API_KEY}&email=alice.callee@tenhands.net&name=Alice Callee"
                

3. Sign in & get the user object

In order to use TenHands service, you are required to initialize the service with a user object (which encapsulates the user id, authToken, etc). Typically, we recommend that you make a call to the /user/signIn API to sign into TenHands as part of your regular application login workflow. The user json object that’s returned from the REST API call should be used in the TenHands JS API call to load and initialize the TenHands video service. For this tutorial, we sign in the users using a curl script.

                curl https://tenhands.net/api/v1/user/signIn --data "apiKey=${API_KEY}&email=bob.caller@tenhands.net"

                curl https://tenhands.net/api/v1/user/signIn --data "apiKey=${API_KEY}&email=alice.callee@tenhands.net"
                

4. Load & Initialize TenHands video service

4.1 We'll create 2 sample HTML pages (one for the caller and the other for the callee) that include the TenHands RTC javascript. The caller/callee pages will be similar except for the user object used to load TenHands VideoService.

                <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
                <head>
                <title>TenHands Getting Started</title>
                <link href="https://tenhands.net/css/bootstrap.min.css" rel="stylesheet">
                <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
                <script type="text/javascript" src="https://tenhands.net/js/tenhands.loader.v2.0.js"></script>
                </head>
                <body>
                </body>
                </html>
                

4.2 Now add a div within your HTML body tags for holding the TenHands video service related objects. For e.g.,

                   <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
                <head>
                <title>TenHands Getting Started</title>
                <link href="https://tenhands.net/css/bootstrap.min.css" rel="stylesheet">
                <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
                <script type="text/javascript" src="https://tenhands.net/js/tenhands.loader.v2.0.js"></script>
                </head>
                <body>
<div id="THVideoContainer" style="position:absolute; top:0px; left:0px; height: 100%; width: 100%; visibility: hidden;"> </div>
</body> </html>

4.3 Add javascript code to load the VideoService object into the THVideoContainer div. Register a callback function (by setting the onSuccess property) in TenHands.videoService(options) method to receive a callback once the videoService is successfully loaded. The user json object is the one that’s returned from the /signIn REST APIs. For the caller page, use the user object from the /signIn call that uses bob.caller@tenhands.net, and for the callee page use the user object from the /signIn call that uses alice.callee@tenhands.net.

                <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
                 <head>
                 <title>TenHands Getting Started</title>
                 <link href="https://tenhands.net/css/bootstrap.min.css" rel="stylesheet">
                 <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
                 <script type="text/javascript" src="https://tenhands.net/js/tenhands.loader.v2.0.js"></script>
                 
<script> $(function() { user = {...}; // plugin in the user object from the /signIn call TenHands.videoService({videoContainerId: "THVideoContainer", user: user, onSuccess: onVideoServiceLoad}); } </script>
</head> <body> <div id="THVideoContainer" style="position:absolute; top:0px; left:0px; height: 100%; width: 100%; visibility: hidden;"> </div> </body> </html>

4.4 In the "onVideoServiceLoad" callback function, store a reference to the videoService that was loaded. Add a connectionEventHandler so you get notified when the user is signedIn/signedOut of the service. Get the default videoPlayer from the videoService so you can make/receive calls. Alternatively, you can use create your custom UI, instead of using the default player, using VideoService's more granular API. Take a look at the sample code (Custom Caller and Custom Callee) for the custom player to understand how to use the more granular API.

                <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
                 <head>
                 <title>TenHands Getting Started</title>
                 <link href="https://tenhands.net/css/bootstrap.min.css" rel="stylesheet">
                 <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
                 <script type="text/javascript" src="https://tenhands.net/js/tenhands.loader.v2.0.js"></script>
                 <script>
                    $(function() {
                        user = {...};  // plugin in the user object from the /signIn call

                        TenHands.videoService({videoContainerId: "THVideoContainer",
                                                   user: user,
                                                   onSuccess: onVideoServiceLoad});
                    }
                    
function onVideoServiceLoad(videoService) { gVideoService = videoService; // Store reference to videoService gVideoPlayer = gVideoService.getDefaultVideoPlayer({}); // Get a handle to the default videoPlayer // Add a connectionEventHandler gVideoService.registerConnectionEventHandler(function(connectionStatus) { console.log(connectionStatus); if (connectionStatus.signedIn) { console.log("Signed In"); } else { console.log("Signed out"); } }); };
</script> </head> <body> <div id="THVideoContainer" style="position:absolute; top:0px; left:0px; height: 100%; width: 100%; visibility: hidden;"> </div> </body> </html>

5. Add a call button

Now, we'll add a call button to your HTML page and wire the click eventHandler of the call button to make an outgoing call using the videoPlayer's call API. Note that the call button is initially disabled. We enable the call button once the user is signed into TenHands service

                <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
                 <head>
                 <title>TenHands Getting Started</title>
                 <link href="https://tenhands.net/css/bootstrap.min.css" rel="stylesheet">
                 <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
                 <script type="text/javascript" src="https://tenhands.net/js/tenhands.loader.v2.0.js"></script>
                 <script>
                    $(function() {
                        user = {...};  // plugin in the user object from the /signIn call

                        TenHands.videoService({videoContainerId:"THVideoContainer",
                                                   user: user,
                                                   onSuccess: onVideoServiceLoad});
                       
$("#callBtn").click(function(e) { e.preventDefault(); var callee = $("#callee").val(); var coreService = gVideoPlayer.coreService(); coreService.getParticipantName(callee, function(name) { gVideoPlayer.launchOutgoingCall({to:callee, callLabel: name, type: "video"}); }); });
} function onVideoServiceLoad(videoService) { gVideoService = videoService; // Store reference to videoService gVideoPlayer = gVideoService.getDefaultVideoPlayer({}); // Get a handle to the default videoPlayer // Add a connectionEventHandler gVideoService.registerConnectionEventHandler(function(connectionStatus) { console.log(connectionStatus); if (connectionStatus.signedIn) {
$("#callBtn").removeAttr("disabled"); $("#loginStatus").html("Signed In as " + user.email);
console.log("Signed In"); } else { console.log("Signed out"); } }); }; </script> </head> <body> <div id="THVideoContainer" style="position:absolute; top:0px; left:0px; height: 100%; width: 100%; visibility: hidden;"> </div>
<form class="well"> <h3>Point 2 Point Calling</h3> <div id="loginStatus"></div><br> <input type="text" id="callee" class="input-large search-query" style="height:28px"> <button type="submit" class="btn" id="callBtn" disabled>Place a Call</button> </form>
</body> </html>

6. Make a call

Now, you should have 2 pages - caller.html and callee.html. The pages are identical except for the user object used to initialize TenHands VideoService. From the caller.html page, type in the callee's id - alice.callee@tenhands.net and click on "place a call" button.

Congrats! You've built your first TenHands app (hopefully in less than 15 minutes). Take a look at API Test Console for a live example of all the APIs.


Code Examples:

Default Player - Download caller.html and callee.html
Custom Player - Download customCaller.html and customCallee.html

Core Concepts

The TenHands platform provides application developers with the capabilities to add real-time communications (RTC) to their existing web applications. The TenHands RTC functionality is exposed to developers via a client side API (javascript) and a server side API (REST).

Users

TenHands has a lightweight mechanism to keep track of users so as to a) route calls in p2p calling scenarios and b) identify users when they join group conferences. The application developer is required to register application users who need to be RTC enabled with TenHands using the REST API (https://tenhands.net/api/v1/user/create). This can be done either at the time of new user sign-up (recommended) and/or through a bulk provisioning process (for existing users).

To use the TenHands API, the application needs to sign into TenHands service using TenHands’s REST API (https://tenhands.net/api/v1/user/signIn). The user json object that’s returned from the REST API call should be used in the TenHands JS API call to load TenHands video service. It is recommended that you make the signIn call at the time a user signs into the host application.

Groups

TenHands uses the concept of groups to distribute presence to users. By definition, a group contains a set of users and a user can belong to multiple groups. When a user is logged into TenHands, that user will receive presence status (online/offline status) of users who belong to any of the groups that the logged in user belongs to.

VideoService, VideoCall, LocalMedia, VideoPlayer and ChatService

The javascript library consists of 5 main classes - VideoService, VideoCall, LocalMedia, VideoPlayer and ChatService. The VideoService class is the primary interface into the TenHands platform and is loaded by the developer by calling the static method - TenHands.videoService().

                    TenHands.videoService({videoContainerId: "video container div id",
                                              user: a valid user object,
                                              onSuccess: onVideoServiceLoad});
                    
Note that an application developer needs to add a container div to the HTML page for holding the TenHands service related objects. This is required since TenHands loads all the JS/CSS files required in a separate iframe within this container div.

The VideoService object allows an application developer to register for connection events, call events and presence events by providing callback functions. It has factory methods to create a videoCall object, and get access to the LocalMedia (your web camera). It also provides methods to get the default video player and the chat service which encapsulate functionality for making/receiving audio/video calls and text chat respectively. Both VideoPlayer and ChatService are higher level abstractions that include UI elements.

An application developer can either use VideoService's lower-level APIs (via the VideoCall object and implementing the appropriate callback handlers) to create a completely customized real-time communications experience or use the built-in VideoPlayer class for p2p and multi-point calling. Similarly, an application developer can use the ChatService to inject a fully-functional chat widget into the HTML document or use the lower level APIs to add real-time text messaging.

TenHands Javascript API

Namespace

TenHands

Namespace for all TenHands classes

Classes

VideoService

The VideoService class provides the following capabilities:

  • Signing in and out of the Tenhands video service
  • Monitoring status of connection with Tenhands video service
  • Monitoring call events (inbound call requests)
  • Monitoring presence of Tenhands peers (busy/online/offline status)
  • Send/Receive real-time messages
  • Access to default VideoPlayer and ChatService widgets
  • Provide factory methods to create the right VideoCall object (incoming, outgoing or conference call)
  • Access to LocalMedia (web camera)

VideoCall

The VideoCall is a low level class that's used to make/accept calls. It also provides methods to control the mute status of audio/video devices and start/stop screen sharing sessions within a call. The application developer is required to implement all the necessary callback functions, and provide a UI to display the video stream and wire up the buttons required for in-call controls (accept/reject/hangup, mic/speaker mute etc.)

LocalMedia

The is a wrapper class that provides access to the user's local video camera outside a call. This class can be used to turn on/off the local camera and render the resulting video feed in the drawing surface. This class should only be used OUTSIDE a live call.

VideoPlayer

The VideoPlayer class is a built-in UI widget that can be used to make/receive point2point and multi-point calls. It's a fully functional communications component that provides all the UI elements and wiring needed for in-call controls (device controls, screen sharing, etc).

ChatService

The ChatService class is a built-in UI widget that can be used to chat and start p2p calls with other users in your own group(s) who are online. The chat widget, currently, doesn't allow you to send any offline messages to other users.

Static Methods

TenHands.videoService(options)

The videoService method should be called to initialize and load the TenHands javascript classes required to work with the TenHands video service. Returns a handle to the VideoService class.

options {
    videoContainerId: string - the div id where all TenHands related JS and plugin code
    will be injected into. Note that the visibility of this container div can be hidden but
    style SHOULD NOT be set to display:none.

    user: json object returned from the /user/signIn REST API

    onSuccess: callback function called once videoService is successfully loaded
    and initialized in the window document.

    autoDetectPlugin: boolean  - whether the JS should automatically detect if
    plugin is missing or outdated and prompt user to download/install plugin. The default
    value is true.

    autoSignIn: boolean  - whether the user should be automatically signed into
    TenHands service after initialization. The default value is true.

    pluginFree: boolean - whether native webRTC, if available, should be used instead of the TenHands plugin.
    This option is only  supported on webRTC compliant browsers (Chrome ver 24+ for now).
    If set to true, the API will use the native webRTC APIs on Chrome ver 24+ and fallback to TenHands
    plugin in case of other browsers. The default value is false.
}

TenHands.loadVideoService(videoContainerId, user, options, onSuccessHandler) @deprecated - use TenHands.videoService(options) instead

The loadVideoService method should be called to initialize and load the TenHands javascript classes required to work with the TenHands video service.

videoContainerId: string - the div id where all TenHands related JS and plugin code
will be injected into. Note that the visibility of this container div can be hidden but
style SHOULD NOT be set to display:none.

user: json object returned from the /user/signIn REST API

options: json object
{
    autoDetectPlugin: boolean  - whether the JS should automatically detect if
    plugin is missing or outdated and prompt user to download/install plugin. The default
    is true.

    autoSignIn: boolean  - whether the user should be automatically signed into
    TenHands service after initialization. The default is true.
}

onSuccessHandler(videoService): callback function called after videoService is
successfully loaded and initialized in the window document.

VideoService

Constructor

Handle to videoService should be obtained by calling the static method - videoService()

Event Handlers

registerConnectionEventHandler(connectionEventHandler)

Applications should register a callback handler to be notified when user is explicitly signed in/out of the service or by external events such as loss of network connectivity. The callback handler is called with a json object (connectionStatus) as the parameter.

connectionStatus {
    signedIn: boolean - true if the user is signed in
}

Sample callback function - 
function connectionEventHandler(connectionStatus) {
    if (connectionStatus.signedIn) {
    }
}

registerPresenceEventHandler(presenceEventHandler)

Applications should register a callback handler to be notified of presence status of the signed-in user’s contacts. The callback handler is called with a json object (presenceEvent) for each contacts’s presence status change.

presenceEvent {
    from: string - email handle of the contact
    status: string - online/idle/busy/offline
}

Sample callback function -
function presenceEventHandler(presenceEvent) {
}

registerAppMessageEventHandler(appMessageEventHandler)

Applications should register a callback handler to be notified of app messages (see "sendAppMessage" method). The callback handler is called with a json object (appMessageEvent) that embeds the application message.

appMessageEvent {
    from: string - sender's id
    message: string - the application message
}

Sample callback function -
function appMessageEventHandler(appMessageEvent) {
}

registerCallEventHandler(callEventHandler)

Applications should register a callback handler to be notified of incoming calls. The callback handler is called with a json object (callData) that contains the incoming call's meta data. The application should set the "onCallStatusEvent" on the callData object and pass the callData as a parameter to VideoService's "createIncomingCall" method.

callData {
    from: string - caller's id
    to: string - callee's id
    type: string - audio|video
    onCallStatusEvent: callback - the callback function to call for in-call status events
}

Sample callback function -
function callEventHandler(callData) {
        console.log("Incoming call from: " + callData.from);
        callData.onCallStatusEvent = onNotifyCallStatusEvent; // app defined callback

        videoCall = videoService.createIncomingCall(callData);
        // In this example, we accept the incoming call automatically.
        // The application can popup a dialog with accept/reject buttons and accept/reject the call
        // based on user selection.
        videoCall.acceptCall();

        //videoCall.rejectCall();    // code to reject incoming call
}

Methods

signIn()

signIn is an asynchronous method. Results are returned via callbacks to connectionEventHandler.

signOut()

signOut is an asynchronous method. Results are returned via callbacks to connectionEventHandler.

VideoCall createIncomingCall(callData)

Creates a new VideoCall object used to accept an incoming call. Pass in call data object from the callEventHandler callback. Additionally, set the call "type" and "onCallStatusEvent" properties on the callData object.

callData {
    type: string - audio|video
    onCallStatusEvent: callback - callStatusHandler
    onScreenShareEvent: callback - screenShareEventHandler
}

VideoCall createOutgoingCall(callData)

Creates a new VideoCall object used to make an outgoing call.

callData {
    to: string - identifier of remote user who you wish to call
    type: string - audio|video
    onCallStatusEvent: callback - callStatusHandler
    onScreenShareEvent: callback - screenShareEventHandler
}

VideoCall createRoomCall(callData)

Creates a new VideoCall object used to join a user's permanent room.

callData {
    to: string - identifier of user whose room you wish to join
    type: string - audio|video
    onConferenceEvent: callback - conferenceEventHandler
    onCallStatusEvent: callback - callStatusHandler
    onScreenShareEvent: callback - screenShareEventHandler
}
                    

VideoCall createConferenceCall(callData)

Creates a new VideoCall object used to join a conference.

callData {
    to: string - conferenceId as recd from createConference REST call
    type: string - audio|video
    onConferenceEvent: callback - conferenceEventHandler
    onCallStatusEvent: callback - callStatusHandler
    onScreenShareEvent: callback - screenShareEventHandler
}

positionVideoStream(options)

Positions the video stream - remote, local or screen sharing video - within the rendered surface. The streams are rendered such that the screen sharing video has highest z-index, the remote video has the next highest z-index and the local video stream is the lowest layer. The xOffset, yOffset are the offset (in pixels) from the top, left corner of the rendering surface. The width and height are the width and height of the rendered stream in pixels.

options: {
    callId: the call id of the video call. Can be obtained from the videoCall.
    stream: string - remote|local|sharing
    zIndex: number - for future use
    xOffset: number - offset from left edge in pixels
    yOffset: number - offset from top edge in pixels
    width: number - width of rendered stream in pixels
    height: number - height of rendered stream in pixels
}

getScreenSharingWindows()

Returns an array of json objects that enumerate the list of application windows that can be shared during an ongoing call. Each object in the array has the following properties: type, id and name.

Format of returned array:
[{type: "desktop", id: "some id", name: "some name"}, {type:"window", id: "some id", name: "some name"},....]

sendAppMessage(messageEvent)

Sends a message to a recipient as identified in messageEvent

messageEvent {
    to: string - message recipient's identifier. This id can also
    be an conferenceId if you want to send the message to all conference participants.
    message: string - Actual message to be sent. If a json is passed as a message,
    it will be stringified and sent to the recipient.
}

getDefaultVideoPlayer(options)

Gets the handle to the default video player. See documentation for VideoPlayer to understand how to use this object.

options {
    onVideoCallReceive(callComplete, callData): callback function that gets called when
    a call is received. If the callback function is specified, then the callback function will
    be called with 2 parameters - a "callComplete" function parameter and a "callData" parameter
    that holds the call data info. The implementor of the "onVideoCallReceive" should call the
    callComplete at the end of the function to receive the call.

    onVideoCallEnd(callData): callback function that gets called after a call ends. The
    callback function will be called with a callData parameter that holds the call data info.

    allowBoxFileSharing: boolean(default: false)

    allowScreenSharing: boolean (default: true)

    customStylesheet: string. Fully qualified URL to your custom stylesheet to skin the
    video player UI. See the videoPlayer skinning section for details.

    buttonSize: string (default: btn-small). the button class (from bootstrap) used for
    the buttons in the video player. Should be one of the following values: btn-large, btn,
    btn-small, btn-mini.
}

getChatService(options)

Gets the handle to the chat service.

options {
    startVideoCall(userEmail, userName): callback function that gets called when the
    video call icon is clicked from the chat window. The callback function will be called
    with 2 parameters - userEmail and userName which will be the email and name of the person
    that was called from the chat window.
}

VideoCall

Constructor

Handle to videoCall should be obtained by creating an incoming call, outgoing call or conference call from the videoService object.

Event Notifications

onCallStatusEvent(eventObject)

This callback handler should be set in the callData object of the method called to create VideoCall. onCallStatusEvent is invoked during call initiation, acceptance, duration and termination to provide the application with in-call notifications. All call status events have a common event property, "event". Other properties are a function of the specific "event"
Call Initiation/ termination/acception
eventObject {
    event:  "callInitiated" | "callTerminated" | "callAccepted"
}

Media added/removed events
eventObject {
    event:  " localMediaAdded", " remoteMediaAdded", " remoteMediaRemoved"
    eventData: {
        audioTracks: [{
            enabled: true | false
            label:"audio_label"}]
            videoTracks: [{
                enabled: true | false
                label:"audio_label"}]
            }
        }

Call rejected, the reason property contains additional information
eventObject {
    event:  "callRejected"
    reason:  "busy" | "Internal Server Error" | "network-error-udp-port-block" |
     "media-server-not-reachable" | "peer-connection-error" | "plugin-error" |
    "cameraMicAccessFailed"
}

Video size has changed
eventObject {
    event:  "videoSizeChanged"
    eventData: {
        callId: "string"
        video: "local" | "remote" | "screen"
        width:integer
        height: integer
    }
}

Audio settings have been changed
eventObject {
    event:  "audioSettingsChanged"
}

onScreenShareEvent(screenShareEvent)

This callback handler should be set in the callData object of the method called to create VideoCall. This handler would be notified of screen share messages that are sent when a screen sharing session is started or stopped. The callback handler is called with a json object (screenShareEvent) that contains the screen share message.

screenShareEvent {
    from: string - screen share host's id
    message: json object - the screen share message
    {
        screenShare: string - join|leave
        bridge: string - address of screen share session (used internally by TenHands service)
    }
}

Sample callback function -
function screenShareEventHandler(screenShareEvent) {
    var screenShareMsg = screenShareEvent.message;
    if( screenShareMsg.screenShare == "join" ) {
        videoCall.joinShareScreen(screenShareMsg);

        // AppCode: Update UI to reflect new screen sharing session

        // Show screen sharing video
        videoCall.renderSharingVideo(true);

    } else if(screenShareMsg.screenShare == "leave") {
        videoCall.stopShareScreen( );

        // AppCode: Update UI to reflect screen sharing session has stopped

        // Hide screen sharing video
        videoCall.renderSharingVideo(false);
    }
}

onConferenceEvent(conferenceEvent)

This callback handler should be set in the callData object of the method called to create VideoCall. This handler would be notified of conference events (participants joining/leaving, etc) that are sent when a participant is in a multi-party conference call. The callback handler is called with a json object (conferenceEvent) that contains the conference event messages.

conferenceEvent {
    command: string - participant-join|participant-leave|participant-list
    conference: string - unique id that identifies the current conference
    from: string - participant's id in case of a participant-join/participant-leave command
    participants: json array of initial participants (available when command is participant-list)
}

Sample callback function - 
function conferenceEventHandler(conferenceEvent) {
     var participant = conferenceEvent.from;
     if (conferenceInfo.command == "participant-join") {
        // e.g. show a message in the UI indicating new participant has joined the conference
        // addConferenceParticipant(participant);
     } else if (conferenceInfo.command == "participant-leave") {
        // e.g. show a message in the UI indicating a participant has left the conference
        // removeConferenceParticipant(participant);
     } else if (conferenceInfo.command == "participant-list") {
        // e.g. Update UI to show list of existing participants
        // addInitialParticipants(conferenceEvent.participants);
     }
}

Methods

callId()

Unique identifier of the video call.

call()

Makes an outgoing call. This is an asynchronous method. Results are returned by a call to onCallStatusEvent.

acceptCall()

Accepts an incoming call. Results are returned by subsequent calls to onCallStatusEvent.

rejectCall()

Rejects a call. Results are returned by subsequent calls to onCallStatusEvent.

terminateCall()

Terminate a call. Results are returned by subsequent calls to onCallStatusEvent.

LocalMedia getLocalMedia(options)

Used to turn on local video camera outside of a call.

setAudioOutputMute(bool)

Mute/Unmute the audio output channel (speakers).

bool getAudioOutputMute()

Returns the mute status (true if muted) of the audio output channel (speakers).

setAudioInputMute(bool)

Mute/Unmute the audio input channel (microphone).

bool getAudioInputMute()

Returns the mute status (true if muted) of the audio input channel (microphone).

setVideoMute(bool)

Mute/Unmute the video being transmitted to remote end.

bool getVideoMute()

Returns the mute status (true if muted) of the video camera.

startShareScreen(shareData)

Start a screen sharing session as presenter. The methods requires one parameter - a json object that identifies the application window to be shared. This object should be one of the array elements from a call to VideoService's getScreenSharingWindows(). Calling this method will broadcast a "screenShare event (join)" to all other participants in the call. Recipients of this event (callbacks registered via the onscreenShareEvent) will typically respond to the "screenShare event" to join the ongoing screen share session.

stopShareScreen()

Stop a screen sharing session that was started. If you are the presenter, then calling this API will stop the ongoing screen sharing session and broadcast a "leave" screenShareEvent to all other participants. Other participants who receive a "leave" screenShareEvent should call the stopShareScreen method to leave the screen sharing session.

joinShareScreen(shareMessage)

Join an ongoing screen sharing session that was started by a previous call to startShareScreen() API. Typically, this method should be called in response to a "join" screenShareEvent. This method takes one parameter - the message property from the screenShareEvent object passed in to the screenShareEvent callback handler.

isRemoteVideoVisible

Returns true/false to indicate whether the remote video is rendered or not.

renderRemoteVideo(bool)

Shows/hide remote video stream (when available). This method should typically be called after the remote video is ready to be rendered.

renderLocalVideo(bool)

Shows/hide local video stream (when available). This method should typically be called after the local video is ready to be rendered.

renderSharingVideo(bool)

Shows/hide screen sharing video stream (when available). This method should typically be called after the screen sharing video is ready to be rendered.

LocalMedia

Constructor

A handle to LocalMedia should be obtained by calling getLocalMedia(options) on the videoService object

Methods

show()

Shows the video feed from your local video camera

hide()

Hides the video feed from your local video camera

VideoPlayer

Constructor

Handle to videoPlayer should be obtained by calling getDefaultVideoPlayer(options) on the videoService object

Methods

launchOutgoingCall(callData)

Launches the player and makes an outgoing point-to-point call to user identified by the "to" field

    callData {

    remoteEmail: string - remote user to call  @deprecated - use the "to" field instead

    to: string -  remote user to call

    callLabel: string - call label to use in video player

    type: string - audio|video

    audio: bool @deprecated - use "type" instead

    video: bool @deprecated - use "type" instead

}

joinRoom(callData)

Launches the player and joins a user's permanent room.


callData {

to: string -  id of user whose room you wish to join

callLabel: string - call label to use in video player

type: string - audio|video

}

joinConference(callData)

Launches the player and joins a conference as identified by the conference Id.


callData {

conferenceId: string - conference Id from REST call @deprecated - use the "to" field instead

to: string -  conference Id from REST call

callLabel: string - call label to use in video player

type: string - audio|video

audio: bool @deprecated - use "type" instead

video: bool @deprecated - use "type" instead

}

Skinning your VideoPlayer

With custom CSS, you can customize the look and feel of the TenHands video player to match the rest of your web application. Your custom CSS can be set up by providing your stylesheet location in the options parameter of the "getDefaultVideoPlayer" call. Note that your custom CSS rules are appended to the document viewer's default stylesheet.

The videoPlayer's default stylesheet is available for reference here.

Sample Custom CSS

With the following custom CSS rules, the logo of the player is changed to webRTC's logo and the background color of the "share screen" button is changed to blue.

/*custom logo*/
.inCallLogo {
    background: url('http://tenhands.net/images/webrtc.png');
    background-size: 100%;
}

/*button styles for share screen button*/
.btn {
    border-color: #7aa7d8 !important;
    background: #d4e4ef; /* Old browsers */
    background: -moz-linear-gradient(top,  #d4e4ef 0%, #86aecc 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#d4e4ef), color-stop(100%,#86aecc)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  #d4e4ef 0%,#86aecc 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  #d4e4ef 0%,#86aecc 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  #d4e4ef 0%,#86aecc 100%); /* IE10+ */
    background: linear-gradient(to bottom,  #d4e4ef 0%,#86aecc 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#d4e4ef', endColorstr='#86aecc',GradientType=0 ); /* IE6-9 */
    background-clip: padding-box !important;
}

.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled] {
    border-color: #72a7e0; !important;
    background: #7aa7d8; /* Old browsers */
    background: -moz-linear-gradient(top, #7aa7d8 0%, #d1e3f3 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#7aa7d8), color-stop(100%,#d1e3f3)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top, #7aa7d8 0%,#d1e3f3 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top, #7aa7d8 0%,#d1e3f3 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top, #7aa7d8 0%,#d1e3f3 100%); /* IE10+ */
    background: linear-gradient(to bottom, #7aa7d8 0%,#d1e3f3 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#7aa7d8', endColorstr='#d1e3f3',GradientType=0 ); /* IE6-9 */
    background-clip: padding-box !important;
}
                    

ChatService

Constructor

Handle to chatService should be obtained by calling getChatService(options) on the videoService object

Methods

insertChatManager()

Injects the TenHands chat window into the current HTML document

removeChatManager()

Removes the TenHands chat window from the current HTML document

iOS SDK

Download iOS SDK

TenHands REST API

Overview

The TenHands REST API allows you to:

  • Create/Update/Delete Users
  • Create/Update/Delete Groups
  • List Users/Groups
  • Sign In/Sign Out from TenHands service
  • Create Group Conference

Important Security Note: Your partner apiKey is private and should not shared with anyone nor used directly from the browser to make API calls. You should use the REST APIs that require an API key from your backend server and only use the APIs that take authToken as a parameter directly from the browser.

Create a new group

POST https://tenhands.net/api/v1/group/create


Request parameters

  • apiKey - Your TenHands API key
  • name - full name of group
Sample request:
curl https://tenhands.net/api/v1/group/create --data "apiKey=[API_KEY]&name=TenHands Inc"

Response properties

  • userGroupId - unique id of new group
Sample response:
{ "userGroupId": "${uniqueId}" }

Delete a group

POST https://tenhands.net/api/v1/group/delete


Request parameters

  • apiKey - Your TenHands API key
  • name - full name of group
Sample request:
curl https://tenhands.net/api/v1/group/delete --data "apiKey=[API_KEY]&name=TenHands Inc"

Response properties

  • userGroupId - id of group to delete
Sample response:
{"Status":"User group deleted"}

List all groups

POST https://tenhands.net/api/v1/group/list


Request parameters

  • apiKey - Your TenHands API key
Sample request:
curl https://tenhands.net/api/v1/group/list --data "apiKey=[API_KEY]"

Response properties

  • Array - array of groups that have been created
Sample response:
[{"userGroupId":"d43fd556-f393-11e1-af96-12313b0d1188","partnerApplicationId":"7593a44a-dc02-11e1-bded-12313b0d1188","userGroupName":"ABC","status":1,"dateCreated":1346435215081,"dateModified":1346435215081},{"userGroupId":"d6a9e444-f393-11e1-af96-12313b0d1188","partnerApplicationId":"7593a44a-dc02-11e1-bded-12313b0d1188","userGroupName":"Test","status":1,"dateCreated":1346435219132,"dateModified":1346435219132}]

List users in a group

POST https://tenhands.net/api/v1/group/users


Request parameters

  • apiKey - Your TenHands API key
  • name - group name
Sample request:
curl https://tenhands.net/api/v1/group/users --data "apiKey=[API_KEY]&name=TenHands Inc"

Response properties

  • Array - list of users that belong to this group
Sample response:
[{"id":"be81888e-f2ff-11e1-af96-12313b0d1188","email":"karthik@tenhands.net","photoUrl":"","displayName":"Karthik Kothandaraman"},{"id":"eb5e6f6e-01b7-11e1-ad43-12313d01cade","email":"venky@tenhands.net","photoUrl":"","displayName":"Venky"}]

Create or update a user

POST https://tenhands.net/api/v1/user/create


Request parameters

  • apiKey - Your TenHands API key
  • email - email address of new user
  • name - full name of new user
  • photoUrl - User’s photo URL (optional)
  • orgId - (optional: id of organization that user belongs) - Deprecated
  • groupNames - comma separated list of groups that the user belongs to. These groups should have been created previously
Sample request:
curl https://tenhands.net/api/v1/user/create --data "apiKey=${API_KEY}&email=karthik@tenhands.net&name=Karthik Kothandaraman"

Response properties

  • userId - unique id of new user
Sample response:
{ "userId": "${uniqueId}" }

Notes: The email address of the new user is meant to uniquely identify the user in our system for call routing purposes. We will not use that email address to contact/notify the user at any point. If you do not know the email of the user or do now want to share the email address, you can use some unique id formatted as an email address. For e.g. ${uniqueid}@acme.widgets
All users that belong to the same group will share presence status with each other.

Update existing user @deprecated (Use /user/create instead)

POST https://tenhands.net/api/v1/user/update


Request parameters

  • apiKey - Your TenHands API key
  • email - email address of new user
  • name - full name of new user
  • photoUrl - User’s photo URL (optional)
  • orgId - (optional: id of organization that user belongs)
Sample request:
curl https://tenhands.net/api/v1/user/update --data "apiKey=${API_KEY}&email=karthik@tenhands.net&name=Karthik Kothandaraman"

Response properties

  • userId - unique id of new user
Sample response:
{ "userId": "${uniqueId}" }

Notes: All users that belong to the same organization will share presence status with each other. If a user with the email address does not already exist, then a 400 bad request response will be sent.

Get user details

POST https://tenhands.net/api/v1/user/get


Request parameters

  • apiKey - Your TenHands API token
  • email - the email of the registered user
Sample request:
curl https://tenhands.net/api/v1/user/get --data "apiKey=${API_KEY}&email=karthik@tenhands.net"

Response properties

  • id: user id
  • email: user’s email address
  • photoUrl: user’s photoURL used in the video player
  • displayName: display name of the user used in the video player
Sample response:
{"id":"be81888e-f2ff-11e1-af96-12313b0d1188","email":"karthik@tenhands.net","photoUrl":"","displayName":"Karthik Kothandaraman"}

List all users

POST https://tenhands.net/api/v1/user/list


Request parameters

  • apiKey - Your TenHands API token
Sample request:
curl https://tenhands.net/api/v1/user/list --data "apiKey=${API_KEY}"

Response properties

  • Array: List of all users that have been created under this partner key
Sample response:
[{"id":"be81888e-f2ff-11e1-af96-12313b0d1188","email":"karthik@tenhands.net","photoUrl":"","displayName":"Karthik Kothandaraman"},{"id":"eb5e6f6e-01b7-11e1-ad43-12313d01cade","email":"venky@tenhands.net","photoUrl":"","displayName":"Venky"}]

Add groups to user

POST https://tenhands.net/api/v1/user/groups/add


Request parameters

  • apiKey - Your TenHands API token
  • email - email address of user
  • groupNames - comma separated list of group names that user should be added to. These groups should have been created apriori using the /group/create API.
Sample request:
curl https://tenhands.net/api/v1/user/groups/add --data "apiKey=${API_KEY}&email=karthik@tenhands.net&groupNames=TenHands Inc"

Response properties

  • Status: string that indicates success
Sample response:
{ "Status":"User group modified"} }

List of groups that belong to a user

POST https://tenhands.net/api/v1/user/groups/list


Request parameters

  • apiKey - Your TenHands API token
  • email - email address of user
Sample request:
curl https://tenhands.net/api/v1/user/groups/list --data "apiKey=${API_KEY}&email=karthik@tenhands.net"

Response properties

  • Array: list of groups that user belongs to
Sample response:
[ {"userGroupId":"dd96600a-f3a9-11e1-af96-12313b0d1188","partnerApplicationId":"7593a44a-dc02-11e1-bded-12313b0d1188","userGroupName":"My Test","status":1,"dateCreated":1346444677157,"dateModified":1346444677157} ]

Remove groups from user

POST https://tenhands.net/api/v1/user/groups/remove


Request parameters

  • apiKey - Your TenHands API token
  • email - email address of user
  • groupNames - comma separated list of group names that user should be removed from.
Sample request:
curl https://tenhands.net/api/v1/user/groups/remove --data "apiKey=${API_KEY}&email=karthik@tenhands.net&groupNames=TenHands Inc"

Response properties

  • Status: string that indicates success
Sample response:
{ "Status":"User group modified"} }

Delete a user

POST https://tenhands.net/api/v1/user/delete


Request parameters

  • apiKey - Your TenHands API token
  • email - email address of user
Sample request:
curl https://tenhands.net/api/v1/user/delete --data "apiKey=${API_KEY}&email=karthik@tenhands.net"

Response properties

none
Sample response:
{ }

Notes: The delete user request will only work for those users that were created by the partner

User sign in

POST https://tenhands.net/api/v1/user/signIn


Request parameters

  • apiKey - Your TenHands API token
  • email - email address of user
Sample request:
curl https://tenhands.net/api/v1/user/signIn --data "apiKey=${API_KEY}&email=karthik@tenhands.net"

Response properties

  • userId - unique id of user
  • email - email of user
  • authToken - sign in token (will be valid until next signIn)
Sample response:
{ "userId": "${uniqueId}", "email": "karthik@tenhands.net", "authToken": "${authToken}", "type": "${userType}" }

Notes: The sign in request will only work for those users that were created by the partner

User sign out

POST https://tenhands.net/api/v1/user/signOut


Request parameters

  • authToken - Your TenHands authToken obtained from /user/signIn request
  • email - email address of user
Sample request:
curl https://tenhands.net/api/v1/user/signOut --data "authToken=${AUTH_TOKEN}&email=karthik@tenhands.net"

Response properties

None
Sample response:
{ }

Notes: The sign out request will only work for those users that were created by the partner

Create a group conference

POST https://tenhands.net/api/v1/conference/create


Request parameters

  • owner - email address of signed in user
  • authToken - Your TenHands authToken obtained from /user/signIn request
  • title - Meeting title
  • duration - duration of the meeting in mins (optional: defaults to 240 mins)
Sample request:
curl https://tenhands.net/api/v1/conference/create --data "authToken=${AUTH_TOKEN}&title=My first conference

Response properties

  • url - unique url for the conference.
  • conferenceId - id used to join a conference if using the TenHands JS API
Sample response:
{ "url": "http://tenhands.net/conference/${uniqueId}", "conferenceId": "${confId}" }

Notes: The owner field entered while creating a group conference is for future use by TenHands. Currently, the owner and participants in a conference get the same privileges. However, in the future we may use this field to implement moderator/host controls for the conference.

Create a new organization @deprecated (use /group/create instead)

POST https://tenhands.net/api/v1/organization/create


Request parameters

  • apiKey - Your TenHands API key
  • name - full name of organization
Sample request:
curl https://tenhands.net/api/v1/organization/create --data "apiKey=[API_KEY]&name=TenHands Inc"

Response properties

  • orgId - unique id of new organization
Sample response:
{ "orgId": "${uniqueId}" }

Delete an organization @deprecated (use /group/delete instead)

POST https://tenhands.net/api/v1/organization/delete


Request parameters

  • apiKey - Your TenHands API key
  • orgId - id of organization
Sample request:
curl https://tenhands.net/api/v1/organization/delete --data "apiKey=[API_KEY]&orgId=[orgId]"

Response properties

none
Sample response:
{ }

Notes: If the organization has existing users, then that organization cannot be deleted. The request will return a 400 bad request with appropriate message in the response field.

ERROR HANDLING

Status Codes

The HTTP status code of TenHands API responses should checked to determine if a request was valid and interpreted correctly. Here's a list of the error codes you can receive:

  • 200 OK - The request was received successfully.
  • 400 Bad Request - There was a problem with your request parameters. Check the error field of the response object for info on what was wrong.
  • 401 Unauthorized - Your API token in the token parameter was either missing or incorrect.
  • 404 Not Found - The API method was not found. Check your request URL for typos.
  • 405 Method Not Allowed - An incorrect HTTP verb (i.e. GET, POST, etc) was used for the request
  • 5XX - There was a system error that TenHands could not recover from. Report this to us if you see such errors repeatedly.