Files
ombi-mcp/docs/schema/02-tool-mapping.md
gronod 80e01253a2
Build and publish / Test and build (linux) (pull_request) Canceled after 0s
Build and publish / Test and build (windows) (pull_request) Canceled after 0s
Build and publish / Build and publish Docker image (pull_request) Canceled after 0s
Build and publish / Test and build (darwin) (pull_request) Canceled after 40s
Build and publish / Test and build (darwin) (push) Successful in 2m9s
Build and publish / Test and build (linux) (push) Successful in 2m34s
Build and publish / Test and build (windows) (push) Successful in 3m11s
Build and publish / Build and publish Docker image (push) Successful in 1m55s
Fix #10, #27 and #28 from the read-tools sweep
v2 request lists sent the RAML example sort field requestDate; Ombi
looks up RequestedDate and NullReferenceException'd every non-empty
page. Browse now streams TV popular/most-watched payloads and skips
the hydrated seasonRequests graph that blew the 8 MiB read budget.
provider_summary treats an empty upstream body as an empty group_page.
2026-09-19 20:59:57 +01:00

32 KiB

Tool catalogue and routing

Grouping strategy

There are 31 defined tools: 18 core, 5 moderation, 8 administration. Core is the ordinary media workflow; it is not a guarantee that every core branch is authorized for every Ombi user. User listing, retry queue inspection, saved integration server/user details, cron validation and another user's preferences require additional branch authorization. The administration and moderation bundles are opt-in. A read-only deployment can advertise just the 12 core read tools, with privileged branches removed.

Grouping follows a task and side-effect boundary: searching, browsing, reading details, submitting, moderating, deleting and subscribing are distinct. Movie/TV/album variants belong together when their intention is the same. Settings sections share one schema-driven administration tool; credentials, raw HTTP, arbitrary URLs, full entity edits and unbounded bulk deletes are not tools. This balances discoverability against overly large action enums.

The input catalogue defines every parameter, required field, enum and structural constraint. The operation ledger supplies exact method/path pairs, wire parameters and upstream body/result types. Together with the rules below, these form the routing contract. There is no undocumented fallback precedence or generic endpoint passthrough.

Catalogue

Tool Task Bundle Result family
read_search Text, multi, movie refinement, actor search core media page
read_discover Curated lists, similar movies, collection, credits, artist albums, advanced movie filters core media page
read_media Details by explicit provider or request; ratings; streaming core media/details page
read_reference Genres, languages, keywords, watch-provider catalogue, countries, issue categories core reference page
read_requests List/get/search, TV children, recent requests, privileged retry queue core request page / retry page
read_request_stats Counts, totals, per-media quota, user-has-requests core metrics
read_issues Issues, grouped summaries, comments and counts core issue/comment/group page or metrics
read_votes Global vote list or votes on a request core vote page
read_users Self, authorized user lookup, claims, online users, preference read core user/reference page
read_library Recent additions, calendar, artwork core media/calendar/artwork page
read_server Status, version, features, stats, cron validation core metrics/reference page
read_integration Saved ARR options and authorized media-server metadata core reference/user page
write_request_create One media request or explicit collection request core mutation
write_request_subscribe Subscribe/unsubscribe core mutation
write_issue_create Report an issue core mutation
write_issue_comment Add a comment core mutation
write_vote Up/down vote core mutation
write_user_preferences Language, streaming country, newsletter opt-out core mutation
write_request_moderate Approval, denial, availability moderation mutation
write_request_delete Explicit single movie/album/TV-parent/TV-child deletion moderation mutation
write_request_options Advanced routing overrides and TV root/quality moderation mutation
write_request_reprocess Reprocess an existing request moderation mutation
write_issue_manage State, deletes, category management moderation mutation
read_settings Safe configuration projection and revision administration settings
write_settings_patch Typed non-secret patch or feature flag administration mutation
write_user_manage Delete user or send welcome email administration mutation
write_integration_test Test a saved profile; can send notifications administration mutation
write_job_run Trigger permitted jobs / watchlist revalidation administration mutation
write_notification_send Email explicit recipients administration mutation
write_retry_remove Remove one queue entry administration mutation
read_logs Bounded, sanitized diagnostic reads administration log page

