Events API

Horn’s Events API can call a customer-provided endpoint with “webhook” style events when certain things happen within the customer’s clientID context. This makes it easy to integrate the lifecycle of a Horn session into a broader customer application context.

Authentication

Every emitted event includes an authentication header (X-Horn-Signature) with an HMAC that can be used to verify that the webhook request came from a valid source and was not tampered with in transit. The HMAC is computed with a shared secret (defined per clientID). The shared secret can be managed with Horn API: Get events secret, Regenerate events secret.

A customer’s Event API endpoint (the server that receives the webhook) can choose to validate the webhook payload by recomputing the expected HMAC (based on request payload and the shared secret) and comparing the computed value and the provided header.

The header can be computed as follows:

Base64(HMAC-SHA-256(SHARED_SECRET, EVENT_BODY))

Configuration

To use the Events API, at least one endpoint URL must be specified. Multiple endpoints are supported. Events will be sent to all defined endpoints.

You can turn on the Events API and add endpoints using the Horn Management Console under the Account / Events API section:

../../_images/hmc_events_settings.png

When a valid endpoint is provided and the Events API is enabled, you will immediately start receiving events, such as notifications when users join the lobby:

../../_images/hmc_events_example_webhook.png

Security

Events API endpoints can be either http:// or https://, though https:// is strongly recommended.

If an endpoint is specified with https://, the Events API will insist on it having a valid TLS/SSL certificate.

Webhooks/Events API

Webhook events are send using HTTP POST method with JSON-encoded payload.

Payload Basic Structure is defining what can be found in every payload. It contains event type as well as has event-specific parameters.

Basic Structure

Here are fields that will be provided in every payload:

name

type

Description

version

int

Version of the horn API used by this message

timestamp

long

Time at which event occur in milliseconds

message_id

string

The ID for this specific callback event. Can be used to de-duplicate if needed.

channel_id

string

The ID of the horn channel the message refers to.

event_type

string

Type of event this for this payload.

session_id

string

Optional The ID of the horn session the message refers to.

A session is when given channel is being used by users. It starts when channel is being created for given meeting and ends when last participant leave the channel. Note that given channel can have multiple sessions in general but only one in given period of time

This field is optional because some events have no context of session. In example if you’ll setup something like channel name or it’s privacy before an actual meeting, it won’t be related to any active session but might still produce an event like “channel name changed”

data

json

Optional event-specific data. Each event will have different data structure here. See specific events below for details

Note that this field is optional so there might be events that do not provide any additional data.

Example JSON for Channel Created event would look like this:

{
    "version": 1,
    "timestamp": 1565954987470,
    "message_id": "18bc9a69-92c4-4c85-b415-fae96535f79d",
    "channel_id": "35a2sk6s-a91v-1246-5sab-aa2k56jyc458",
    "session_id": "ba98b35a-ry11-t2d6-ntd2-ba36a56ms3kf",
    "event_type": "channel_created"
}

Channel Created

Fired when Channel was created in our database. It does not mean the session has started yet.

Note that this will only fire for sub-channels if parent channel has setup webhook endpoints.

event_type: channel_created

session_id: no

data: none

Channel Deleted

Fired when Channel was deleted from Horn’s ecosystem. It means an permanent deletion of given channel occur.

event_type: channel_deleted

session_id: no

data: none

Recording Started

Fired when recording has started for given session in channel.

event_type: recording_started

session_id: yes

data:

name

type

Description

id

string

The ID of recording

Example:

{
    // ...
    "event_type": "recording_started",
    "data": {
        "id": "ba9baf335b-a251-tba9-upd2-ab433aj75s"
    }
}

Recording Stopped

Fired when recording has stopped for given session in channel.

event_type: recording_stopped

session_id: yes

data:

name

type

Description

id

string

The ID of recording

Example:

{
    ...
    "event_type": "recording_stopped",
    "data": {
        "id": "ba9baf335b-a251-tba9-upd2-ab433aj75s"
    }
}

Recording Available

Fired when recording post processing has finished and output recording files are ready.

event_type: recording_available

session_id: yes

data:

name

type

Description

id

string

The ID of recording

url

string

Location of recording output file. This is an MP4 file containing mixed audio tracks of all users and individual video tracks for users from given recording session

users

array

An array containing list of users that participate during this recording session. Structure of one user is described below.

user structure:

name

type

Description

id

string

The ID of user

handle

string

Name of user

Example:

{
    // ...
    "event_type": "recording_available",
    "data": {
        "id": "ba9baf335b-a251-tba9-upd2-ab433aj75s",
        "url": "https://domain.com/path/to/output.mp4",
        "users": [
            {
                "id": "6821561",
                "handle": "John"
            },
            {
                "id": "1266545",
                "handle": "Alice"
            },
            {
                "id": :"98552",
                "handle": "Ben"
            },
        ]
    }
}

Session Started

Fired when first participant joins channel.

event_type: session_started

session_id: yes

data: none

Session Ended

Fired when last participant leaves channel.

event_type: session_ended

session_id: yes

data: none

User Login

Fired when given user logs into channel.

event_type: user_login

session_id: yes

data:

name

type

Description

id

string

The ID of user

handle

string

Name of user

Example:

{
    // ...
    "event_type": "user_login",
    "data": {
        "id": "6821561",
        "handle": "John"
    }
}

User Logout

Fired when given user logs out of a channel.

event_type: user_logout

session_id: yes

data:

name

type

Description

id

string

The ID of user

handle

string

Name of user

Example:

{
    // ...
    "event_type": "user_logout",
    "data": {
        "id": "1266545",
        "handle": "Alice"
    }
}

User entered the Lobby

Fired when given user entered the lobby(not yet in the meeting room).

event_type: user_lobby_enter

session_id: yes

data:

name

type

Description

id

string

The ID of user

handle

string

Name of user

Example:

{
    // ...
    "event_type": "user_lobby_enter",
    "data": {
        "id": "6821561",
        "handle": "John"
    }
}

User has left the Lobby

Fired when given user has left the Lobby. This event is not fired when the user leaves the lobby to go to the meeting room

event_type: user_lobby_leave

session_id: yes

data:

name

type

Description

id

string

The ID of user

handle

string

Name of user

Example:

{
    // ...
    "event_type": "user_lobby_leave",
    "data": {
        "id": "6821561",
        "handle": "John"
    }
}

Handling Failures

Horn’s Events API is using HTTP when delivering individual events to client endpoints. Such operation can fail by various reasons like network outage or temporary issues on endpoint side. This section will describe how Horn is handling such cases.

Identify failure

Horn will identify delivery attempt as failed when at least one of these conditions occur:

  • Response status code will not equal 200

  • Response won’t finish within 30s (timeout)

Retry mechanism

When we mark event delivery as failed, we’d try to re-send it with exponential back-off pattern to max back-off of 60s.

Base delay is 5 seconds and it’ll doubled with every retry attempt two times.

So in case of continuous failure, our retry attempts will be be delayed by:

  • 5s

  • 10s

  • 20s

  • 40s

  • 60s

  • 60s

  • 60s

We’ll stop trying after 60 retry attempts which means around roughly 1 hour of trying.

Every request will be identical as previous ones. In case of duplicated delivery of event, client can always use message_id field to de-duplicate events.