Learn about webhooks

Receive notifications about media assets without polling the JW Platform Management API.

Webhooks allow you to automate your workflow by notifying you when specific events occur in the JWP video process.

🔑

If you are not a developer or prefer a simpler implementation, you can use Zapier to create Zaps to manage your notifications.



Prerequisite

Item Description
Secret Unique user API credential

Follow these steps to obtain the secret:
  1. On the API Credentials page, under v2 API Credentials, click Show Secret in the row of the relevant API key name
  2. 📘

    If no API key names exist, type a new API key name, select a permission level, and click Add New API Key. Your account must have the Admin permission to create a new API key.

  3. Click to copy the secret.


Available webhook events

JWP's webhooks support the following notifications.

Webhook Event Description
Conversions Complete Notification sent when both the thumbnail is ready and all initial video renditions have been processed

This event is only fired for the first media upload, and not when the media's original is replaced through a reupload.

Event Example
{
    "event": "conversions_complete",
    "media_id": "MNYaMSQl",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T00:20:57+00:00"
}
Live Channel is Active Notification sent when a Live Channel enters an active state

Event Example
{
    "event": "channel_active",
    "channel_id": "YijbmYHJ",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-29T03:01:24+00:00"
}
Live Channel is Idle Notification sent when a Live Channel enters an idle state

Event Example
{
    "event": "channel_idle",
    "channel_id": "t8JLGRl9",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-29T03:00:34+00:00"
}
Media Available Notification sent when media is first publishable and indexed

At this point, the thumbnail is ready, but not all video renditions, such as those with higher resolutions, may have finished processing. If your media has a publish start date in the future, no Media Available notification will be sent.

Event Example
{
    "event": "media_available",
    "media_id": "MNYaMSQl",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T00:15:45+00:00"
}
Media Created Notification sent when media has been first uploaded

Event Example
{
    "event": "media_created",
    "media_id": "MNYaMSQl",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T00:13:10+00:00"
}
Media Deleted Notification sent when media has been deleted

Event Example
{
    "event": "media_deleted",
    "media_id": "MNYaMSQl",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T01:14:01+00:00"
}
Media Reuploaded Notification sent when a media has been reuploaded

The Media Reuploaded notification is the point at which the reupload has been initiated, not the time at which the reupload has been fully processed. You will receive Conversion Complete and Media Available notifications when conversions have been indexed and the media is first publishable.

Event Example
{
    "event": "media_reuploaded",
    "media_id": "MNYaMSQl",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T01:09:24+00:00"
}
Media Updated Notification sent when a media is updated

Event Example
{
    "event": "media_updated",
    "media_id": "MNYaMSQl",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T01:05:28+00:00"
}
New Live Channel Created Notification sent when a Live Channel is created

Event Example
{
    "event": "channel_created",
    "channel_id": "YijbmYHJ",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-29T02:59:57+00:00"
}
Stream created Notification sent when the Broadcast Live stream has been successfully created

Event Example
{
    "event": "stream_created",
    "stream_id": "IfufXrxY",
    "webhook_id": "mvYr6Twx",
    "site_id": "HFWDaPJo",
    "event_time": "2024-09-17T14:41:47+00:00",
    "data": {
        "status": "requested"
    }
}
Stream deleted Notification sent when the the Broadcast Live stream has been deleted

Event Example
{
    "event": "stream_deleted",
    "stream_id": "IfufXrxY",
    "webhook_id": "mvYr6Twx",
    "site_id": "HFWDaPJo",
    "event_time": "2024-09-17T14:57:47+00:00",
    "data": {
        "status": "deleted"
    }
}
Stream go live Notification sent when the Broadcast Live stream has become available

Event Example
{
    "event": "stream_go_live",
    "stream_id": "IfufXrxY",
    "webhook_id": "mvYr6Twx",
    "site_id": "HFWDaPJo",
    "event_time": "2024-09-17T14:47:47+00:00",
    "data": {
        "status": "streaming"
    }
}
Stream transitioned Notification sent when the Broadcast Live stream has transitioned to a new state

Event Example
{
    "event": "stream_transitioned",
    "stream_id": "IfufXrxY",
    "webhook_id": "mvYr6Twx",
    "site_id": "HFWDaPJo",
    "event_time": "2024-09-17T14:47:47+00:00",
    "data": {
        "status": "streaming"
    }
}
Stream updated Notification sent when the properties of the Broadcast Live stream have changed

Event Example
{
    "event": "stream_updated",
    "stream_id": "IfufXrxY",
    "webhook_id": "mvYr6Twx",
    "site_id": "HFWDaPJo",
    "event_time": "2024-09-17T14:50:07+00:00",
    "data": {
        "status": "stopping",
        "ingest": {
            "rtmp": {},
            "srt": {
                "ingest_point": {
                    "url": "srt://ingest-9fbb8955abe1.jwplive.com:8000/"
                }
            },
            "srt_pull": {},
            "rtp": {},
            "hls_pull": {},
            "rtp_fec": {},
            "zixi_push": {}
        }
    }
}
{
    "event": "stream_updated",
    "stream_id": "IfufXrxY",
    "webhook_id": "mvYr6Twx",
    "site_id": "HFWDaPJo",
    "event_time": "2024-09-17T14:50:08+00:00",
    "data": {
        "status": "stopping",
        "playout": {
            "hls": "https://livecdn.use1-0004.jwplive.com/live/sites/HFWDaPJo/media/IfufXrxY/live.isml/.m3u8?t=2024-09-17T14:47:33-2024-09-17T14:49:02",
            "dash": "https://livecdn.use1-0004.jwplive.com/live/sites/HFWDaPJo/media/IfufXrxY/live.isml/.mpd?t=2024-09-17T14:47:33-2024-09-17T14:49:02"
        }
    }
}
Thumbnail Created Notification sent when a thumbnail is created

