Use this file to discover all available pages before exploring further.
Hooks are the primary way plugins react to things happening inside Riven. Each hook is a plain async function that receives a typed event payload, a DataSourceMap, a PluginSettings instance, and a logger — and optionally returns a response value that the Riven core acts on. Under the hood every hook gets its own dedicated BullMQ queue so it runs independently of other system work.
The handler signature is defined by createEventHandlerSchema in the SDK. Every handler receives the same four arguments regardless of event type:
type HookHandler<TEvent, TResponse = void> = (args: { event: Omit<TEvent, 'type'>; // the event payload, without the discriminant dataSources: DataSourceMap; // access any DataSource registered by this plugin settings: PluginSettings; // read plugin settings via settings.get(Schema) logger: Logger; // Winston logger scoped to this job}) => Promise<TResponse>;
Hooks live in the hooks object of your RivenPlugin export. Keys are event type strings; values are handler functions. All keys are optional — only subscribe to the events your plugin needs.
Use dataSources.get(YourDataSourceClass) to retrieve an instance of any DataSource your plugin has registered. The DataSourceMap.get() method throws at runtime if the class is not found, catching mismatches early.
The TVDB plugin hooks into riven.media-item.index.requested.show to populate show metadata. If the item has a tvdbId it fetches series and episode data from TVDB and timezone info from TVMaze, then returns a structured item payload for the Riven core to persist.
import { TvdbAPI } from '../datasource/tvdb.datasource.ts';import { TvMazeAPI } from '../datasource/tvmaze.datasource.ts';import { transformSeries } from '../transformers/transform-series.ts';import type { MediaItemIndexRequestedShowEventHandler } from '@repo/util-plugin-sdk/schemas/events/media-item.index.requested.event';import type z from 'zod';export const indexTVDBMediaItem: z.infer< typeof MediaItemIndexRequestedShowEventHandler> = async ({ dataSources, event }) => { if (event.item.tvdbId) { const api = dataSources.get(TvdbAPI); const tvMazeApi = dataSources.get(TvMazeAPI); const series = await api.getSeries(event.item.tvdbId); const episodes = await api.getAllEpisodesInOfficialOrder(event.item.tvdbId); const timezone = await tvMazeApi.getShowTimezone(event.item.tvdbId); return { item: transformSeries(event.item, series, episodes, timezone), }; } else if (event.item.imdbId) { // IMDb-only path not yet implemented return null; } return null;};
Use the exported handler type (e.g. z.infer<typeof MediaItemIndexRequestedShowEventHandler>) for the handler variable so TypeScript enforces the exact return shape the core expects.
The riven.content-service.requested hook is how content-source plugins (e.g. a watchlist or Overseerr integration) push new media items into Riven. Return movies and shows arrays with at least one external ID each.
Indexing failed because the item was in an unexpected state
void
Return null from an index hook to indicate your plugin did not handle the item (e.g. because the required external ID is missing). The core collects results from all plugins and uses the first non-null response.
Each event has a corresponding handler schema exported from the events index. Using these schemas for type inference means TypeScript catches mismatches in the return type:
import type { ContentServiceRequestedEventHandler } from '@repo/util-plugin-sdk/schemas/events/content-service-requested.event';import type z from 'zod';export const fetchWatchlist: z.infer< typeof ContentServiceRequestedEventHandler> = async ({ dataSources, settings }) => { const api = dataSources.get(WatchlistAPI); const { updateIntervalSeconds } = settings.get(WatchlistSettings); const items = await api.getWatchlist(); return { movies: items.filter((i) => i.type === 'movie').map(toExternalIds), shows: items.filter((i) => i.type === 'show').map(toExternalIds), updateIntervalSeconds, };};
Why does the event payload omit the `type` field?
The type discriminant is used internally by Riven to route the event to the correct queue and handlers. By the time your hook function is called, routing is already done, so the type field is stripped from the payload to keep handler signatures clean. You already know the event type from the key you subscribed to.