Fork me on GitHub
RESTful, WebSockets, RabbitMQ, MQTT and UnixSockets API

Since version 0.0.6, there are three different ways to interact with a Janus instance: a Plain HTTP REST Interface (the default), a WebSockets Interface, a RabbitMQ interface, MQTT interface and a UnixSockets interface (both optional, need an external library to be available). All of the interfaces use the same messages (in terms of requests, responses and notifications), so almost all the concepts described in the Plain HTTP REST Interface section apply to the WebSocket/RabbitMQ/MQTT/UnixSockets interfaces as well. Besides, since version 0.1.0 the transport mechanism for the Janus API has been made modular, which means other protocols for transporting Janus API messages might become available in the future: considering the Janus protocol is supposed to be mostly agnostic to the protocol it is transported on, the concepts explained in the following sections should apply to those as well.

As it will be explained later in the WebSockets Interface, RabbitMQ interface and UnixSockets interface sections below, the only differences come when addressing specific sessions/handles and in part in how you handle notifications using something different than the REST interface: in fact, since with WebSockets, RabbitMQ, MQTT and UnixSockets (and, as anticipated, with other protocols that may be added in the future too) there's no REST-based path involved, you'll need a couple of additional identifiers to bridge the gap. Some details are also provided in case you're interested in Authenticating the Janus API.

Plain HTTP REST Interface

As anticipated in the JavaScript API documentation, the gateway deploys a RESTful interface that clients can exploit. The janus.js library makes use of it in a transparent way, but if you're interested in more details about it (e.g., because you want to talk to the gateway your own way), this page described the interface and the protocol the API exposes and uses.

There are basically three types/levels of endpoints you can meet:

  1. The gateway root (/janus by default, but configurable), which you only POST to in order to create a gateway session;
  2. The session endpoint (e.g., /janus/12345678, using the identifier retrieved with a previous create), which you either send a GET to (long poll for events and messages from plugins) or a POST (to create plugin handles or manipulate the session);
  3. The plugin handle endpoint (e.g., /janus/12345678/98765432, appending the handle identifier to the session one) which you only send POST messages to (messages/negotiations for a plugin, handle manipulation), as all events related to this handle would be received in the session endpoint GET (the janus.js library would redirect the incoming messages to the right handle internally).

Messages and requests you can send to and receive from any of the above mentioned endpoints are described in the following chapters. In general, all messages share at least two fields:

Different messages will of course add different information to this base syntax. Error message, instead, usually have these fields:

An example of an error is presented here:

{
        "janus" : "error",
        "transaction" : "a1b2c3d4"
        "error" : {
                "code" : 458
                "reason" : "Could not find session 12345678"
        }
}

Getting info about the Janus instance

The API exposes an info endpoint you can query to get information about the Janus instance you're talking to. Specifically, it returns information about the version of the Janus server, whether some of the optional features (e.g., Data Channels or IPv6) are supported or not, and which transports and plugins are available.

