API Reference - Moonfin-Client/Plugin GitHub Wiki

API Reference

Every endpoint Moonbase exposes on the server. This page is for developers and for admins debugging an integration. Normal use of Moonfin never needs it.

Paths are absolute and served by both the Jellyfin and Emby plugins unless noted.

Auth column:

  • Yes requires any valid Jellyfin or Emby session.
  • Admin requires an elevated (administrator) session.
  • Session uses a stored Seerr session rather than the platform token.
  • No is anonymous (used for web assets, discovery, and the inbound Seerr webhook, which has its own shared secret).

Some areas are gated by config: the Settings and public Themes endpoints return 503 when settings sync is disabled, and the Games endpoints return empty results or 404 when the game library feature is turned off.

Status and settings

Method Path Auth Description
GET /Moonfin/Ping Yes Plugin status: version, whether sync is enabled, Seerr/MDBList/TMDB availability, admin defaults, and feature flags so older clients can hide the features they lack. An optional profile query (tv, desktop or mobile) returns defaultSettings with that device's admin defaults layered over the Global ones. messagesSupported is on both servers. recommendationsSupported is Jellyfin only and follows the Use Moonfin Recommends for similar items switch. clientLogSupported is Emby only and follows Enable client log upload. pinSupported, tmdbThumbsSupported, tmdbDetailsSupported, tmdbPersonLinksSupported, tmdbReviewsSupported and extendedCompleteShowingsSupported are on both servers, and incompleteRecordingsSupported follows Detect incomplete recordings
GET /Moonfin/Defaults Yes The admin-configured default settings profile
GET /Moonfin/Settings Yes The current user's full settings envelope
POST /Moonfin/Settings Yes Save the current user's settings (merge or replace via body)
DELETE /Moonfin/Settings Yes Delete all of the current user's settings
HEAD /Moonfin/Settings Yes Check whether the current user has stored settings (200 or 404)
GET /Moonfin/Settings/Stream Yes Server-sent events stream of settings changes, theme changes, and admin messages. On Emby it answers { "supported": false, "transport": "websocket" } and the same events ride the session websocket instead
GET, POST /Moonfin/Settings/{userId} Admin Get or save another user's settings
GET /Moonfin/Settings/Resolved/{profile} Yes The resolved flat values for a profile (global, desktop, mobile, tv)
POST, DELETE /Moonfin/Settings/Profile/{profile} Yes Save or delete one device profile's overrides (global can't be deleted)
GET, POST /Moonfin/Settings/detailsScreenBlur and /detailsScreenBlur/{profile} Yes Get or save the details-screen backdrop blur
GET, POST /Moonfin/Settings/detailsScreenOpacity and /detailsScreenOpacity/{profile} Yes Get or save the details-screen backdrop opacity

See Settings Sync for the envelope shape and the full list of synced settings.

Sign-in PINs

A user's sign-in PIN, stored on the server so it follows them to every device. A PIN is four digits. Only a salted PBKDF2-SHA256 hash is kept, and the PIN is never sent back.

Method Path Auth Description
GET /Moonfin/Pin Yes Whether the calling user has a PIN
PUT /Moonfin/Pin Yes Set the calling user's PIN. Body { "pin", "currentPin" }, where currentPin is required when a PIN already exists, and a wrong one is a 403 that counts as a guess
DELETE /Moonfin/Pin Yes Remove the calling user's PIN. Takes the same body, with currentPin
POST /Moonfin/Pin/Verify Yes Check a PIN. Body { "pin" }. Answers 404 when the user has no PIN

The first five wrong guesses are free. After that each one makes the user wait 30 seconds longer, up to 15 minutes, and while they wait every guess, even the right one, gets a 429 with Retry-After and retryAfterSeconds. The count survives a restart, and a correct PIN or an admin setting a new one clears it.

Admin

