Webhooks are a powerful and fast way to sync your backends nearly instantly. They are triggered by user actions, such as play, pause, stop, etc., on a backend. When an action is performed, the backend sends a webhook to the WatchState API, which processes the data, updates the system, and triggers events to the other backends.
Although webhooks are great for syncing data quickly, they should not be used as the sole method for synchronization.
Webhooks are not 100% reliable, they may be missed or delayed. To ensure your data is always up-to-date, it’s
recommended to use webhooks in combination with scheduled tasks with high scheduled time i.e. every 12 hours.
We have designed the webhook system to be generic rather than backend and user specific, so that means you only need to use single webhook for all users and backends. This is a big improvement over the previous system, which required you to create a separate webhook for each user and backend.
The webhook URL is /v1/api/webhook, of course, if you have enabled secure all endpoints you need to
add ?apikey=your_ws_apikey to the URL. which you can obtain by going to
More > Terminal and then write system:apikey in the box. You should get the apikey which is
hexadecimal string.
If you don't have WS_SECURE_API_ENDPOINTS enabled:
https://your_ws_url/v1/api/webhook
If you have enabled WS_SECURE_API_ENDPOINTS environment variable, then you need to add the apikey to the URL:
https://your_ws_url/v1/api/webhook?apikey=[api_key_you_got_from_terminal]
The easiest way to configure webhooks is through the UI: navigate to
**Backends**, click the **Add Webhook** button on any backend card, then click **Add/Update Webhook**.This will automatically register or update the webhook URL on the remote backend. Alternatively, you can configure webhooks manually using the instructions below.
Click here to open manual configuration instructions
-
Go to your Emby Server:
- Old Emby Versions: Go to Server > Webhooks and click Add Webhook.
- New Emby Versions: Go to Manage Emby Server > xxx Preferences > Notifications > + Add Notification > Webhooks.
-
Name:
- Whatever you want, we recommend WatchState Webhook.
-
Webhook/Notifications URL:
- see The generic webhook URL section.
-
Request Content Type (Emby v4.9+):
- Select
application/json.
- Select
-
Webhook Events (v4.7.9 or higher):
- New Media Added
- Playback
- Mark Played
- Mark Unplayed
For versions prior to v4.7.9:
- Playback events
- User events
-
Limit User Events to:
- Select all users, otherwise events will not contain any user data.
-
Limit Library Events to:
- Select libraries you want to sync or leave it blank for all libraries.
Click Add Webhook / Save.
-
Go to your Jellyfin dashboard, then navigate to Plugins > Catalog and install Notifications > Webhook. Restart Jellyfin.
-
After the restart, go back to Plugins > Webhook and add
Add Generic Destination. -
Webhook Name:
- Whatever you want, we recommend WatchState Global Webhook.
-
Webhook URL:
- see The generic webhook URL section.
-
Notification Type:
- Item Added
- User Data Saved
- Playback Start
- Playback Stop
-
User Filter:
- Select all users, otherwise events will not contain any user data.
-
Item Type:
- Select Movies and Episodes.
-
Send All Properties:
- Toggle this checkbox.
-
*Trim leading and trailing whitespace from message body before sending:
- Toggle this checkbox.
-
Do not send when message body is empty:
- Toggle this checkbox.
-
Click on Add Request Header and add the following header:
- Key:
Content-Type - Value:
application/json
- Key:
Click Save.
-
Go to your Plex Web UI and navigate to Settings > Your Account > Webhooks. Click Add Webhook.
-
Webhook URL:
- see The generic webhook URL section.
Click Save Changes.
-
Go to Options > Notification Agents and click Add a new notification agent > Webhook.
-
Webhook URL:
- see The generic webhook URL section.
-
Webhook Method:
- Select
PUT.
- Select
-
Description:
- Whatever you want, we recommend WatchState Global Webhook.
-
Triggers:
Select the following events:- Playback Start
- Playback Stop
- Playback Pause
- Playback Resume
- Watched
- Recently Added
-
Data:
- For each event, you will need to set the corresponding headers/data fields using the following format.
[!IMPORTANT]
It’s important that you copy the headers and data as they are, without modifying them if you're unsure.
JSON Headers:
{
"user-agent": "Tautulli/{tautulli_version}"
}JSON Data:
{
"event": "tautulli.{action}",
"Account": {
"id": "{user_id}",
"thumb": "{user_thumb}",
"title": "{username}"
},
"Server": {
"title": "{server_name}",
"uuid": "{server_machine_id}",
"version": "{server_version}"
},
"Player": {
"local": "{stream_local}",
"publicAddress": "{ip_address}",
"title": "{player}",
"uuid": "{machine_id}"
},
"Metadata": {
"librarySectionType": null,
"ratingKey": "{rating_key}",
"key": null,
"parentRatingKey": "{parent_rating_key}",
"grandparentRatingKey": "{grandparent_rating_key}",
"guid": "{guid}",
"parentGuid": null,
"grandparentGuid": null,
"grandparentSlug": null,
"type": "{media_type}",
"title": "{episode_name}",
"grandparentKey": null,
"parentKey": null,
"librarySectionTitle": "{library_name}",
"librarySectionID": "{section_id}",
"librarySectionKey": null,
"grandparentTitle": "{show_name}",
"parentTitle": "{season_name}",
"contentRating": "{content_rating}",
"summary": "{summary}",
"index": "{episode_num}",
"parentIndex": "{season_num}",
"audienceRating": "{audience_rating}",
"viewOffset": "{view_offset}",
"skipCount": null,
"lastViewedAt": "{last_viewed_date}",
"year": "{show_year}",
"thumb": "{poster_thumb}",
"art": "{art}",
"parentThumb": "{parent_thumb}",
"grandparentThumb": "{grandparent_thumb}",
"grandparentArt": null,
"grandparentTheme": null,
"duration": "{duration_ms}",
"originallyAvailableAt": "{air_date}",
"addedAt": "{added_date}",
"updatedAt": "{updated_date}",
"audienceRatingImage": null,
"userRating": "{user_rating}",
"Guids": {
"imdb": "{imdb_id}",
"tvdb": "{thetvdb_id}",
"tmdb": "{themoviedb_id}",
"tvmaze": "{tvmaze_id}"
},
"file": "{file}",
"file_size": "{file_size_bytes}"
}
}Click Save.
See the backend limitations for a comprehensive list of per-backend requirements and limitations, including webhook-specific event behaviour.
As previously mentioned, webhooks aren't 100% reliable, thus it's recommended to enable import/export tasks to complement webhook functionality.
Simply go to the Tasks page and enable the Import and Export tasks. and set the schedule to every 12 hours or
every 24 hours depending on your needs.