Skip to content

Event types ​

Every event that an event handler receives is a JSON object with the same outer shape and a body that depends on the type. The examples on this page are real events: a headless Chrome made an EchoTest call (audio and video) against the Rust binary, and a sample HTTP handler wrote down what it received. Session and handle ids change from run to run.

The envelope ​

json
{
  "emitter": "MyJanusInstance",
  "type": 16,
  "subtype": 1,
  "timestamp": 1793807320859272,
  "session_id": 7619045123486259,
  "handle_id": 3190114712345680,
  "opaque_id": "docs-demo",
  "event": { }
}
FieldTypeWhenMeaning
emitterstringwhen the server has a name (server_name in janus.jcfg; the default is MyJanusInstance)The server that raised the event. GELF uses it as host.
typeintegeralwaysThe type number from the table below.
subtypeintegeronly when above 0Narrows core, webrtc and media events.
timestampintegeralwaysMicroseconds since the Unix epoch, wall clock.
session_idintegerall types except core and external; plugin and transport events leave it out when they have no sessionThe session the event is about.
handle_idintegerhandle, jsep, webrtc, media and plugin eventsThe handle.
opaque_idstringwhen the client gave one at attachLets you tie a handle to your own id.
eventobjectalwaysThe body, described per type below.

The handlers add to this: MQTT adds eventtype (the readable type name), GELF wraps the whole object in full_message. See the handler descriptions.

The types ​

The number is one bit, so the numbers add up in a mask. events in a handler file takes the label. all is every bit (4294967295).

TypeNumberLabel in eventsName (GELF short_message)SubtypesRaised when
session1sessionsSessionsnoneA session is created, destroyed or times out.
handle2handlesHandlesnoneA handle attaches to a plugin or detaches.
external4externalExternalnoneAn Admin API custom_event request.
jsep8jsepJsepnoneAn SDP arrives from a client or goes out to one.
webrtc16webrtcWebRTC1 ICE, 2 local candidate, 3 remote candidate, 4 selected pair, 5 DTLS, 6 PeerConnection stateICE, DTLS or the PeerConnection changes.
media32mediaMedia1 state, 2 slow link, 3 statisticsMedia starts or stops flowing; every second per medium.
plugin64pluginsPluginsnoneA plugin raises its own event.
transport128transportsTransportsnoneA transport reports a connection.
core256coreCore1 startup, 2 shutdownJanus starts, every 15 s after, and stops.

none (mask 0) selects no events.

Session (type 1) ​

event.name is created, destroyed or timeout (the session expired with no keepalive). created adds a transport object when the session was made over HTTP: transport is the package name of the transport and id is a number that stands for the connection.

jsonc
// Type 1 (sessions). The `transport` object says which transport created the session.

// ⇠ type 1: created over HTTP
{
  "emitter": "MyJanusInstance",
  "type": 1,
  "timestamp": 1793808494458194,
  "session_id": 1724494162477229,
  "event": {
    "name": "created"
  }
}

// ⇠ type 1: destroyed
{
  "emitter": "MyJanusInstance",
  "type": 1,
  "timestamp": 1793808494859758,
  "session_id": 1724494162477229,
  "event": {
    "name": "destroyed"
  }
}

The transport object is missing for other transports

A session created over WebSockets, a Unix socket or nanomsg sends {"name": "created"} with no transport. The C server always includes it. Code that reads event.transport.transport must tolerate a missing object.

Handle (type 2) ​

event.name is attached or detached; event.plugin is the plugin package name. If the client gave opaque_id at attach it is in the envelope and, as the C server does for older consumers, repeated inside event. If a token was used to attach, it is in event.token. The top-level handle_id is the handle.

jsonc
// Type 2 (handles). `opaque_id` is the one the client gave at attach.

// ⇠ type 2: attached
{
  "emitter": "MyJanusInstance",
  "type": 2,
  "timestamp": 1793808496260761,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "name": "attached",
    "plugin": "janus.plugin.echotest",
    "opaque_id": "docs-demo"
  }
}

// ⇠ type 2: detached
{
  "emitter": "MyJanusInstance",
  "type": 2,
  "timestamp": 1793808505829415,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "name": "detached",
    "plugin": "janus.plugin.echotest",
    "opaque_id": "docs-demo"
  }
}

External (type 4) ​

Sent when someone calls the Admin API request custom_event: schema names your format and data is any JSON. The event has no session_id.