Method Path Auth Description
POST /Moonfin/Admin/ApplyDefaults Admin Push admin defaults onto users' stored settings. Body { "overwrite", "userIds", "profiles", "keys" }. userIds null means everyone, profiles empty means all four, keys null means every set default, otherwise only those settings by JSON name. overwrite resets each user to the defaults alone and can't take keys. Returns usersAffected and orphansDeleted
POST /Moonfin/Admin/CopySettings Admin Copy one user's saved profiles onto others. Body { "fromUserId", "toUserIds", "profiles", "keys", "replace" }. replace makes each profile an exact copy and can't take keys. API keys and hidden items are never copied. Answers 404 when the source has no settings
POST /Moonfin/Admin/PushDefaults Admin Older route. Applies every profile's defaults to all users (overwrite=true resets everyone, otherwise merges)
POST /Moonfin/Admin/PushDefaults/{userId} Admin Older route. Applies every profile's defaults to a single user
POST /Moonfin/Broadcast Admin Send an announcement to all users, over the SSE stream for connected clients and as a push to registered devices
GET /Moonfin/Libraries/CheckWriteAccess Admin Test whether the server can write metadata to each library, and report failing paths
GET /Moonfin/Admin/Pins Admin Every user with whether they have a PIN, administrators first, for the Accounts tab
PUT, DELETE /Moonfin/Users/{userId}/Pin Admin Set or remove one user's PIN. Setting a PIN replaces the old one without asking for it and clears the wrong-guess count, which is how a forgotten PIN is reset
GET /Moonfin/Admin/ConfigBackup Admin Jellyfin only. Whether the plugin settings look like they were reset while a backup with real settings survives: { "looksReset": bool, "backupPath": "..." }. The config page uses it to offer a restore
POST /Moonfin/Admin/RestoreConfig Admin Jellyfin only. Copy the backup over the live plugin settings. Returns 404 when there's no backup. Restart the server afterwards, since Jellyfin only reads the file at startup

Server messages

Admin messages are a stored, targeted successor to the one-shot broadcast: a message is saved on the server, filtered to the audience it was meant for, and delivered to connected clients over the SSE stream (as a messagesChanged event) and as a push to registered devices. Visibility is decided on the server, so a message meant for one user is never sent to another.

Method Path Auth Description
GET /Moonfin/Messages Yes The messages the calling user should see right now, already filtered to their audience and any start or end date. Returns 503 when settings sync is disabled
GET /Moonfin/Admin/Messages Admin Every stored message, including scheduled and expired ones, for the admin panel
POST /Moonfin/Admin/Messages Admin Create a message, or replace one in place when the ID already exists. Emits a messagesChanged event and, for a new interrupting message, a push
POST /Moonfin/Admin/Messages/Order Admin Reorder the stored messages to match the given list of IDs. The app shows them in this order, so this is what the up and down arrows drive
DELETE /Moonfin/Admin/Messages/{messageId} Admin Delete one message by ID

A message carries a title, a Markdown body, a colour, a delivery mode (an unread badge on the menu button, or the window opening on its own), an audience (everyone, a set of users, or admins only), optional start and end dates, and an optional link button. Interrupting messages push to devices that don't have the app open, while inbox messages wait for the user to open it. The old /Moonfin/Broadcast still works for a quick one-off announcement that isn't stored.

Media bar, genres, and themes

Method Path Auth Description
GET /Moonfin/MediaBar Yes The resolved media bar items for the current user (profile query, default global)
GET /Moonfin/Genres Yes All genre names and ids across the user's movie and series libraries
GET /Moonfin/Themes Yes List uploaded custom themes
GET /Moonfin/Themes/{themeId} Yes Get one custom theme by id
GET /Moonfin/Admin/Themes Admin Theme metadata index for the admin panel
POST /Moonfin/Admin/Themes Admin Upload or replace a theme payload
DELETE /Moonfin/Admin/Themes/{themeId} Admin Delete a theme

See Themes for the editor and upload flow.

Recommendations

Jellyfin only.