Shared routing and input rules

  1. Reject unknown properties and irrelevant branch arguments. Required action, media, provider, kind and IDs cannot be supplied by silent defaults. Optional page values default server-side to offset 0 / limit 25; maximum limit is 100. Schema defaults are documentation, not mutation of the input.

  2. Upstream spelling and case are literal. request/music, Request/movie, Requests/album, NotificationPreferences and notificationpreferences are distinct contract spellings. Encode values; do not change literal paths to match public naming conventions.

  3. The public media names are movie, TV, artist and album. “Music” is an upstream route segment or the multi-search filter, not a universal alias. No artist-request operation exists. External IDs are never interchangeable with Ombi request IDs. Music IDs are opaque nonempty provider strings until a stronger MusicBrainz format is verified.

  4. Enum label maps are now published for four upstream integer enums. On the input side, string labels are the only accepted form — *_code integer fields are rejected via additionalProperties:false, and an unknown label fails with INVALID_ARGUMENT. On the read side, every mapped enum emits the string label in its *_label field (status, request_type, agent, notification_type) and the raw int in its *_code twin (status_code, request_type_code, agent_code, notification_type_code) for provenance; an unmapped upstream int omits the label, emits only *_code, and appends unmapped upstream enum value <n> for <field> to warnings[] (graceful degradation).

    T1 — RequestType (upstream enum:[0,1,2]). Critical: upstream order is TvShow=0, Movie=1, Album=2 — NOT positional. Verified against upstream source; still verify per-instance for version drift.

    MCP string Upstream int Used by
    movie 1 write_issue_create.request_type, write_request_reprocess.request_type, issue/retry outputs
    tv 0 same
    album 2 same

    T2 — IssueStatus (upstream enum:[0,1,2,3]; frontend confirms Pending=0, InProgress=1, Resolved=2; closed→3).

    MCP string Upstream int Used by
    pending 0 read_issues list/summary status, write_issue_manage.set_status.status, issue output status
    in_progress 1 same
    resolved 2 same
    closed 3 same

    T3 — NotificationAgent (upstream enum:[0..11]).

    MCP string Int MCP string Int
    email 0 mobile 7
    discord 1 gotify 8
    pushbullet 2 webhook 9
    pushover 3 whatsapp 10
    telegram 4 ntfy 11
    slack 5
    mattermost 6

    T4 — NotificationType (upstream enum:[0..16]).

    MCP string Int MCP string Int
    new_request 0 issue_resolved 9
    issue 1 issue_comment 10
    request_available 2 newsletter 11
    request_approved 3 partially_available 12
    admin_note 4 plex_watchlist_token_expired 13
    test 5 request_deleted 14
    request_declined 6 issue_in_progress 15
    item_added_to_fault_queue 7 issue_deleted 16
    welcome_email 8

    Remaining numeric enums are unresolved gaps and stay numeric pending verification: VoteType [0,1] (labels unverified), RequestSource, RequestLimitType [0,1,2], and the legacy orderType/statusType/availabilityType filter ints. Search response IDs must retain their source/provider namespace, including multi-search TV TMDB IDs.

  5. A TV parent is a show record; a TV child is an individual request under it. read_requests.list(media=tv) uses v2 child pages. Parent detail and children enumeration use v1. A compact projection of v2 children is preferable to secretly swapping in tvlite parent results (the v2 list contract is preserved). Return parent_request_id alongside child IDs when present. read_requests.search for TV uses v1 Request/tv/search/{term} but falls back to a bounded v1 parent scan if it fails, compensating for an upstream LINQ bug. v2 list {sort} is the C# property RequestedDate (not the RAML example requestDate, which TypeDescriptor cannot resolve and which 500s every non-empty page).

  6. Parameter names such as currentPosition, position, skip, count, take and amountToLoad are mapped exactly per ledger. Offsets are zero-based by this MCP contract; the adapter must verify ambiguous upstream paging behaviour. Requests use count then position; issue summary uses position then take; issue list uses take then skip. Do not reverse these pairs.

  7. Array responses without server pagination are sliced locally only within a bounded fetched response. Mark pagination as local and total unknown unless the complete collection was obtained. A result-size cap is a truncation warning, not a fabricated server total or a promise that the next page exists.

  8. format: date-time and cross-field comparisons must be enforced by the server, not assumed from a client's validator. Stats requires from <= to when both are supplied. Strings must contain non-whitespace text where used as queries/comments. Reject duplicate season numbers and duplicate episode numbers; impose a maximum of 2,000 selected episodes per call in addition to per-array limits.

  9. Never infer permissions solely from the fact that a route is in RAML. Verify capability/role at call time, enforce local policy and retain upstream denial. Per-tool annotations are conservative: all mutations are non-idempotent until side effects are verified; all tools have openWorldHint: true because they interact with a configured external service.

