Webhooks
A webhook sends a message to another tool the moment something happens: playback starts, a title is marked watched, a new movie arrives. Home Assistant, Trakt and Simkl bridges, and Discord and Telegram bridges can all read it. The format is the same as Emby's webhooks, so tools made for Emby work without changes. Webhooks are free on every plan.
Add a webhook#
- Open System → Integrations → Webhook in the admin console and choose Add webhook.
- Give it a name and the address of the receiving tool. The address can be on your local network.
- Tick the events to send, and choose Send test on the list to check that the other side receives it.
You can add several webhooks. Each one has its own events, and can be limited to some members or some libraries (leave them empty for all). Example payload in the form shows what an event looks like.
Events#
The code is what the receiving tool sees in the Event field.
| Code | When it is sent |
|---|---|
library.new |
A new title has finished scraping, so its IDs are there. The first scan of a new library sends nothing. |
playback.start |
Playback begins, once per viewing. |
playback.pause, playback.unpause |
Playback is paused, and continued after a pause. |
playback.stop |
Playback ends. PlayedToCompletion says whether it was watched to the end. |
item.markplayed, item.markunplayed |
A member marks a title watched or unwatched. Changes from account sync and from an import are not sent. |
item.rate |
A member adds a title to the favorites or removes it. Both use this one code. |
The Test button sends system.notificationtest. It ignores the switches and the event ticks.
What is sent#
Every message has Title, Date (UTC), Event and Server (Name, Id, Version, ProductName, ProductVersion). Server.Version is the Emby-compatible version number; to show Kryspal's own version, use Server.ProductVersion. Events about a member add User (Name, Id) and Item, the title in the same shape the Emby API uses. Item.ProviderIds holds the TMDB, IMDb and TVDB IDs, and is {} when there are none. Playback events also carry Session (device and address) and PlaybackInfo (PositionTicks, PlayedToCompletion).
Choose the format per webhook:
- JSON (default): the payload is the request body, with
Content-Type: application/json. - Form (multipart): the same payload is in a form field named
data. Some older tools only accept this.
Delivery#
- A reply with a 2xx status counts as received. The server waits up to 15 seconds.
- A failure is tried again three times, after about 10 seconds, 1 minute and 5 minutes. A redirect counts as a failure and is not followed.
- A reply of 410 turns the webhook off at once. A webhook that keeps failing for 24 hours is turned off too. Both show a Webhooks paused card on the overview and the reason on the webhook; switch it on again to resume.
- Events are sent in the background and never slow playback or scanning. If the server restarts, events that were waiting are not sent.
- The last 50 attempts of each webhook are listed under Recent deliveries, with the status and a short error code. The message itself is not stored.
- Addresses on your local network are allowed. Link-local and cloud metadata addresses are blocked. Only
httpandhttpswork. - An address with a key in it (
?token=…) is shown with the value hidden in lists and in logs. When you edit a webhook, leave the address empty to keep it.
Check the sender#
Set a Secret if the other side should be sure a message is yours. Messages then carry three headers in the Standard Webhooks format:
| Header | Content |
|---|---|
webhook-id |
A message ID, the same on every retry. |
webhook-timestamp |
Unix time in seconds. |
webhook-signature |
v1, and the base64 of HMAC-SHA256 over id.timestamp.body, using the secret as typed. |
Without a secret nothing is added, as in Emby. The secret can be replaced or removed, never read back.
Example: Home Assistant#
Create an automation with a webhook trigger, then paste its address, http://<home-assistant>:8123/api/webhook/kryspal-playback, into a new webhook with the format JSON.
automation:
- alias: Lights down when a movie starts
triggers:
- trigger: webhook
webhook_id: kryspal-playback
allowed_methods: [POST]
local_only: true
conditions:
- condition: template
value_template: "{{ trigger.json.Event == 'playback.start' and trigger.json.Item.Type == 'Movie' }}"
actions:
- action: light.turn_off
target:
entity_id: light.living_room