Event Example
{
    "event": "thumbnail_created",
    "thumbnail_id": "k9x00iro",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T00:13:25+00:00",
    "media_id": "MNYaMSQl"
}
Thumbnail Deleted Notification sent when a thumbnail is deleted

Event Example
{
    "event": "thumbnail_deleted",
    "thumbnail_id": "sl5swh3z",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T01:14:01+00:00",
    "media_id": "MNYaMSQl"
}
Track Created Notification sent when a track is created

Event Example
{
    "event": "track_created",
    "track_id": "VHsQNYq9",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T00:58:57+00:00",
    "media_id": "MNYaMSQl"
}
Track Deleted Notification sent when a track is deleted

Event Example
{
    "event": "track_deleted",
    "track_id": "VHsQNYq9",
    "webhook_id": "2yW4KmiG",
    "site_id": "9KboGBFu",
    "event_time": "2024-08-27T01:02:03+00:00",
    "media_id": "MNYaMSQl"
}

Webhooks are enabled at the property level. So, if you have multiple properties under a single account, you will need to create webhooks for each property.



Create a webhook

In your platform or language of choice, use the following steps to create a webhook:

  1. Create a POST api.jwplatform.com/v2/webhooks call.
  2. Add your secret to the header of your POST call to authenticate your request. For example, in the use case below: 'Authorization: 123Four56==7123Four56==7'
  3. Create a request body. The request body must include a metadata object that defines the appropriate webhook properties.
  4. Append the query body to your POST request.
  5. Execute the API POST request.

👍

USE CASE: A publisher wants to automate the publishing workflow for a property whose key is 1A23bCD4. Without needing to poll JWP's API, the publisher wants to know when newly uploaded videos are ready to be published to its site.


SOLUTION: The publisher must create a webhook for the property (1A23bCD4) that subscribes to the Media Available (media_available) notification. As noted in the table at the beginning of the section, the Media Available notification event indicates when media has been indexed and can be shared.

curl -X POST https://api.jwplayer.com/v2/webhooks \
 -H 'Authorization: 123Four56==7123Four56==7' \
 -H 'Content-Type: application/json' \
 -d '{"metadata": {"name" : "Media Available Webhook", "description": "Webhook to notify me when media is ready to be published", "webhook_url": "https://my-endpoint.com", "events": ["media_available"], "site_ids": ["1A23bCD4"]}}'

If the API call is successful, the publisher receives the following response.

{
    "created": "2019-09-05T11:54:37.182547+00:00",
    "id": "c0Y6ebVU",
    "last_modified": "2019-09-05T11:54:37.182547+00:00",
    "metadata": {
        "description": "Webhook to notify me when media is ready to be published",
        "events": [
            "media_available"
        ],
        "name": "Media Available Webhook",
        "site_ids": ["1A23bCD4"],
        "webhook_url": "https://my-endpoint.com"
    },
    "schema": null,
    "secret": "efZQiWvaaoqx1Hpgi-hhLGInZUhWa1UyWjVaVk5ZVldoR2FVZERja3BoV0hWNE4xTjYn",
    "type": "webhook"
}


Verify the authenticity of a webhook

JWP gives you the ability to verify the authenticity of a webhook. To verify that a webhook originated from JWP, JWP's webhooks use JWT encryption.

When a webhook is created, the JSON response contains a secret property. The value of the secret is the shared key used for decryption. Be sure to securely store the value of the secret. This secret is returned only in the response to the POST api.jwplatform.com/v2/webhooks call.

Incoming webhooks include a header Authorization whose value is Bearer {Token}. The token is generated by hashing the webhook payload with the shared secret.

To verify the authenticity of a webhook, use the JWT decode method. The {Token} and secret should be used as arguments in the decode method. The value returned from this method should equal the body sent in the request.


🚧

JWP's webhooks do not support a rotating webhook secret. In order to generate a new webhook secret, you must first delete the existing webhook and then, create a new webhook.


The following code example shows how this workflow might look in Python.

"""
Sample code showing how to decode the JWT token to verify authenticity of sender
"""

import json
import jwt


def parse_token_from_request(request):
  """
  Extracts JWT token from Authorization header

  Args:
    request: Incoming HTTP Request
    
  Returns:
    bytes: JWT token
  """
  # The header will be in the form of "Authorization: Bearer {Token}"
  return request.headers["Authorization"].split(" ")[1]


def verify_authenticity(jwt_token, webhook_secret, webhook_payload):
  """
  Uses the JWT Token and Request body to verify 
    
  Args:
    jwt_token (bytes): token
    webhook_secret (str): shared secret returned on POST /v2/webhooks
    webhook_payload (json): Incoming Webhook notification JSON body
  """
  jwt_payload = jwt.decode(jwt_token, webhook_secret, algorithms=["HS256"])
  return jwt_payload == webhook_payload


def webhook_handler(request):
  """
  Route handler for the endpoint handling incoming webhooks

  Args:
    request: Incoming request
  """
  jwt_token = parse_token_from_request(request)
  # This assumes a Flask implementation, but parse JSON body
  webhook_payload = json.loads(request.data)

  # This represents the shared secret returned on POST /v2/webhooks
  # This should be stored securely.
  shared_secret = "secret!"

  is_jw_webhook = verify_authenticity(jwt_token, shared_secret, webhook_payload)

  if is_jw_webhook:
    # Congrats it is a valid JW webhook!
    process_webhook(webhook_payload)
  else:
    # Not a JW webhook!
    return


Understand available webhooks API routes

In addition to creating webhooks, you can list, return, update, and delete webhooks.