Search, discovery and details

read_search.text routes to the matching v1 Search/movie, Search/tv, Search/music/artist, or Search/music/album search-term route. It does not fan out implicitly. multi POSTs to v2 multi search; map tv_shows to tvShows and include all four Boolean properties explicitly, false for categories not selected. movie_refine POSTs {searchTerm, year?, languageCode?}. actor POSTs {searchTerm, languageCode?} to v1 movie/actor. Local page is never forwarded as an undocumented query parameter.

read_discover.browse uses paged v2 routes. Map now_playing→nowplaying, top_rated→toprated, most_watched→mostwatched. Movie allows popular/now-playing/top-rated/upcoming/seasonal/requested; TV allows popular/anticipated/most-watched/trending/requested. No other cross-product is valid. Nonpaged v1 and v2 equivalents are compatibility routes only.

similar is POST v2 movie/similar with {theMovieDbId, languageCode?}. A verified v1 POST equivalent can preserve language; a v1 GET cannot preserve a language argument. collection returns the collection's members/basic metadata, without creating requests. credits chooses actor/{actorId}/movie or /tv. artist_albums uses v1 music/artist/album/{foreignArtistId}.

advanced_movie POSTs the exact DiscoverModel property names: type:"movie", release_year→releaseYear, genre_ids→genreIds, keyword_ids→keywordIds, watch_provider_ids→watchProviders, and decade unchanged. Ombi 4.53.x accepts but ignores companies, so company_ids is not advertised and legacy calls fail explicitly. Do not invent a query requirement for discovery. Release year and decade must agree if both are supplied.

read_media.details chooses v2 movie TMDB/IMDb, TV TVDB/TMDB, artist, or artist/album routes. TV tmdb uses v2 Search/tv/moviedb/{id}; TV tvdb uses the legacy v1 Search/tv/info/{tvdbId} route (the v2 Search/tv/{tvdbId} route is a TMDB-keyed alias and must never serve a TVDB lookup). The legacy v1 TVDB info route does not reliably carry request state; for TVDB, the adapter performs a bounded scan of v1 Request/tv (matching by tvDbId or imdbId) to overlay requested, request_targets (using the real parent request ID), and per-episode availability flags. IMDb path placeholder spelling differs from its parameter declaration. by_request chooses movie/request, tv/request or artist/request. movie_localized uses POST v1 movie/info. ratings first uses Ombi's title/year ratings route. Ombi 4.53.x still routes that operation through removed Rotten Tomatoes private endpoints; on a 404/5xx (or a successful empty response), the adapter searches the same media/title and requires an exact case-insensitive title and year match before returning explicitly labelled tmdb_vote_* or tvmaze_site_rating references. A warning identifies the fallback source; no fuzzy match is substituted. streaming uses TMDB even for TV. TV identifier labels follow the origin route, not the upstream field name. The v1 TVMaze-backed routes (Search/tv/{term}, Search/tv/info/{tvdbId}) place the TVDB id in the misleadingly-named theMovieDbId field and the TVMaze id in seriesId; emit tvdb and tvmaze there. The v2 engine routes (browse, moviedb, by_request, multi-search) are TMDB-keyed; emit tmdb and do not treat seriesId as TVMaze. When theMovieDbId is absent, id is labelled with that origin namespace so browse and collection members still carry a usable identifier. Multi-search mediaType is case-insensitive (Artist→artist). The v1 RecentlyAdded TV model's id namespace is unverified — keep tmdb pending live confirmation.