Method Path Auth Description
GET /Moonfin/Items/{id}/Similar Yes Similar items for a movie or series, scored by Moonfin on the server. Optional limit (default 20, capped at 200), excludeItemIds, and userId (defaults to the caller). Results are filtered to the libraries the user can see and come back as { "Items": [...], "TotalRecordCount": n }, in the same card shape as the media bar with the user's UserData. Returns 404 for an unknown item, and an empty list when Use Moonfin Recommends for similar items is off

This endpoint works on every supported Jellyfin version. On Jellyfin 12 Moonbase also plugs into Jellyfin's own similar items system, so the stock /Items/{id}/Similar returns Moonfin's scoring to every client, with Jellyfin's own providers only filling empty slots. Add bypass=moonfin (or engine=jellyfin) to that stock request to get Jellyfin's own results for one call.

Seasonal row

Method Path Auth Description
GET /Moonfin/Seasonal/Row Yes The seasonal Home row for the calling user: holiday movies they own, plus Seerr suggestions. Optional country is the viewer's ISO alpha-2 code, ZZ for someone who picked Other, or empty to fall back to the server's. Answers { "holiday", "country", "items", "suggestions" }, with holiday null when no holiday window is open. items are the same cards the browse endpoints return, and suggestions use the custom row item shape with a rating attached. On a failure it answers an empty row rather than an error

Anime markers

Filler, canon and recap markers come from the cache the Moonfin Anime Markers Sync task fills. Subbed and Dubbed are read from the audio tracks in the files.

Method Path Auth Description
GET /Moonfin/AnimeMarkers/Series Yes Markers for every episode of one series (seriesId). Reads the cache only and never goes to the internet, so clients can call it on every episode list. Answers enabled: false when both marker features are off, and pending: true for a matched series that hasn't been fetched yet. Returns 400 for a missing or malformed id and 404 for an unknown series
GET /Moonfin/AnimeMarkers/Items Yes Subbed or Dubbed for up to 200 comma-separated item ids (ids). Series and folders are skipped, since their episodes and seasons come from the Series endpoint. Answers enabled: false when audio markers are off
GET /Moonfin/AnimeMarkers/Diagnostics Admin Match counts for the admin page: how many series matched a show, how many are waiting on a fetch, and which matched nothing
GET /Moonfin/AnimeMarkers/Preview Admin One series' episode list with its markers (seriesId), for the Preview a series tool
POST /Moonfin/AnimeMarkers/Refresh Admin Fetch one series right away instead of waiting for the nightly task (seriesId)
GET /Moonfin/AnimeMarkers/Log Admin The tail of the marker debug log (lines, default 200, up to 2000)
POST /Moonfin/AnimeMarkers/ClearLog Admin Empty the marker debug log
POST /Moonfin/AnimeMarkers/ClearCache Admin Drop every cached show so the next sync fetches from scratch

A Series response looks like this:

{
  "enabled": true,
  "matched": true,
  "slug": "naruto-shippuden",
  "title": "Naruto Shippuden",
  "pending": false,
  "recapKnown": true,
  "episodes": {
    "<episodeId>": { "kind": "Filler", "recap": false, "audio": "Subbed" }
  },
  "seasons": {
    "<seasonId>": { "audio": "Dubbed" }
  },
  "placement": "below"
}

kind is Filler, Mixed, MangaCanon, AnimeCanon, or null when there's no marker or that pill is turned off. audio is Subbed, Dubbed, or SubbedAndDubbed when dual audio is shown separately. placement is the admin's pill placement (below, beside, or thumbnail). The Items endpoint answers { "enabled": true, "items": { "<itemId>": { "audio": "Subbed" } }, "placement": "below" }.

On Jellyfin the episode and season keys are item ids without hyphens. On Emby they're Emby's numeric item ids, the Items endpoint keys each answer by the id you sent, and fields with no value are left out instead of being sent as null.

Live TV recordings

Incomplete recording detection and re-recording. The settings are server-wide and set on the admin page's Live TV tab.