To get this information, just send an HTTP GET message to the info endpoint (e.g., http://yourserver:8088/janus/info), which will return something like this:

{
        "janus": "server_info",
        "transaction": "i1bzIL341Kl2",
        "name": "Janus WebRTC Gateway",
        "version": 10,
        "version_string": "0.1.0",
        "author": "Meetecho s.r.l.",
        "data_channels": "true",        // Data channels are supported
        "ipv6": "false",                        // IPv6 is not configured
        "ice-tcp": "false",                     // ICE-TCP support is disabled
        "transports": {
                "janus.transport.http": {
                        "name": "JANUS REST (HTTP/HTTPS) transport plugin",
                        "author": "Meetecho s.r.l.",
                        "description": "This transport plugin adds REST (HTTP/HTTPS) support to the Janus API via libmicrohttpd.",
                        "version_string": "0.0.1",
                        "version": 1
                },
                [..]    // Other transport plugins
        },
        "plugins": {
                "janus.plugin.sip": {           // The SIP plugin is available
                        "version_string": "0.0.2",
                        "description": "This is a simple SIP plugin for Janus, allowing WebRTC peers to register at a SIP server and call SIP user agents through the gateway.",
                        "author": "Meetecho s.r.l.",
                        "name": "JANUS SIP plugin",
                        "version": 2
                },
                "janus.plugin.videoroom": {     // The Video MCU plugin is available
                        "version_string": "0.0.3",
                        "description": "This is a plugin implementing a videoconferencing MCU for Janus, something like Licode.",
                        "author": "Meetecho s.r.l.",
                        "name": "JANUS VideoRoom plugin",
                        "version": 3
                },
                [..]    // Other plugins
        }

You can use this information to selectively enable or disable features in your application according to what's available in the Janus instance you're trying to contact.

The gateway root

The gateway root is /janus by default but, as anticipated, it is configurable, either via command line or in the janus.cfg configuration.

You can only contact the gateway root when you want to create a new session with the gateway. To do so, you need to POST the a janus "create" JSON message to the gateway:

{
        "janus" : "create",
        "transaction" : "<random alphanumeric string>"
}

If the request is successful, you'll receive the unique session identifier in a response formatted like this:

{
        "janus" : "success",
        "transaction" : "<same as the request>",
        "data" : {
                "id" : <unique integer session ID>
        }
}

In case of an error, you'll receive an error message as the one introduced before. This request, if issued with a POST to the gateway root, can only fail if you miss any of the required fields in the request.

The session endpoint

Once you've created a session, a new endpoint you can use is created in the gateway. Specifically, the new endpoint is constructed by concatenating the gateway root and the session identifier you've been returned (e.g., /janus/12345678).

This endpoint can be used in two different ways:

  1. using a parameter-less GET request to the endpoint, you'll issue a long-poll request to be notified about events and incoming messages from this session;
  2. using a POST request to send JSON messages, you'll interact with the session itself.

Long-poll requests
The long-poll will only trigger events related to messages you're being sent from plugins, and as such will be clearer to understand once you read the The plugin handle endpoint section. That said, the events are formatted this way:

An example of such an event (in this case, sent by the janus_echotest.c plugin in response to a request) is presented here:

{
        "janus" : "event",
        "sender" : 1815153248,
        "transaction" : "sBJNyUhH6Vc6",
        "plugindata" : {
                "plugin": "janus.plugin.echotest",
                "data" : {
                        "echotest" : "event",
                        "result" : "ok"
                }
        },
}

The long-poll request has a 30 seconds timeout. If it has no event to report, a simple keep-alive message will be triggered:

{
        "janus" : "keepalive",
}

As with all long-poll based approaches, it's up to your application to send a new polling request as soon as an event or a keep-alive has been received.

Notice that, by default, the long poll returns a single event: that is, as soon as a message becomes available in the session queue, that event is returned and the long poll closes. If you want to receive more events within the context of the same long poll, you can pass the maxev query string parameter to the GET, e.g.:

GET http://host:port/janus/<sessionid>?maxev=5
[
        {
                // Event #1
                "janus" : "event",
                [..]
        },
        {
                // Event #2
                "janus" : "event",
                [..]
        },
        [..]
]

This request will instruct the gateway to return at maximum 5 events within the context of the same long poll, formatted as a JSON array of events. Please beware that this does NOT mean that you'll always get 5 events this way: it only means that, if a message becomes available in the queue and more events are present as well, Janus will return more than one without needing you to send multiple long polls immediately thereafter to get them. For this reason, don't be surprised if even with a maxev parameter set, you'll still get a single event being notified as the sole object in the returned array.


Interacting with the session
To interact with the session, e.g., to create a new handle to attach to a plugin or destroy the current session, you need to send a POST JSON message to the session endpoint.

To attach to a plugin in order to exploit its features, you need to POST a janus "attach" JSON message to the gateway; you'll need of course to provide information on the plugin you want to attach to, which can be done using the plugin field:

{
        "janus" : "attach",
        "plugin" : "<the plugin's unique package name>",
        "transaction" : "<random string>"
}

If the request is successful, you'll receive the unique plugin handle identifier in a response formatted the same way as the session create one, that is like this:

{
        "janus" : "success",
        "transaction" : "<same as the request>",
        "data" : {
                "id" : <unique integer plugin handle ID>
        }
}

In case of an error, you'll receive an error message as the one introduced before. This request, if issued with a POST to a valid session endpoint, can only fail if you miss any of the required fields in the request or if the plugin you requested is not available in the gateway.

To destroy the current session, instead, just send a "destroy" janus request:

{
        "janus" : "destroy",
        "transaction" : "<random string>"
}

This will also destroy the endpoint created for this session. If your session is currently managing one or more plugin handles, make sure you destroy them first (as explained in the next section). The gateway tries to do this automatically when receiving a session destroy request, but a cleaner approach on the client side would help nonetheless avoid potential issues.

The plugin handle endpoint

Once you've created a plugin handle, a new endpoint you can use is created in the gateway. Specifically, the new endpoint is constructed by concatenating the gateway root, the session identifier and the new plugin handle identifier you've been returned (e.g., /janus/12345678/98765432).

You can use this plugin handle for everything that is related to the communication with a plugin, that is, send the plugin a message, negotiate a WebRTC connection to attach to the plugin, and so on.

To send a plugin a message/request, you need to POST the handle endpoint a janus "message" JSON payload. The body field will have to contain a plugin-specific JSON payload. In case the message also needs to convey WebRTC-related negotiation information, a jsep field containing the JSON-ified version of the JSEP object can be attached as well.

Note
If you attach a jsep object, whether it's an offer or an answer, you're stating your will to negotiate a PeerConnection. This means that an empty or invalid jsep object will trigger a validation and will cause the whole request to fail, so make sure you exclude the field completely from your request if all you're interested into is sending a message to a plugin.

Here's an example of a message you may send the janus_echotest.c plugin to mute your audio:

{
        "janus" : "message",
        "transaction" : "sBJNyUhH6Vc6",
        "body" : {
                "audio" : false
        }
}

The same message containing negotiation information as well, instead, (an OFFER, in this example), is presented here:

{
        "janus" : "message",
        "transaction" : "sBJNyUhH6Vc6",
        "body" : {
                "audio" : false
        },
        "jsep" : {
                "type" : "offer",
                "sdp" : "v=0\r\no=[..more sdp stuff..]"
        }
}

Please notice that, if for any reason you don't want to use the trickling of ICE candidates from your application (which means you'll include them all in the SDP OFFER or ANSWER, which is usually not recommended), you'll have to add an additional "trickle" : false attribute to the "jsep" object, to explicitly tell Janus you won't send any trickle candidate (by default Janus will always assume support for trickle).

If you're going to trickle candidates, instead, there is an ad-hoc message you can use to do so which is called, unsurprisingly, trickle and which you can use to send one or more trickle candidates to Janus. Since such a message is related to a specific PeerConnection, it will need to be addressed to the right Handle just as the message introduced previously. A trickle message can contain three different kind of information:

This is an example of a single candidate being trickled:

{
        "janus" : "trickle",
        "transaction" : "hehe83hd8dw12e",
        "candidate" : {
                "sdpMid" : "video",
                "sdpMLineIndex" : 1,
                "candidate" : "..."
        }
}

This, instead, is an example of how to group more trickle candidates in a single request (particularly useful if you're wrapping Janus in your server and want to reduce the number of transactions):

{
        "janus" : "trickle",
        "transaction" : "hehe83hd8dw12e",
        "candidates" : [ 
                {
                        "sdpMid" : "video",
                        "sdpMLineIndex" : 1,
                        "candidate" : "..."
                },
                {
                        "sdpMid" : "video",
                        "sdpMLineIndex" : 1,
                        "candidate" : "..."
                },
                [..]
        ]
}

Finally, this is how you can tell Janus that you sent all the trickle candidates that were gathered:

{
        "janus" : "trickle",
        "transaction" : "hehe83hd8dw12e",
        "candidate" : {
                "completed" : true
        }
}

Plugins may handle this requests synchronously or asynchronously. In the former, plugins would return a response to the request itself immediately; in the latter, instead, the plugin would only notify a successful reception of the request, which it would process later. Considering the asynchronous nature of the Janus API, a successful management of such messages within the gateway would in such case result in a janus "ack" messages being sent back to the client. A logical response to those messages, if needed, would be provided as an event in the long-poll interface described previously, and clients would be able to match it to the original request by means of the transaction identifiers. It is worth noting, though, that should a WebRTC negotiation be involved you don't have to expect an ANSWER to your OFFER to be sent back in the same transaction. A plugin may decide, in its application logic, to not provide you with an ANSWER right away, but only after some internal state changes occur. It's up to your application to handle the negotiation state accordingly.

An example of an "ack" being sent back to the client, using the previous sample request as a reference, is presented here:

{
        "janus" : "ack",
        "transaction" : "sBJNyUhH6Vc6"
}

If you receive this ack instead of a "success" response, you can be sure the plugin has received the message, and is going to process it soon.

In case of an error, instead, you'll receive an error message as the one introduced before. This request, if issued with a POST to a valid plugin handle endpoint, can only fail if you miss any of the required fields in the request, if the plugin you tried to contact is not available in the gateway anymore, if an error occurred in the plugin when trying to receive the message or if the jsep SDP you may have provided is invalid.

To destroy the plugin handle, instead, just send a "detach" janus request:

{
        "janus" : "detach",
        "transaction" : "<random string>"
}

This will also destroy the endpoint created for this plugin handle. If your plugin handle is also managing an ongoing WebRTC connection with the plugin, make sure it is torn down as part of this process. The plugin implementation and the gateway core should do this automatically, but implementing the right behaviour in clients would help avoid potential issues nonetheless.

If you're interested in keeping the handle alive but want to hang up the associated PeerConnection, if available, just send a "hangup" janus request:

{
        "janus" : "hangup",
        "transaction" : "<random string>"
}

This is usually not required, as you can typically just hangup your WebRTC PeerConnection normally and Janus will figure out it's gone by itself. Anyway, there are cases where this might be useful (e.g., the connection was stuck in some weird ICE/DTLS state) as it can be used to reset the connection state for the handle.

WebRTC-related events

As anticipated in the previous sections, Janus can send events and notifications at any time through the long poll channel (or, as it will be explained later, through the related push mechanisms made available by the WebSockets Interface, RabbitMQ interface and UnixSockets interface ). While this channel is mostly used to convey asynchronous notifications originated by plugins as part of the messaging they may have with the application using it, the same channel is actually used by Janus to trigger events related to different aspects pertaining a specific handle.

In particular, for each handle involving a PeerConnection Janus provides notifications about its current state. To do so, the following events may be received as well:

As such, to monitor the status of a PeerConnection as seen from Janus you can make use of these events to track what's going on. A correct flow for an active PeerConnection would be one that, after a WebRTC negotiation and setup, results in a webrtcup event followed by two media events (in case both audio and video have been negotiated) specifying that the first audio/video packets have been received. A hangup event would inform the user/application that no media is being exchanged with Janus anymore.

Here are a few examples of how these events may look like.

A PeerConnection becoming ready:

{
        "janus" : "webrtcup",
        session_id: <the session identifier>,
        sender: <the handle identifier>
}

First audio bytes being received by Janus:

{
        "janus" : "media",
        session_id: <the session identifier>,
        sender: <the handle identifier>,
        "type" : "audio",
        "receiving" : true
}

Audio not getting to Janus anymore for some reason:

{
        "janus" : "media",
        "session_id" : <the session identifier>,
        "sender" : <the handle identifier>
        "type" : "audio",
        "receiving" : false
}

Audio getting to Janus again (same message as first audio):

{
        "janus" : "media",
        "session_id" : <the session identifier>,
        "sender" : <the handle identifier>
        "type" : "audio",
        "receiving" : true
}

Janus reporting problems sending media to a user (user sent many NACKs in the last second; uplink=true is from Janus' perspective):

{
        "janus" : "slowlink",
        "session_id" : <the session identifier>,
        "sender" : <the handle identifier>
        "uplink" : true,
        "nacks" : <number of NACKs in the last second>
}

PeerConnection closed for a DTLS alert (normal shutdown):

{
        "janus" : "hangup",
        "session_id" : <the session identifier>,
        "sender" : <the handle identifier>,
        "reason" : "DTLS alert"
}

It is important to point out that the media event notifications only apply if your PeerConnection is going to actually send media to Janus. A recvonly PeerConnection, for instance (e.g., as the Streaming plugin would create) would never trigger any media event, as Janus would never be receiving media, but only send it.

WebSockets Interface

In recent version of Janus we added support for WebSockets to control the gateway, along the already existing (and still the default) REST API. In fact, while WebSockets still present some more issues in terms of reachability when compared to plain HTTP, they definitely provide a more efficient means for implementing a bidirectional communication. This is especially useful if you're wrapping the Janus API on your servers, as it allows you to avoid all the noise and overhead introduced by several concurrent HTTP transactions and long polls by relying on what may be seen as a single "control channel".

To interact with Janus using WebSockets you MUST specify a specific subprotocol, named janus-protocol, e.g.,

var websocket = new WebSocket('ws://1.2.3.4:8188', 'janus-protocol');

The janus.js library does this automatically.

As anticipated at the beginning of this section, the actual messages being exchanged are exactly the same. This means that all the concepts introduced before still apply: you still create a session, attach to a plugin and interact with it exactly the same way. What is different is, of course, the REST path approach that becomes unavailable when using a WebSocket as a control channel. To address the idenfitiers that become missing using WebSockets, you'll need to add additional fields to the requests when necessary.

So, when you want to create a session using the REST API, you send a POST to the gateway base path:

{
        "janus" : "create",
        "transaction" : "<random alphanumeric string>"
}

The same applies if you're interested in getting generic info from the Janus instance. Since there's no GET you can use, a specific janus request type called info is available:

{
        "janus" : "info",
        "transaction" : "<random alphanumeric string>"
}

Since you'd contact the base path for both requests, you don't need to add any identifier for this scenario. But if instead you want to attach to a plugin within the context of a specific session, using the REST API you'd send a post to the /janus/<session-id> endpoint:

{
        "janus" : "attach",
        "plugin" : "<the plugin's unique package name>",
        "transaction" : "<random string>"
}

To make this work with WebSockets as well, you need to add a further field called session_id in the request:

{
        "janus" : "attach",
        "session_id" : <the session identifier>,                // NEW!
        "plugin" : "<the plugin's unique package name>",
        "transaction" : "<random string>"
}

which will allow the WebSocket server to understand which session this request pertains to. At the same time, when you need to address a specific handle (e.g., to send a message to a plugin, or negotiate a WebRTC PeerConnection) you'll need to add a handle_id field to the request as well, or the request will be rejected:

{
        "janus" : "message",
        "session_id" : <the session identifier>,                // NEW!
        "handle_id" : <the handle identifier>,          // NEW!
        "transaction" : "sBJNyUhH6Vc6",
        "body" : {
                "audio" : false
        }
}

Considering the bidirectional nature of WebSockets and the fact that the channel will be shared for different requests, you'll need to pay extra attention to the transaction identifier, which will allow you to map incoming responses and events to the request you sent that originated them.

An important aspect to point out is related to keep-alive messages for WebSockets Janus channels. A Janus session is kept alive as long as there's no inactivity for 60 seconds: if no messages have been received in that time frame, the session is torn down by the gateway. A normal activity on a session is usually enough to prevent that; for a more prolonged inactivity with respect to messaging, on plain HTTP the session is usually kept alive through the regular long poll requests, which act as activity as long as the session is concerned. This aid is obviously not possible when using WebSockets, where a single channel is used both for sending requests and receiving events and responses. For this reason, an ad-hoc message for keeping alive a Janus session should to be triggered on a regular basis:

{
        "janus" : "keepalive",
        "session_id" : <the session identifier>,
        "transaction" : "sBJNyUhH6Vc6"
}

This will make sure that the gateway detects activity on the session even when no actual messages are being exchanged with handles.

As a last point, another slight difference with WebSockets comes from how push notifications are implemented. In the Plain HTTP REST Interface this is done via long polls: that is, you explicitly subscribe to notifications, and have to do that again as soon as an event has been received. With WebSockets, this is not needed: as soon as you create a session on a WebSocket, that channel becomes automatically subscribed for events related to that sessions, and you'll receive them on the same WebSocket. For the same reason, as soon as the WebSocket is closed, all the sessions created within its context are considered closed as well, and so their resources (including all the handles and PeerConnections) will be released as well.

Note
The same janus.js JavaScript library can be used both with the REST and the WebSockets API: all you need to do is provide the right Janus server address during the initialization and the library will use one or the other according to the protocol prefix.

RabbitMQ interface

The semantics of how the requests have to be built, when compared to the usage of plain HTTP, is exactly the same as for WebSockets, so refer to the WebSockets Interface documentation for details about that.

Of course, there are other aspects that differ when making use of RabbitMQ messaging to talk to Janus, rather than using HTTP messages or WebSockets. Specifically, RabbitMQ just basically forwards messages on queues, and as such implementing a pseudo-bidirectional channel as the Janus API requires some precaution.

In particular, when configuring Janus to use RabbitMQ you'll have to specify two queues:

The proper usage of these queues will allow you to implement the kind of bidirectional channel Janus needs.

Another aspect to point out is that Janus requires all requests to have a random correlation_id identifier. In fact, as pointed out in the previous sections, the Janus API is conceived as a request/response protocol that can involve asynchronous notifications as well. In order to make sure that an application can match a received response to one of the requests made earlier, Janus copies the correlation_id identifier from the original request in the response to it: this is compliant with the RPC pattern as specified in the RabbitMQ documentation. Notifications originated by Janus, instead, will not include a correlation_id identifier, and as such applications shouldn't expect any: applications will still be able to match a notification to a request, if the involved plugin was implemented to do so, by looking at the Janus-level transaction identifier.

MQTT interface

The semantics of how the requests have to be built, when compared to the usage of plain HTTP, is exactly the same as for WebSockets, so refer to the WebSockets Interface documentation for details about that.

Of course, there are other aspects that differ when making use of MQTT messaging to talk to Janus, rather than using HTTP messages or WebSockets. Similar to RabbitMQ, MQTT just basically forwards messages on queues, and as such implementing a pseudo-bidirectional channel as the Janus API requires some precaution.

In particular, when configuring Janus to use MQTT you'll have to specify two queues:

The proper usage of these queues will allow you to implement the kind of bidirectional channel Janus needs.

UnixSockets interface

The semantics of how the requests have to be built, when compared to the usage of plain HTTP, is exactly the same as for WebSockets, RabbitMQ and MQTT, so refer to the WebSockets Interface documentation for details about that.

Apart from that, the only configuration needed is related to the path the client and server will be sharing, and the socket type. Notice that only the SOCK_SEQPACKET and SOCK_DGRAM types are supported in the plugin.