read_reference.keywords passes query searchTerm; keyword detail uses its own keyword ID. Watch-provider catalogue search also has optional searchTerm; it is distinct from streaming availability for a particular title. Other branches have no request body. Reference values are good optional cached resources, but remain available through tools for tool-only clients.

Requests and quotas

Use v2 list/status routes, always with sort and page segments. status defaults all; sort_direction maps to the {sortOrder} segment (asc/desc). Public sort.field=request_date maps to Ombi's RequestedDate property (the RAML example requestDate does not match and 500s on non-empty pages). No speculative sort fields are published. all means the base route, not an /all/ segment. Album lacks an unavailable-status route, so that combination fails schema validation. The misspelled movie availble route is an explicitly gated compatibility alias, not the primary path.

get supports movie and TV parent only; no album single-request endpoint is advertised. children returns children for a parent; child ids are the real upstream child PKs (provider-shaped for first-request children by design), with provider ids surfaced from the embedded parentRequest. Request search uses the appropriate v1 route and rejects list-only status/sort arguments. recent uses v2 recentlyRequested; for movie/album target.id is requestId, while TV requestId is a child request id (often provider-shaped) that is resolved to the parent request id through a bounded v1 parent scan — unresolvable rows emit target.id 0 with a warning rather than a provider-shaped target. retry_queue is a privileged GET and returns queue IDs separately from underlying request IDs.

read_request_stats.counts uses Request/count; total uses the media's total endpoint; quota uses its remaining endpoint. Quota belongs to the actual upstream principal. has_requests requires an explicit user_id by MCP policy and sends it as the optional upstream userId query; viewing another user is subject to authorization. Do not combine instance totals with a per-user quota under an unlabeled “total”.

Creating requests

Branch Route Exact body construction
movie POST /api/v1/Request/movie tmdb_id→theMovieDbId, is_4k→is4kRequest (optional, defaults false), language→languageCode; authorized optional on-behalf/overrides
TV, TMDB POST /api/v2/Requests/tv id→theMovieDbId, optional languageCode, selection, language profile and overrides
TV, TVDB POST /api/v1/Request/tv id→tvDbId, selection, language profile and overrides; no languageCode or 4K field
album POST /api/v1/request/music musicbrainz_id→foreignAlbumId, optional requested_by_alias→requestedByAlias; no shared overrides
collection POST /api/v2/Requests/movie/collection/{collectionId} No documented body. No 4K, language or on-behalf overrides accepted
TV season selection (TV branches above) season_numbers[] → adapter reads details, expands each season's full episode list → seasons[].episodes; never sent as a bare season flag

on_behalf_user_id is an MCP user-ID contract. Enable only after verifying whether upstream requestOnBehalf expects that ID or a username; if username, resolve ID to username internally and explicitly. Translate root/quality overrides to rootFolderOverride/qualityPathOverride. Never silently ignore unavailable options.

