Skip to Content
SSE PluginEvent Reference

Event Reference

The plugin exposes one streaming endpoint per server. Everything below applies to both Jellyfin and Emby unless a difference is called out.

The Endpoint

ServerURLAuth
JellyfinGET http://your-jellyfin:8096/api/sse/eventsAny token Jellyfin accepts for its REST API (see below)
EmbyGET http://your-emby:8096/emby/sse/eventsX-Emby-Token: YOUR_API_KEY header

Jellyfin accepts the token four ways, same as the rest of its API: an Authorization: MediaBrowser Token="..." header, an X-Emby-Token header, an X-MediaBrowser-Token header, or an api_key query parameter. The query parameter matters for browser EventSource, which can’t set headers. But tokens in URLs end up in access logs, so prefer a header anywhere you control the client.

Wire Format

Standard Server-Sent Events: a named event, a single-line JSON data payload, a blank line between events. Null fields are omitted from the JSON.

event: playing data: {"sessionId":"abc123","itemId":"def456","userId":"user1","state":"playing","positionTicks":0} event: ping data: {}

positionTicks is in .NET ticks (100-nanosecond units, 10,000,000 per second), matching what the Jellyfin and Emby APIs report elsewhere.

Connection Events

hello

Sent once, immediately on connect. Confirms you’re talking to the plugin and tells you which server and plugin version.

event: hello data: {"version":"0.4.0.0","server":"jellyfin"}
FieldMeaning
versionPlugin version
serverjellyfin or emby

ping

Keepalive, sent every 30 seconds with an empty {} payload. Useful for detecting a dead connection and for keeping intermediaries (Cloudflare, reverse proxies) from timing out an idle stream.

Playback Events

EventWhen
playingPlayback started
progressPosition update during playback
pausedPlayback paused
stoppedPlayback stopped

All four carry the same fields:

FieldMeaning
sessionIdDevice session ID; matches what /Sessions returns, not the per-playback PlaySessionId
itemIdID of the item being played
userIdUser who is playing it
stateplaying, paused, or stopped
positionTicksPlayback position in ticks
playedToCompletionstopped only; whether the item finished

Notes:

  • progress events pass through at whatever rate the media server reports them, typically every 5–10 seconds. The plugin does not throttle them.
  • Theme music and local trailer playback are filtered out, so you won’t get playing events every time someone browses a menu.
  • A pause is its own paused event. You don’t need to watch progress payloads for a state flag.

Session Events

EventFieldsWhen
session.startsessionId, userIdA device session connected
session.endsessionId, userIdA device session disconnected

Sessions are device connections, not playback. A client can hold a session open for hours without playing anything.

Library Events

EventFieldsWhen
library.item.addeditemId, itemType, parentIdAn item finished being added to a library
library.item.removeditemId, itemType, parentIdAn item was removed

Events fire once per changed item, as it happens: no batching, no scheduled-task delay. Theme media and virtual placeholder items (missing episodes, for example) are filtered out.

parentId is the item’s immediate parent: a season for an episode, a library folder for a top-level movie or series. It is not necessarily the library root.

Platform differences worth knowing:

  • IDs differ by server. Library event IDs match what each server’s own REST API reports: the 32-character GUID form on Jellyfin, the numeric internal ID on Emby. Emby playback events keep the GUID itemId they have always had.
  • Emby also raises folder events. A new movie’s directory arrives as itemType: "Folder" alongside the movie itself, and deleting a directory fires one removed event for the folder, not one per child. Don’t assume added/removed pairs match one-to-one on Emby; treat a removal as a cue to re-query the library.

Scheduled Task Events

EventFieldsWhen
task.startedtaskId, taskName, taskCategoryA scheduled task began running
task.progresstaskId, taskName, progressProgress update; throttled to 1% or 2 seconds per task, whichever comes first
task.completedtaskId, taskName, stateTask finished; state is the completion status

This covers library scans, metadata refreshes, and every other task in the server’s scheduled task list, including a live progress percentage for a running library scan.

Server Stats

server.stats

A CPU and memory sample, sent every 6 seconds while at least one client is connected.

event: server.stats data: {"at":1786151199,"hostCpuUtilization":3.257,"processCpuUtilization":0.622,"hostMemoryUtilization":30.042,"processMemoryUtilization":0.548}
FieldMeaning
atUnix timestamp (seconds) of the sample
hostCpuUtilizationWhole-machine CPU use, percent
processCpuUtilizationThe media server process’s CPU use, percent
hostMemoryUtilizationWhole-machine memory use, percent
processMemoryUtilizationThe media server process’s memory use, percent

Host values are read from /proc, so they appear on Linux hosts only and are omitted elsewhere. Inside a container, /proc reports the host machine, which is usually what you want. This event is what powers Tracearr’s Server Resources charts for Jellyfin and Emby; neither server exposes these numbers through its own API.

Delivery and Buffering

  • Events broadcast to every connected client. There is no per-connection filtering.
  • Each client gets a bounded buffer of 512 events. A client that falls behind is disconnected rather than silently losing events. Reconnect and resync from /Sessions (and your library, if you track it) to catch up.
  • Any disconnect means missed events. Treat the stream as a live signal, not a durable queue: on reconnect, poll once to re-establish state, then go back to listening.

New event types get added over time (the changelog  lists them per release). Unknown event names are safe to ignore, so a client that only handles the events it knows about keeps working across plugin updates.

Last updated on