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:

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:

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.