Method Path Auth Description
GET /Moonfin/LiveTv/Recordings/Incomplete Live TV access The incomplete state of each recording listed in itemIds, or of every incomplete recording when none are listed. Recordings that aren't incomplete are left out
POST /Moonfin/LiveTv/Recordings/{itemId}/Dismiss Live TV management Keep as Complete. Clears the recording's incomplete state for everyone and removes its DVRIncompleteRecording tag
POST /Moonfin/LiveTv/CompleteShowing Live TV management Ask for a later showing of a program's episode or movie to be recorded. Repeating the call for the same one joins the same request, so it's safe to retry
GET /Moonfin/LiveTv/CompleteShowing/Options Live TV management For a program that's already airing (programId), whether a later showing can be searched for and the one found: name, description, start, channel and alreadyScheduled. Only channels the caller can see are searched
POST /Moonfin/LiveTv/CompleteShowing/Suggestion Live TV management Record the later showing that Options suggested. Answers 204, or 409 when that airing changed, was canceled or can't be seen
GET /Moonfin/LiveTv/Settings/IncompleteRecordings Live TV access The server-wide settings
POST /Moonfin/LiveTv/Settings/IncompleteRecordings Admin Change the server-wide settings

TMDB

These use the calling user's TMDB key, or the server-wide one, and answer 404 without either.

Method Path Auth Description
GET /Moonfin/Tmdb/Thumb Yes A 302 to the TMDB backdrop with the title on it for a title the server doesn't hold (type is movie or tv, plus tmdbId, or imdbId when there's no TMDB id). It prefers the backdrop in the server's metadata language, then TMDB's main one. scheme=http asks for a plain http image, for clients that can't load https. Paths are cached for 7 days
GET /Moonfin/Tmdb/Details Yes Overview, rating, genres, runTimeTicks, a textless backdrop, a logo and a YouTube trailer for a title the server doesn't hold (type, tmdbId or imdbId), in the server's metadata language. Shares Thumb's cache
GET /Moonfin/Tmdb/PersonLinks Yes A person's IMDb id, website, and Instagram, X, Facebook, TikTok and YouTube handles (tmdbId). Cached for 24 hours
GET /Moonfin/Tmdb/Reviews Yes TMDB user reviews for a title (tmdbId, type, language), up to 20, with the HTML turned into light Markdown. Asks for English when the requested language has none. Cached for 24 hours per title and language

Ratings (MDBList and TMDB)

Method Path Auth Description
GET /Moonfin/MdbList/Ratings Yes MDBList ratings for a title (type = movie or show, tmdbId), filtered to the user's chosen sources. An optional profile query (desktop, mobile, or tv) resolves the sources against that device profile, anything else falls back to global
GET /Moonfin/MdbList/KeyInfo Admin Validate an MDBList API key and return the account's plan and remaining quota (backs the Test Key button, optional key query tests an unsaved key, otherwise the server-wide key is used)
POST /Moonfin/MdbList/ClearCache Admin Drop every cached MDBList rating so lookups refetch from the API
GET /Moonfin/MdbList/Lists Yes The cached catalog of MDBList official lists
GET /Moonfin/MdbList/Lists/{slug}/Items Yes Cached items for one official list (optional mediatype)
GET /Moonfin/Tmdb/EpisodeRating Yes TMDB rating for one episode (tmdbId, season, episode)
GET /Moonfin/Tmdb/SeasonRatings Yes TMDB ratings for every episode in a season (tmdbId, season)
GET /Moonfin/Tmdb/NextEpisode Yes The next episode to air for a series (tmdbId), so clients can show an air date without Sonarr. Answers { "success", "airDate", "seasonNumber", "episodeNumber", "name" }, or success: false with an error when there's no TMDB key or nothing is scheduled. Uses the user's TMDB key or the server-wide one, and caches each series for 6 hours for all users
GET /Moonfin/Tmdb/ProductionCompanies Yes TMDB studios for a title (tmdbId, type), each flagged for logo availability
GET /Moonfin/Tmdb/StudioImage/{companyId} Yes A cached studio logo PNG (re-downloaded from TMDB if evicted)
GET /Moonfin/Assets/{fileName} No Bundled rating provider icons
GET /Moonfin/UserRatings/Mine Yes Every personal rating the calling user has set, in one response, so a client can sort a library by My Rating and filter it by liked or disliked. Neither server has a user rating sort of its own
POST, DELETE /Moonfin/UserRatings/{itemId} Yes Emby only. Set the calling user's rating for an item (rating out of ten, or likes for a thumb) or clear it. Emby's own rating route answers 200 but stores nothing, so Moonfin writes through here

