Open event stream
const url = 'https://example.com/api/v1/events';const options = { method: 'GET', headers: {cookie: 'kivo_session=<kivo_session>', Upgrade: 'websocket'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://example.com/api/v1/events \ --header 'Upgrade: websocket' \ --cookie kivo_session=<kivo_session>Upgrades to a WebSocket that carries EventStreamMessage frames,
filtered to what the user may see and to admin-only scopes for
admins. The server sends a snapshot first, then event hints, and
resync (followed by a fresh snapshot) after the client fell
behind. Every 25 seconds it also sends a keepAlive, so a client
that hears nothing for a few intervals may take the socket for dead
and reconnect. Clients ignore a frame of a type they don’t know.
Clients send nothing.
The web client is authenticated by its session cookie, and its
upgrade needs a same-origin Origin. An app sends its session’s
token, or a script its API key, as Authorization: Bearer <token>
on the upgrade request, as on every other request, and no Origin is checked; the
token never goes in the URL. A client whose WebSocket can’t set a
header, as the packaged TV app’s page can’t, offers the token as a
subprotocol instead, Sec-WebSocket-Protocol: kivo, kivo-bearer.<token>,
and the upgrade is answered with Sec-WebSocket-Protocol: kivo; no
Origin is checked for it either. Only this upgrade reads the
subprotocol. Without valid credentials the upgrade is refused with
401.
The session is rechecked every 30 seconds, and a change in which
libraries the profile sees, or whether it’s an admin, applies with a
resync. The server closes the socket with:
4401when the session or API key is no longer valid: at once when it’s signed out or revoked, and within 30 seconds when it expires or its user is disabled or deleted. The client is signed out and doesn’t reconnect.1001(going away) when the server is stopping. The client reconnects with backoff.
Any other end, such as a dropped connection, is reconnected with
backoff; after a reconnect the new snapshot stands in for whatever
was missed.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Responses
Section titled “Responses”Switched to WebSocket. The content describes each frame, not a body.
Sent first on every connection and after resync. A changed serverVersion means the client should offer a reload.
object
Admins only. Open problems, for the dot on Admin.
A hint that something changed. It carries IDs and counts only; clients refetch through the API. Each kind maps to one cache operation. Hints of many changes come at most about once a second. Documented kinds and their fields:
workChanged(workId): one work’s detail changed, such as after Fix match or a metadata refresh.libraryChanged(libraryIds): root works were added to or removed from these libraries (found by a scan, matched, unmatched, moved, gone or purged), one was created or edited, or one is being deleted. It names only libraries the profile sees and comes at most about once every 3 seconds. Refetch Home, the library list and its counts, and any open grid, list, filter or letter rail of those libraries, keeping the viewer’s place in a grid.userStateChanged(workIds): the user’s state for these works changed, possibly on another client: marked played or unplayed (a series or season with its episodes), a favorite, or a play’s last position. Refetch them, and the lists that show them.playEnded(playId,message): the user’s play was stopped by an admin or another client, or expired;messageis the admin’s words to the viewer, if any.downloadsChanged: the user’s downloads on the server moved: in line, in their preparation (at most every 10 seconds), or to ready or failed. Refetch the downloads that aren’t ready.tasksChanged: admins only. A task’s work moved: an item started or finished, or reported its progress, a run began or ended, or something was asked for. Refetch the tasks and libraries, whosescansays how a scan is going, and the missing files.playsChanged: admins only. A play started or ended, or a heartbeat reported its position; refetch the plays and the play events.problemsChanged(problemCount): admins only. The open problems changed; refetch them.logsChanged: admins only. The server logged lines at Info or above; refetch the recent log.activityChanged: admins only. An event was recorded; refetch the activity.
object
What an event says changed. Skip kinds you don’t know.
A UUIDv7.
A UUIDv7.
The client fell behind and events were dropped. Treat every live-kept query as stale and refetch; a fresh snapshot follows.
object
Sent every 25 seconds, so a client can tell a quiet socket from a dead one. It changes nothing.
object
Example
{ "type": "snapshot"}The request is malformed or a field is invalid (code invalidRequest).
An RFC 9457 problem document. code is stable and is what clients
translate.
object
A URI naming the problem type; about:blank when only code matters.
A short English summary, for logs.
English detail for logs and developers; never internal error text.
A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.
The request ID, also in the X-Request-Id header and the server log.
For transcodeBusy and notPlayable, why. For transcodeBusy:
transcodeLimit or storageFull. For notPlayable: noTracks,
fileUnplayable, subtitleUnplayable, transcodeFailed,
keyframeIndexMissing or downloadsNotAllowed.
For fileUnplayable, the capability the file failed.
object
The declared value, lists joined by commas; absent when the client declared none.
For throttled and rateLimited, the Retry-After header’s value.
For invalidRequest, what’s wrong with which field.
object
A JSON pointer into the body, or a parameter name.
What’s wrong with a field: tooLarge for a file or list over its
size, unsupported for a file in a format the server doesn’t take,
unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list
for the series, and notABook for a work edited with books that
isn’t one.
For streamLimit, the user’s plays, to show what is playing where.
One of the user’s plays, for the stream limit.
object
A UUIDv7.
A UUIDv7.
The name of the session or API key it was started by.
For playEnded, the admin’s words to the viewer, when they gave any.
For quotaUsed, when the quota resets, when the provider said.
Example
{ "code": "invalidRequest", "reason": "noTracks", "errors": [ { "code": "required" } ]}No valid session or API key (code unauthenticated), or a failed sign-in (code invalidCredentials).
An RFC 9457 problem document. code is stable and is what clients
translate.
object
A URI naming the problem type; about:blank when only code matters.
A short English summary, for logs.
English detail for logs and developers; never internal error text.
A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.
The request ID, also in the X-Request-Id header and the server log.
For transcodeBusy and notPlayable, why. For transcodeBusy:
transcodeLimit or storageFull. For notPlayable: noTracks,
fileUnplayable, subtitleUnplayable, transcodeFailed,
keyframeIndexMissing or downloadsNotAllowed.
For fileUnplayable, the capability the file failed.
object
The declared value, lists joined by commas; absent when the client declared none.
For throttled and rateLimited, the Retry-After header’s value.
For invalidRequest, what’s wrong with which field.
object
A JSON pointer into the body, or a parameter name.
What’s wrong with a field: tooLarge for a file or list over its
size, unsupported for a file in a format the server doesn’t take,
unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list
for the series, and notABook for a work edited with books that
isn’t one.
For streamLimit, the user’s plays, to show what is playing where.
One of the user’s plays, for the stream limit.
object
A UUIDv7.
A UUIDv7.
The name of the session or API key it was started by.
For playEnded, the admin’s words to the viewer, when they gave any.
For quotaUsed, when the quota resets, when the provider said.
Example
{ "code": "invalidRequest", "reason": "noTracks", "errors": [ { "code": "required" } ]}Signed in, but this needs an admin (code forbidden); a
same-origin request (code crossOrigin); or a device code that was
denied (code denied).
An RFC 9457 problem document. code is stable and is what clients
translate.
object
A URI naming the problem type; about:blank when only code matters.
A short English summary, for logs.
English detail for logs and developers; never internal error text.
A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.
The request ID, also in the X-Request-Id header and the server log.
For transcodeBusy and notPlayable, why. For transcodeBusy:
transcodeLimit or storageFull. For notPlayable: noTracks,
fileUnplayable, subtitleUnplayable, transcodeFailed,
keyframeIndexMissing or downloadsNotAllowed.
For fileUnplayable, the capability the file failed.
object
The declared value, lists joined by commas; absent when the client declared none.
For throttled and rateLimited, the Retry-After header’s value.
For invalidRequest, what’s wrong with which field.
object
A JSON pointer into the body, or a parameter name.
What’s wrong with a field: tooLarge for a file or list over its
size, unsupported for a file in a format the server doesn’t take,
unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list
for the series, and notABook for a work edited with books that
isn’t one.
For streamLimit, the user’s plays, to show what is playing where.
One of the user’s plays, for the stream limit.
object
A UUIDv7.
A UUIDv7.
The name of the session or API key it was started by.
For playEnded, the admin’s words to the viewer, when they gave any.
For quotaUsed, when the quota resets, when the provider said.
Example
{ "code": "invalidRequest", "reason": "noTracks", "errors": [ { "code": "required" } ]}default
Section titled “default”A server-side failure: 500 (code internal, or transcodeFailed
for a play’s media), or 503
for something unavailable (codes providerUnavailable and
transcodeBusy, for now; notConfigured, until an admin configures
it).
An RFC 9457 problem document. code is stable and is what clients
translate.
object
A URI naming the problem type; about:blank when only code matters.
A short English summary, for logs.
English detail for logs and developers; never internal error text.
A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.
The request ID, also in the X-Request-Id header and the server log.
For transcodeBusy and notPlayable, why. For transcodeBusy:
transcodeLimit or storageFull. For notPlayable: noTracks,
fileUnplayable, subtitleUnplayable, transcodeFailed,
keyframeIndexMissing or downloadsNotAllowed.
For fileUnplayable, the capability the file failed.
object
The declared value, lists joined by commas; absent when the client declared none.
For throttled and rateLimited, the Retry-After header’s value.
For invalidRequest, what’s wrong with which field.
object
A JSON pointer into the body, or a parameter name.
What’s wrong with a field: tooLarge for a file or list over its
size, unsupported for a file in a format the server doesn’t take,
unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list
for the series, and notABook for a work edited with books that
isn’t one.
For streamLimit, the user’s plays, to show what is playing where.
One of the user’s plays, for the stream limit.
object
A UUIDv7.
A UUIDv7.
The name of the session or API key it was started by.
For playEnded, the admin’s words to the viewer, when they gave any.
For quotaUsed, when the quota resets, when the provider said.
Example
{ "code": "invalidRequest", "reason": "noTracks", "errors": [ { "code": "required" } ]}