jsonc
// Type 4 (external): sent with the Admin API request custom_event. It has no session_id.

// ⇠ type 4
{
  "emitter": "MyJanusInstance",
  "type": 4,
  "timestamp": 1793808495262621,
  "event": {
    "schema": "docs.example",
    "data": {
      "hello": "world",
      "count": 3
    }
  }
}

The request that made it was {"janus": "custom_event", "schema": "docs.example", "data": {"hello": "world", "count": 3}, "admin_secret": "janusoverlord", "transaction": "t"}.

Jsep (type 8) ​

One event for each SDP that passes the core. event.owner is remote for what the client sent and local for what Janus sent back. event.jsep has type (offer, answer) and the full sdp. The SDPs below are cut after the third line; the real events carry all of it.

jsonc
// Type 8 (jsep): every SDP that crosses the core. `owner` is who made it. The SDP is shortened here.

// ⇠ type 8: remote offer
{
  "emitter": "MyJanusInstance",
  "type": 8,
  "timestamp": 1793808496659900,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "owner": "remote",
    "jsep": {
      "type": "offer",
      "sdp": "v=0\r\no=- 8687266155835230716 2 IN IP4 127.0.0.1\r\ns=-\r\n…"
    }
  }
}

// ⇠ type 8: local answer
{
  "emitter": "MyJanusInstance",
  "type": 8,
  "timestamp": 1793808496661746,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "owner": "local",
    "jsep": {
      "type": "answer",
      "sdp": "v=0\r\no=- 8687266155835230716 2 IN IP4 192.0.2.10\r\ns=-\r\n…"
    }
  }
}

WebRTC (type 16) ​

The subtype says what changed. All of them have stream_id and component_id (1 and 1 with BUNDLE and rtcp-mux, which is what browsers use).

SubtypeBodyValues
1icedisconnected, gathering, connecting, connected, ready, failed
2local-candidatethe candidate line Janus sent
3remote-candidatethe candidate line the client sent
4selected-pair, candidatesa text form of the pair, and local and remote objects with address, port, type, transport and family
5dtls, retransmissionscreated, trying, connected, failed
6connectionwebrtcup, or hangup with an optional reason

The order in the run was: ICE gathering, the local candidates, the remote candidates, ICE connecting, connected, the selected pair, ICE ready, DTLS trying, DTLS connected, webrtcup. A hangup sends hangup with "reason": "Janus API" when the client asked for it.

jsonc
// Type 16 (webrtc), with a subtype for each kind of update: 1 ICE state, 2 local candidate, 3 remote candidate, 4 selected pair, 5 DTLS state, 6 PeerConnection state (webrtcup and hangup).

// ⇠ type 16, subtype 1: ICE state
{
  "emitter": "MyJanusInstance",
  "type": 16,
  "subtype": 1,
  "timestamp": 1793808497031211,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "ice": "connected",
    "stream_id": 1,
    "component_id": 1
  }
}

// ⇠ type 16, subtype 2: local candidate
{
  "emitter": "MyJanusInstance",
  "type": 16,
  "subtype": 2,
  "timestamp": 1793808496661790,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "local-candidate": "1 1 udp 2015363327 192.0.2.10 57734 typ host",
    "stream_id": 1,
    "component_id": 1
  }
}

// ⇠ type 16, subtype 3: remote candidate
{
  "emitter": "MyJanusInstance",
  "type": 16,
  "subtype": 3,
  "timestamp": 1793808496661801,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "remote-candidate": "800151196 1 udp 2122260223 192.0.2.11 64725 typ host",
    "stream_id": 1,
    "component_id": 1
  }
}

// ⇠ type 16, subtype 4: selected pair
{
  "emitter": "MyJanusInstance",
  "type": 16,
  "subtype": 4,
  "timestamp": 1793808497364895,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "selected-pair": "192.0.2.10:57734 [host,udp] <-> 192.0.2.10:55114 [host,udp]",
    "candidates": {
      "local": {
        "address": "192.0.2.10",
        "port": 57734,
        "type": "host",
        "transport": "udp",
        "family": 4
      },
      "remote": {
        "address": "192.0.2.10",
        "port": 55114,
        "type": "host",
        "transport": "udp",
        "family": 4
      }
    },
    "stream_id": 1,
    "component_id": 1
  }
}