Custom rows and collections

Method Path Auth Description
GET /Moonfin/CustomRows/Items Yes Resolve an external home row (source, type, params) into items, cached server-side for 24 hours. Items carry officialRating (the TMDB certification), overview, genres and runTimeTicks, and the response is filtered by the calling user's parental limit, where a title with no known rating is left out under a limit. A row fetched with the user's own TMDB or MDBList key is cached for that user only. An optional language (a BCP 47 tag like fr or pt-BR) fetches MDBList, TMDB and Letterboxd rows in that language and caches each language apart. IMDb rows stay in English. Items without an overview get the overview, genres and runtime from TMDB in the server's metadata language. On Jellyfin a resolve keeps running if the client disconnects, and a repeat request joins the one in progress
GET /Moonfin/Collections/{collectionId}/Order Yes The current user's saved item order for a collection
POST /Moonfin/Collections/{collectionId}/Order Yes Save the user's item order for a collection (empty array clears it)

Audiobook bookmarks and notes

Jellyfin only.

Method Path Auth Description
GET /Moonfin/Bookmarks Yes Every bookmark the calling user has, across items
GET /Moonfin/Bookmarks/{itemId} Yes One item's bookmarks and notes for the calling user
POST /Moonfin/Bookmarks/{itemId} Yes Save the item's bookmarks for the calling user
POST /Moonfin/Bookmarks/{itemId}/Notes Yes Save the item's notes for the calling user

Clients work from local storage first and reconcile with these, so nothing waits on the network to open a book.

Retro games

All game endpoints require the game library feature to be enabled. See Retro Games for the full guide.

A library backed by RomM answers the same endpoints. Its game ids look like romm-12 and its BIOS ids like romm-fw-3, and the ROM and BIOS streams come from RomM through the server. For a user who linked their RomM account, those calls use that user's own RomM key, and the saves of RomM's games go to their RomM account. Save states are keyed ejs-<core>-romm-<id> or lr-<core>-romm-<id>, and battery saves (kind=sram) use the bare game id.

The EmulatorJS player page takes an optional sram URL next to save. It loads that battery save when the game starts and skips it when save is given, since a save state already holds it. window.moonfinGetSram() returns the battery save as base64 for the app to upload on exit.