TV selection all, first_season, latest_season sets exactly one of requestAll, firstSeason, latestSeason true and the other two false. season takes season_numbers[]; the adapter reads TV details in the same provider namespace (TMDB via v2 Search/tv/moviedb/{id}, TVDB via v1 Search/tv/info/{tvdbId}), expands each listed season to its full episode list and emits explicit seasons[].episodes — expansion is the default construction, never an upstream empty-means-all assumption. episodes sets all false and maps seasons to {seasonNumber, episodes:[{episodeNumber}]}. Require explicit nonempty episode lists. RAML does not establish that an empty list means a whole season; a caller wanting a full season must read details and select its actual episodes. If a verified adapter later adds a whole-season shortcut, add an explicit branch. Do not fabricate source enum labels or send a Plex-watchlist origin from this MCP client.

Collection requests may have partial effects; RAML specifies a single RequestEngineResult, not a per-movie result list. Read/display collection membership for review, but do not imply transactional consistency between that read and the later creation. Return accepted/unknown detail where upstream offers no per-item results. Do not simulate collections by silently issuing many movie POSTs.

Existing request writes

Moderation approve/available/unavailable is POST; deny is PUT for all three media families. Movie body is {id, is4K} and deny adds reason; is_4k is optional and defaults false. TV/album body is {id} and deny adds reason. Require a nonempty denial reason by MCP policy. Movie creation's is4kRequest and moderation's is4K are deliberately different wire names.

TV moderation IDs are treated as child IDs by the proposed contract, but RAML's bare id does not prove controller semantics. Verify before enabling, including subscription and advanced-options target semantics. Do not send a parent ID merely because an integer validates.

Delete routes distinguish movie, album, TV parent and TV child. Parent deletion may affect all associated children and must be described that way. No delete-all branch exists. Subscription has separate movie/TV subscribe/unsubscribe endpoints; album subscriptions are absent.

Advanced options body is {requestId, rootPathOverride?, qualityOverride?, languageProfile?}. The schema accepts only these editable fields. Direct TV root/quality PUTs put both IDs in the URL and have no body. Reprocess uses numeric RequestType, request ID and is4K path Boolean; enable supported type/ID/variant combinations only after verification. In particular, do not claim that album reprocessing is absent simply because album lacks its own named route.

Full-entity movie/TV/child PUTs are intentionally excluded: their server-owned and recursive fields are not safe public patch contracts. This does not prevent typed routing changes through the documented advanced-options routes.

Issues, votes and users

Issue list uses v1 paged issues and returns individual records. summary uses v2 and returns provider-grouped summaries; these are not interchangeable pagination units. Provider details v2 is a summary route; the v1 provider/request routes have unspecified result schemas and must be verified before publishing their projections. Category reads are owned by read_reference, avoiding a second overlapping tool branch.

Issue creation maps only title, subject, description, issueCategoryId, numeric requestType, and optional requestId/providerId. Require at least one association by design; RAML marks these properties optional and does not establish a server requirement. If both are provided, verify they refer to the same media. The server supplies author, timestamps and state; never accept userReported, comments, resolved date or persistence ID from the model. Comment POST uses {comment, issueId}. Status POST uses {issueId, status}. Category POST uses {value}. Delete IDs are in the path; no speculative update-category endpoint exists.

Vote reads use Vote/music/{requestId} for albums; writes use Vote/{up|down}/album/{requestId}. Do not derive both directions from one generic segment rule. Vote toggling/repeated-call behaviour is unverified, hence no idempotence promise. On Ombi 4.53.10, the global vote view and movie vote-record route can return HTTP 500 for existing instance data while the corresponding empty TV route succeeds. Ombi exposes no equivalent read route from which the adapter can reconstruct vote ownership or totals. Preserve UPSTREAM_REJECTED in that case; never convert the failure into a fabricated empty vote_page.

User GET-by-ID is /Identity/User/{id}. self, all users, dropdown, claims, online and notification preferences each have their exact separate routes. Respect hide-user settings and upstream visibility; do not disclose hidden requester IDs simply to populate a normalized field.