// ⇠ type 16, subtype 5: DTLS state
{
  "emitter": "MyJanusInstance",
  "type": 16,
  "subtype": 5,
  "timestamp": 1793808497967814,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "dtls": "connected",
    "stream_id": 1,
    "component_id": 1,
    "retransmissions": 0
  }
}

// ⇠ type 16, subtype 6: webrtcup
{
  "emitter": "MyJanusInstance",
  "type": 16,
  "subtype": 6,
  "timestamp": 1793808497967879,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "event": {
    "connection": "webrtcup"
  },
  "opaque_id": "docs-demo"
}

// ⇠ type 16, subtype 6: hangup
{
  "emitter": "MyJanusInstance",
  "type": 16,
  "subtype": 6,
  "timestamp": 1793808504826819,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "event": {
    "connection": "hangup",
    "reason": "Janus API"
  },
  "opaque_id": "docs-demo"
}

Media (type 32) ​

SubtypeWhatBody
1media started or stopped flowingmedia (audio, video), mid, receiving (true or false), substream for simulcast, seconds how long it was silent when receiving is false
2slow linkmedia, mid, slow_link (uplink or downlink), lost_lastsec
3statisticsone event per medium every stats_period seconds (default 1): see below. With combine_media_stats there is one event per handle with an array

The statistics body: mid, mindex, media, codec, base (clock rate), rtt, rtt-values (once a receiver report has arrived), lost, lost-by-remote, jitter-local, jitter-remote, the four link-quality percentages, packets-received, packets-sent, bytes-received, bytes-sent, bytes-received-lastsec, bytes-sent-lastsec, nacks-received, nacks-sent, retransmissions-received, and remb-bitrate when the browser sent one. With simulcast, the second and third layer are reported as their own events with media set to video-sim1 and video-sim2, and a data channel has an event with media set to data.

A media statistics event is one of the most frequent events: with stats on and a handle with audio and video, expect two events per second per handle. Turn stats_period up, or off with 0, if the volume matters.

jsonc
// Type 32 (media): subtype 1 media state, 2 slow link, 3 statistics. The slow link event is not shown: it needs packet loss, which this run did not have.

// ⇠ type 32, subtype 1: media is flowing
{
  "emitter": "MyJanusInstance",
  "type": 32,
  "subtype": 1,
  "timestamp": 1793808497980440,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "media": "video",
    "mid": "1",
    "receiving": true
  }
}

// ⇠ type 32, subtype 3: statistics (one per medium, every second)
{
  "emitter": "MyJanusInstance",
  "type": 32,
  "subtype": 3,
  "timestamp": 1793808498971965,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "mid": "0",
    "mindex": 0,
    "media": "audio",
    "codec": "opus",
    "base": 48000,
    "rtt": 0,
    "lost": 0,
    "lost-by-remote": 0,
    "jitter-local": 0,
    "jitter-remote": 0,
    "in-link-quality": 100,
    "in-media-link-quality": 100,
    "out-link-quality": 100,
    "out-media-link-quality": 100,
    "packets-received": 50,
    "packets-sent": 50,
    "bytes-received": 3861,
    "bytes-sent": 3461,
    "bytes-received-lastsec": 0,
    "bytes-sent-lastsec": 0,
    "nacks-received": 0,
    "nacks-sent": 0,
    "retransmissions-received": 0
  }
}

Check the numbers before you alert on them

In this run, once the first receiver reports arrived, rtt was 45542573 (a "millisecond" value of 12 hours), with rtt-values.ntp smaller than lsr. And lost for the video stream was -8318. Both came from a clean loopback call with Chrome and fake devices. The C server gives an rtt of a few milliseconds there. I did not compare the numbers with the C server, but these two are wrong enough that you should not build alerts on rtt or lost until they are fixed.

Plugin (type 64) ​

event.plugin is the package name; event.data is whatever the plugin passes to notify_event. The shape of data is the plugin's: see the plugin's own page. EchoTest sends its settings when you change them. Plugin events may have no session_id or handle_id when the plugin raises them outside a handle.

jsonc
// Type 64 (plugin): what a plugin passes to notify_event. The shape of `data` is the plugin's.

// ⇠ type 64, from EchoTest
{
  "emitter": "MyJanusInstance",
  "type": 64,
  "timestamp": 1793808496661617,
  "session_id": 8229749208234940,
  "handle_id": 8977468686213072,
  "opaque_id": "docs-demo",
  "event": {
    "plugin": "janus.plugin.echotest",
    "data": {
      "audio_active": true,
      "video_active": true,
      "bitrate": 0
    }
  }
}