Method Path Auth Description
GET /Moonfin/Games/Libraries Yes Libraries treated as game (ROM) libraries
GET /Moonfin/Games/{libraryId}/Systems Yes Top-level system folders in a game library
GET /Moonfin/Games/{libraryId}/Games Yes Games in a library, optionally filtered by system
GET /Moonfin/Games/{libraryId}/Games/{gameId} Yes One game's full detail (ROM, core, BIOS)
GET /Moonfin/Games/{libraryId}/Thumb/{gameId} Yes Stream a game's box art or screenshot (type query), downloaded and cached on first request
GET, HEAD /Moonfin/Games/{libraryId}/Rom/{token} Yes Stream a ROM file, auto-extracting archives, with range support
GET, HEAD /Moonfin/Games/{libraryId}/Rom/{token}/{fileName} Yes Jellyfin only. The same stream with an explicit file name, which some cores need to identify the ROM
GET, HEAD /Moonfin/Games/{libraryId}/Bios/{token} Yes Stream a BIOS file a core needs
GET /Moonfin/Games/Cores/Status Yes Whether the self-hosted emulator cores are installed or downloading
POST /Moonfin/Games/Cores/Install Admin Download and install the cores bundle in the background
POST /Moonfin/Games/Cores/Upload Admin Install cores from an uploaded zip
GET /Moonfin/Games/Saves/{gameId} Yes Download a saved game blob for the current user (kind is state, sram or settings, default state)
PUT /Moonfin/Games/Saves/{gameId} Yes Upload or overwrite a saved game blob (max 32 MB)
GET /Moonfin/Games/Romm/Account Yes The current user's RomM link. While a link waits for approval, each call also checks with RomM. Returns {available, linked, username, pending, error}
POST /Moonfin/Games/Romm/Account Yes Start linking the current user's RomM account. pending holds userCode, verificationUrl, verificationUrlComplete, expiresInSeconds and intervalSeconds
DELETE /Moonfin/Games/Romm/Account Yes Unlink the current user's RomM account
GET /Moonfin/EmulatorJS/player.html No The in-browser player shell
GET /Moonfin/EmulatorJS/data/{**path} No Self-hosted EmulatorJS runtime files, with a CDN fallback when not installed
GET /Moonfin/Games/Debug Admin Diagnostics for game library detection
POST /Moonfin/Games/Romm/Test Admin Check a RomM address and token from the admin page before saving them. Body {"url", "token"}
PUT /Moonfin/Games/{libraryId}/Games/{gameId}/Core Yes Jellyfin only. Pin one game to a specific core
PUT /Moonfin/Games/{libraryId}/Games/{gameId}/Backend Yes Jellyfin only. Pin one game to the native or EmulatorJS backend

Artwork

Jellyfin only.

Method Path Auth Description
GET /Moonfin/Games/ArtworkCapabilities Yes Advertises the manifest-driven artwork protocol, so older clients keep the old path
GET /Moonfin/Games/{libraryId}/ArtworkManifest Yes The cataloged artwork state for one system, without scheduling any provider work. Returns 304 when nothing changed
POST /Moonfin/Games/{libraryId}/ArtworkPriority Yes Promotes already cataloged artwork into the order the client actually needs it
GET /Moonfin/Games/{libraryId}/Artwork/{gameId}/{role}/{revision} Yes One versioned artwork file, served locally
POST /Moonfin/Games/{libraryId}/Artwork/{gameId}/Refresh Admin Repair path that reopens and reschedules one artifact
POST /Moonfin/Games/{libraryId}/Artwork/Refresh Admin Library-wide repair, optionally for one system. Checks each system's game database for updates, then reopens artwork that never found an original. Answers 202 and runs in the background, or 409 when a refresh is already running

Manifest entries carry each image's width and height, and ArtworkCapabilities answers dimensions: true, so a client can shape a system's grid around its box art.

Arcade compatibility

Jellyfin only.

Method Path Auth Description
GET /Moonfin/Games/ArcadeCompatibility/Status Admin Whether the FBNeo and MAME data files are installed, and what they cover
PUT /Moonfin/Games/ArcadeCompatibility/{core}/Dat Admin Replace one core's data file with an uploaded XML, up to 512 MB

Active downloads

Method Path Auth Description
GET /Moonfin/Transcodes/Active Admin Active transcode and download jobs with progress and session info (includePlayback adds HLS playback jobs)
DELETE /Moonfin/Transcodes/Active/{jobId} Admin Cancel an active job
GET /Moonfin/Transcodes/Mine Yes Jellyfin only. The caller's own transcode jobs, scoped by the device id on its token, so a client can show progress and an ETA for a download it started

Client logs

Emby only. Jellyfin has POST /ClientLog/Document of its own and Moonfin uses that there. Emby has no equivalent anywhere in its API, so the plugin provides one, which is what makes the app's Send report to server button work on Emby.