Language writes send {lang}; country writes send {code}. Newsletter opt-out is the mutating GET /Identity/newsletter/unsubscribe/{userId} and is always a write tool. An explicit user ID prevents accidental interpretation as a query; enforce self-or-authorized-admin access. Preference values can be delivery credentials, so generic notification-preference mutation remains outside the tool surface.

User creation/update/local-profile mutation are intentionally excluded because their RAML bodies mix credentials, claims and internal state. Administrative deletion and welcome email are supported separately. Welcome email loads the existing user's required view-model fields internally; it never asks the model to construct a UserViewModel or password.

Library, server, settings and integrations

Recent TV grouped and ungrouped routes are separate branches. Calendar has no documented date-range query: local bounding is labeled, not sent as an invented parameter. Movie images use TMDB; TV accepts explicit TVDB or TMDB. TV banners are not documented. Album art uses releasegroupart. The generic poster and background routes take no documented title ID; never describe them as a selected title's poster.

Images may be binary, redirects or URLs depending on the route; RAML leaves several response bodies unspecified. Return a safe resource reference only after validating actual content type/shape and destination. Do not invent a signed URL or expose an ApiKey query. Random backgrounds are read-only but their outputs are not deterministic; idempotent read annotations describe side effects, not identical results.

Stats requires RFC3339 from and to query values. update_check is GET Job/update; update_info is GET Update. Running updates is an explicit administrative job. cron_validate POSTs {expression} to Settings/testcron and is an administrator-gated read calculation, not a scheduled-job mutation. Ombi uses Quartz cron syntax: six or seven fields, with ? in the unused day-of-month or day-of-week field.

read_integration prefers saved-settings GETs. Radarr 4K only applies to profiles/root folders, not tags. Sonarr language profiles uses /v3/LanguageProfiles. Lidarr Metadata is POST-only, so load saved Lidarr settings privately and construct its request internally. CouchPotato profile is singular and POST-only. Credential acquisition /CouchPotato/apikey is never a tool. RPC POST counterparts accepting settings are compatibility adapters to the same read intent; they do not add caller connection overrides.

Plex library lookup uses a known saved machine_id (discoverable via read_integration plex servers, which emits the machine identifier in id); Emby/Jellyfin info and Library POSTs use an authorized saved server_id to resolve the server settings privately (discoverable via read_settings emby / jellyfin / plex, which expose /servers/<digits>/{id,serverId,machineIdentifier} leaves in values). No arbitrary server object or connection destination is accepted. Routes that acquire Plex/Emby/Jellyfin access or provision accounts remain internal/manual because their authentication/side effects are not adequately specified here.

Settings section names and wire types are fully listed in the registry. Read-only flags and customization content are not accidentally accepted as writable sections. Before saving, privately read the original section, verify the revision, merge a typed patch preserving secrets and omitted fields, and POST the full correct wire model. An incomplete original object means save is unsupported. Redacted strings must never become stored credentials. Feature writes are enable/disable POSTs with {name, enabled}.

Integration testers take an administrator-provisioned profile ID. The server selects the exact tester body type, resolves credentials internally and forbids destination overrides. profile_id must match the service and authorized instance; it is not an arbitrary filesystem path. Some tests really send messages; completion means the tester returned, not necessarily that a human received a notification.

Job run names exactly match the 14 POST Job routes in the ledger, including the case-sensitive arrAvailability. revalidate_watchlist separately POSTs Plex/WatchlistUsers/revalidate. Update, clear-media-server-data, auto-delete and newsletter jobs have significant effects; deployments must opt in to each. No synthetic job-status polling API or MCP task handle is claimed.

Mass email maps subject/body/bcc and resolves explicit unique user IDs to the upstream users entity list privately. Verify the minimal accepted user fields before enabling; never accept arbitrary OmbiUser JSON. Recipient count is capped at 100; an empty list cannot mean “everyone”. Retry removal only DELETEs its queue ID, and never calls a nonexistent POST RequestRetry.