Transport (type 128) ​

event.transport is the transport package, event.id stands for the connection, and event.data is the transport's own payload with an event name.

Transportdata.eventOther keys
janus.transport.httprequestadmin_api, ip, port of the client. One per HTTP request.
janus.transport.websocketsconnected, disconnectedadmin_api, ip (connected)
janus.transport.pfunixconnected, disconnectedadmin_api, fd, and type for SOCK_DGRAM
janus.transport.mqttconnected, disconnected, reconnecting, failed
janus.transport.rabbitmqconnected
janus.transport.nanomsgcreatedadmin_api, socket

Each transport has a general.events switch that turns its transport events off.

jsonc
// Type 128 (transport). `transport` is the package, `data` is the transport's own payload. HTTP reports each request, WebSockets each connection.

// ⇠ type 128: HTTP request
{
  "emitter": "MyJanusInstance",
  "type": 128,
  "timestamp": 1793808494453987,
  "event": {
    "transport": "janus.transport.http",
    "id": "1",
    "data": {
      "event": "request",
      "admin_api": false,
      "ip": "127.0.0.1",
      "port": 49650
    }
  }
}

// ⇠ type 128: WebSocket connected
{
  "emitter": "MyJanusInstance",
  "type": 128,
  "timestamp": 1793808494457685,
  "event": {
    "transport": "janus.transport.websockets",
    "id": "6224993832",
    "data": {
      "event": "connected",
      "admin_api": false,
      "ip": "127.0.0.1"
    }
  }
}

// ⇠ type 128: WebSocket disconnected
{
  "emitter": "MyJanusInstance",
  "type": 128,
  "timestamp": 1793808494859703,
  "event": {
    "transport": "janus.transport.websockets",
    "id": "53703894096",
    "data": {
      "event": "disconnected"
    }
  }
}

The WebSocket ids do not match

In the run above, the connected event of a WebSocket connection has the id 6204218920 and its disconnected event a different one. The id is meant to let you pair the two events. With the Rust port, pair them by order and ip instead, or by the session ids the connection owned.

Core (type 256) ​

Subtype 1 (startup) is sent when Janus starts, with the same body as GET /janus/info in event.info, and again every 15 seconds with "status": "update" and the counts. Subtype 2 (shutdown) is sent when Janus gets a stop signal, with signum.

jsonc
// Type 256 (core). Subtype 1 is startup, 2 is shutdown. The core also repeats the startup subtype every 15 s with the session, handle and PeerConnection counts.

// ⇠ type 256, subtype 1: startup
{
  "emitter": "MyJanusInstance",
  "type": 256,
  "subtype": 1,
  "timestamp": 1793808494306124,
  "event": {
    "status": "started",
    "info": {
      "janus": "server_info",
      "…": "the same body as GET /janus/info"
    }
  }
}

// ⇠ type 256, subtype 1: periodic status update
{
  "emitter": "MyJanusInstance",
  "type": 256,
  "subtype": 1,
  "timestamp": 1793808509312575,
  "event": {
    "status": "update",
    "info": {
      "sessions": 0,
      "handles": 0,
      "peerconnections": 0,
      "stats-period": 1
    }
  }
}

// ⇠ type 256, subtype 2: shutdown (caught by a GELF handler: see the note on the sample handler in the events page)
{
  "emitter": "MyJanusInstance",
  "type": 256,
  "subtype": 2,
  "timestamp": 1793808511145471,
  "event": {
    "status": "shutdown",
    "signum": 15
  }
}

The info bodies are cut in the transcript; the real startup event carries the full server info (about 150 lines). The shutdown event was received by a GELF handler: the sample HTTP handler of the same run did not get it.

Known gaps ​

  • gap session created events lack the transport object unless the session came over HTTP.
  • gap The id of a WebSocket transport event changes between connected and disconnected.
  • gap The media statistics rtt and lost values in the recorded run are not plausible.
  • gap The slow link event (media subtype 2) was not produced here: it needs packet loss. Its shape is from the C source.
  • gap The timeout session event, the MQTT reconnecting and failed transport events, and simulcast statistics were not produced here either; their shapes are from the C source.
  • gap info does not report event handlers (event_handlers is always false).

GPL-3.0-or-later, like the Janus gateway it ports.