Method Path Auth Description
POST /Moonfin/ClientLog/Document Yes Upload a diagnostic or crash report as text/plain. The report is written into the server's log folder as upload_<client>_<version>_<utc>.txt, matching the .txt Emby writes its own logs as, so it appears under Logs beside them. The response names the file: { "FileName": "..." }

Not admin-only on purpose: crash reports come from whichever user hit the bug. The guards are elsewhere. The body is capped at 1 MB (413 past it), each user gets 10 uploads an hour (429 past that), the file name is built entirely on the server from the authenticated session rather than anything the client sends, and the whole endpoint returns 503 when Enable client log upload is off in the plugin settings. Clients redact server addresses, IP addresses and access tokens before sending.

Seerr

Method Path Auth Description
GET /Moonfin/Seerr/Config Yes Seerr config for the client (see the response shape below)
POST /Moonfin/Seerr/Login Yes Sign in to Seerr with Jellyfin/Emby or local credentials and store the session
GET /Moonfin/Seerr/Status Yes Whether Seerr is enabled and the current user's session status
GET /Moonfin/Seerr/Validate Yes Confirm the stored session is still valid
DELETE /Moonfin/Seerr/Logout Yes Log out and clear the stored session
GET /Moonfin/Seerr/Radarr/Calendar Yes The upcoming Radarr calendar (optional start and end). The server reads the Radarr connection from Seerr with a stored Seerr admin session and calls Radarr itself, so the API key stays on the server and remote clients still see it. Returns 503 when Seerr is off or no Seerr admin has signed in through Moonfin, and 404 when Seerr has no Radarr server. It uses Seerr's regular (non-4K) default Radarr, and when Seerr's internal address for it can't be reached it tries the External URL set in Seerr, but only over https or when that address is on the local network
GET /Moonfin/Seerr/Sonarr/Calendar Yes The same for Sonarr. Clients try it first for a series' next air date and fall back to TMDB
GET, POST, PUT, DELETE /Moonfin/Seerr/Api/{**path} Session Authenticated proxy to the Seerr API using the stored session. For a user with a parental limit, titles above it and unrated ones are left out of discover, search, recommendations, credits and collection results, and opening, requesting or watchlisting one answers 403. The rating comes from TMDB, so without a TMDB key such a user gets nothing

See Reverse Proxy and Seerr for proxy and single sign-on notes.

Seerr config response

{
  "enabled": true,
  "url": "https://seerr.example.com",
  "displayName": "Seerr",
  "variant": "seerr",
  "userEnabled": true
}
Field Type Description
enabled bool Whether Seerr is enabled by the admin
url string The Seerr server URL used for proxying
displayName string The name shown in the client (admin override, defaults to Seerr)
variant string Always seerr on current versions
userEnabled bool Whether the user has Seerr enabled in their own settings

Notifications (Seerr and new media)

Method Path Auth Description
GET, POST /Moonfin/Notifications/Prefs Yes Get or save the caller's notification preferences, including the new media opt-in (notifyOnNewMedia)
POST, DELETE /Moonfin/Notifications/Register Yes Register or remove a push token for the caller's device
GET /Moonfin/Notifications/WebhookInfo Admin The Seerr webhook URL, secret, supported types, and provisioning status
POST /Moonfin/Notifications/Reprovision Admin Re-register Moonfin's webhook in Seerr immediately
POST /Moonfin/Seerr/Webhook No (shared secret) Inbound Seerr webhook, authenticated by the secret query or X-Moonfin-Webhook-Secret header

Web hosting and discovery

Method Path Auth Description
GET /Moonfin/Web/{**path} No Serve the Moonfin web app static files with SPA fallback
GET /Moonfin/Web/config.json No Runtime config for web plugin mode
GET /Moonfin/Web/loader.js No The loader bridge script for the stock Jellyfin header button
GET /Moonfin/Discovery and /Moonfin/Discovery/discover No Same-origin server metadata for web discovery
⚠️ **GitHub.com Fallback** ⚠️