Logs list returns opaque file IDs bound to a vetted upstream basename. Read accepts that ID, with bounded local line slicing. Reject unknown IDs and traversal; sanitize before caching or returning. If robust sanitization is unavailable, omit logs tools. Raw log downloads and UI logging submission remain excluded.

Compatibility policy

The ledger's alternative routes are accounted for, not all promised working fallbacks. Use only an adapter verified for the instance/version. A global 404 probe cannot distinguish unsupported routes from hidden or missing resources. Never fall back on 401/403, arbitrary 404, validation failures or ambiguous mutation outcomes. Do not transform TVDB to TMDB by copying an integer; any conversion requires an authoritative lookup and an unambiguous match.

Legacy request filters are integers without documented mappings. No passthrough filter integers are exposed and no “partial availability” mapping is invented. Where v2 is unavailable, a verified unpaged v1 list may be projected and locally bounded with truthful metadata; unavailable filtered semantics produce UNSUPPORTED_CAPABILITY. API version selection must not silently change parent/child granularity, counts, language, filters or target identity.

Appendix A — Parameter name translation

Locked decision D3 keeps snake_case MCP field names; the Go adapter translates each to its exact wire form. This table is the mandated mapping layer — every public field name appears here with its upstream spelling per context.

MCP field Context Wire form
query search/requests/issues keywords {searchTerm} path or searchTerm body/query
person_id read_discover.credits {actorId} path
artist_id read_discover.artist_albums {foreignArtistId} path
collection_id read_discover.collection, write_request_create.collection {collectionId} path
tmdb_id read_media, write_request_create.movie theMovieDbId body / {movieDbId}/{theMovieDbId} path
tvdb_id read_library.tv_images {tvdbid} path
musicbrainz_id write_request_create.album, read_library.album_art foreignAlbumId body / {musicBrainzId} path
request_id many tools {requestId}/{id} path or requestId/id body (per ledger)
parent_request_id read_requests.children, write_request_options.tv_* {parentRequestId}/{requestId} path (verify per route)
issue_id read_issues.comments, write_issue_manage, write_issue_comment {id} path / issueId body
comment_id write_issue_manage.delete_comment {id} path
category_id write_issue_manage.delete_category, write_issue_create {catId} path / issueCategoryId body
keyword_id read_reference.keyword {keywordId} path
user_id read_users.get, write_user_preferences, write_user_manage, write_notification_send {id}/{userId} path / users body list
queue_id write_retry_remove retry-queue {requestId} path (queue namespace only)
file_id read_logs.read {logFileName} path
machine_id read_integration.plex_libraries saved Plex server resolution (no passthrough)
server_id read_integration.media_server saved Emby/Jellyfin server resolution (no passthrough)
profile_id write_integration_test saved-settings resolution (no passthrough)
root_folder_id create/options rootFolderOverride body / {rootFolderId} path
quality_profile_id create/options qualityPathOverride body / {qualityId} path
language_profile_id create/options languageProfile body
is_4k create/moderate/reprocess is4kRequest body / is4K body / {is4K} path
request_type write_issue_create, write_request_reprocess requestType body int / {type} path int (T1 map)
status read_issues, write_issue_manage {status} path int / status body int (T2 map)
sort_direction read_requests.list {sortOrder} path asc/desc (sort=RequestedDate)
season_numbers write_request_create tv season mode adapter expansion → seasons[].episodes
on_behalf_user_id write_request_create requestOnBehalf body (Verify: id vs username)
requested_by_alias write_request_create.album requestedByAlias body
from,to read_server.stats from,to query
expression read_server.cron_validate {expression} body
section,revision,changes read_settings,write_settings_patch private load/merge → section POST body
name,enabled write_settings_patch feature FeatureEnablement body → enable/disable POST
subject,body,bcc,user_ids write_notification_send NotificationMessage fields; ids → users entity list
job write_job_run.run literal /api/v1/Job/{job} path segment
media everywhere route segment or body discriminator per ledger