Public client base URL: the Dashboard origin plus /api/v1 (for local development, http://localhost:5016/api/v1).
The Engine origin (http://localhost:61495 in development) is an internal service boundary. Native clients must discover and call the Dashboard origin and never receive the Dashboard service credential or connect to the Engine port.
Administrative Engine endpoints require live effective administrator authority (with any configured surface unlock), or the exact registered Application permission where supported. Public v1 client endpoints use scoped bearer tokens issued to a paired device. Every route also enforces its resource/profile scope; the Auth column below is a summary, not a substitute for the mapped endpoint policies. See the Access implementation status for final cutover gates.
API v1 is the frozen native-client contract. Breaking request or response changes require a new major path. Additive fields may be introduced in v1; clients must ignore fields they do not understand.
GET /.well-known/tuvima on the Dashboard origin returns the public API base, supported versions, device-authorization and token endpoints, verification URL, and server capabilities.
A television or other input-constrained client posts form data or JSON to POST /api/v1/oauth/device_authorization with a stable public client_id, display names, version, device class, requested space-delimited scopes, and capabilities.
The user opens the returned verification_uri on an authenticated browser, reviews the exact client/device/scopes, and approves or denies it. Device codes expire after 10 minutes and polling starts at the returned interval.
The device polls POST /api/v1/oauth/token using grant type urn:ietf:params:oauth:grant-type:device_code. authorization_pending, slow_down, access_denied, and expired_token are normal OAuth error responses.
Access tokens last 10 minutes. Refresh tokens rotate on every successful refresh_token grant and expire after 30 days. Reuse of a consumed refresh token revokes its token family.
Tokens are opaque bearer credentials and must be stored in the platform secure credential store. Device and token identifiers in request bodies or query strings are never authority; the Engine uses the profile, device class, client, and scopes bound to the bearer token. GET /api/v1/devices lists grants for the active profile, GET /api/v1/devices/current describes the caller, PUT /api/v1/devices/current/capabilities replaces negotiated capabilities, and DELETE /api/v1/devices/{deviceId} immediately revokes that device and all of its tokens.
GET /api/v1/display/home, /browse, /shelves/{key}, /groups/{id}
library.read
Search
GET /api/v1/display/search?q=
library.read
Details
GET /api/v1/details/{entityType}/{id}
library.read
Artwork/media bytes
URLs returned by display/detail/manifest models; /api/v1/stream/{assetId} supports ranges and signed /api/v1/stream/hls/... paths serve adaptive packages
artwork.read or playback.read as applicable
Progress
GET/PUT /api/v1/progress/{assetId}, GET /recent, GET /journey
progress.read / progress.write
Queues
GET /api/v1/player/state, mutations under /api/v1/player/queue
Capability negotiation declares containers, video/audio codecs, subtitle formats, protocols, maximum dimensions/bitrate/channels, HDR, speed, and offline-download support. The playback manifest compares those registered capabilities with inspected source media and returns directPlaySupported, recommendedDelivery, source/alternate URLs, tracks, chapters, resume state, warnings, and a conversion reason. For HLS it also returns hlsStatus, hlsUrl, and hlsExpiresAt. preparing means the client should retry the manifest; ready supplies the playable path. Clients must follow the manifest rather than independently guessing whether a file can direct-play.
The HLS URL is a bearer capability scoped to one asset, package, and expiration time. It is intentionally usable by native HLS media stacks that cannot attach application authorization headers to every segment request. Clients must not log, persist, or share it. Expired or modified paths return 403; missing and traversal-rejected package resources return 404. Relative URIs in the master and media playlists preserve the same signed path for variant, alternate-audio, caption, and segment requests.
Offline encode jobs created by paired clients are owned by the authenticated
profile and device. Job listing and cancellation use those trusted token claims;
one client cannot enumerate, cancel, discover, or download another device's
work. Public JSON never includes the Engine's local output path. Clients
download only the API v1 URL exposed by their ready offline-variant model.
Most paginated GET endpoints accept offset/limit query parameters. View
timeline and discovery endpoints use opaque/keyset cursor plus limit, and
Manual Gallery membership uses position/item keysets. Limits are clamped
server-side so a caller cannot force an unbounded read.
JSON errors use RFC 7807 Problem Details through the shared ApiErrors
factories. JSON success bodies use named MediaEngine.Contracts types, and
every production route declares its 2xx response metadata for Swagger. Untyped
success metadata is reserved for reviewed bodyless, redirect, file, stream, and
direct-response routes.
MediaEngine.Contracts is the sole owner of non-frozen HTTP and SignalR wire
types. Engine endpoints map internal projections explicitly into those
contracts, and the Dashboard may add presentation wrappers without redefining
the JSON payload. The only frozen exception is the exact Universe/Chronicle
SignalR pair LoreDeltaDiscoveredEvent and
UniverseEnrichmentProgressEvent; Universe HTTP responses are not exempt.
The canonical contracts intentionally include fields that existed on only one
side of the former API/Dashboard mirror boundary. Notable examples are
ingestion count_unit; person and person-backed collection roles; complete AI
settings plus resource cpu_pressure; complete managed-collection, placement,
rule, permission, and artwork fields; playback manifest, player, settings,
chapter, bookmark, text-track, and offline-delivery fields; and plugin
manifest, capability, permission, tool, AI-permission, health, job, and approved
catalog fields. These are contract completeness corrections, not separate
compatibility versions.
Library configuration uses schema 4.0, including explicit server-visible folder-picker roots. Older flat-folder and Photos/General
schemas are rejected during the pre-beta cutover rather than migrated or served
through compatibility routes.
Method
Path
Description
Auth
GET
/settings/libraries
Return the complete library configuration, including stable sources and universal incoming sources
Administrator
PUT
/settings/libraries
Validate and replace the complete schema-3 library configuration
Execute one confirmed plan once, revalidating source policy, files, collisions, and free space immediately before each operation
Administrator
Reorganization execution requires both the returned plan_id and its exact
fingerprint. A plan expires after 15 minutes and cannot be reused. Partial
outcomes are reported per item; an existing-library source is always blocked
from mutation.
Personal View routes require the trusted profile assertion established by the
Dashboard. The server-to-server X-Tuvima-View-Profile,
X-Tuvima-View-Timestamp, and X-Tuvima-View-Signature headers bind the active
profile to the exact method/path/query through HMAC-SHA256 using the already
validated API key. They are accepted only for /view and /collections, must
be within the configured short skew window, and are not browser credentials.
Browser-supplied profile, library, Gallery, scope-profile, or asset IDs are
never treated as authority. Friendly Shared, Mine, and permitted Profile scopes
are resolved by the Engine before any asset, Gallery, People, Places,
thumbnail, original-file, or personal-media Collection query runs. Direct
unauthorized identifiers return the same not-found shape as missing resources.
Method
Path
Description
Auth
GET
/view/scopes
Resolve the selected/default scope and return only authorized labeled scope options
Required + trusted profile
GET, PUT
/view/preferences
Read or persist the active profile's last scope and timeline density
Required + trusted profile
GET
/view/assets
Cursor-page authorized mixed local assets by scope, search, kind, state, or Gallery
Required + scope read
GET
/view/folders
Page authorized source roots or one indexed folder hierarchy with URL-backed path breadcrumb, scoped search, and optional descendants
Required + scope read
PUT
/view/folders/pin
Pin or unpin an authorized folder for the active profile
Required + scope read
PUT
/view/folders/timeline-policy
Set or clear the source-owner branch override inherited by the Photos timeline
Source owner or administrator
POST
/view/uploads
Upload to the caller's server-resolved Personal Space
Required + Personal Space owner
GET
/view/items/{id}
Read authorized personal-asset metadata
Required + asset read
GET
/view/items/{id}/content
Stream an authorized original with range support
Required + asset read
GET
/view/items/{id}/thumbnail
Return a bounded local image preview when supported
Required + asset read
PUT
/view/items/{id}/favorite
Set favorite state on an owned asset
Required + asset owner
PUT
/view/items/{id}/hidden
Set hidden state on an owned asset
Required + asset owner
POST
/view/items/{id}/archive
Archive an owned asset without changing its file
Required + asset owner
POST
/view/items/{id}/trash
Soft-delete an owned asset without deleting its original
Required + asset owner
POST
/view/items/{id}/restore
Restore an archived or trashed owned asset
Required + asset owner
POST
/view/shared/contributions/preview
Preview a selected batch, compound-file totals, destination, and eventual move/copy behavior
Shared submit grant + asset owner
POST
/view/shared/contributions
Create an idempotent, revision-bound pending contribution without changing files
Shared submit grant + asset owner
GET
/view/shared/contributions
Page the caller's submissions or the curator review queue
Required; curator grant for review mode
GET
/view/shared/contributions/{id}
Read an authorized contribution, item states, decision, and activity timeline
Contributor or Shared curator
POST
/view/shared/contributions/{id}/cancel
Cancel a caller-owned pending contribution with optimistic revision checking
Contributor
POST
/view/shared/contributions/{id}/decision
Accept or decline a pending contribution and queue accepted transfers
Shared curator
POST
/view/shared/contributions/{id}/retry
Requeue recoverable accepted item transfers
Shared curator
POST
/view/shared/items/direct
Create an auto-accepted contribution for curator-owned items using the same transfer worker
Shared curator + asset owner
GET, POST
/view/galleries
List owned/shared Galleries or create an owned Gallery
Required + trusted profile
GET, PUT, DELETE
/view/galleries/{id}
Read or manage an authorized Gallery
Required + Gallery permission
GET, POST, DELETE
/view/galleries/{id}/items
Page or mutate Manual Gallery membership
Required + Gallery permission
GET, PUT
/view/galleries/{id}/shares
Read or replace selected-profile Gallery shares
Owner + Gallery sharing enabled
GET
/view/share-targets
Return only enabled profiles the caller may select as Gallery share recipients
Required + trusted profile
GET
/view/people
Cursor-page named/reviewed, provenance-aware people annotations within an authorized scope; no face-recognition claim
Required + scope read
GET
/view/places
Cursor-page real GPS/place aggregates within an authorized scope; no map/tile request
Required + scope read
GET
/view/admin/profiles/{profileId}/sources
Return an administrator-safe source/device summary without paths, secrets, client identifiers, or invented capacity
Administrator
POST
/view/admin/profiles/{profileId}/reconcile
Recovery/admin reconciliation for a profile's Personal Space
Paginated item list for current browse/detail surfaces. Includes projection-backed fields such as pipelineStep, libraryVisibility, isReadyForLibrary, artworkState, artworkSource, and artworkSettledAt. Supports filtering by status, media type, collection, and search term.
Required
GET
/library/items/{entityId}/detail
Full item detail including claims, canonical values, pipeline projection fields, artwork truth, and linked persons
Dry-run scan of configured library folders. Reports what would be ingested without making changes.
Administrator
POST
/ingestion/library-scan
Scan library folders and update known file paths. Triggers ingestion for new files.
Administrator
POST
/ingestion/upload
Upload a file to the explicit managed, writable primary destination identified by multipart destinationLibraryId; catalogued files retain stable intake IDs, while personal files are indexed directly into View without catalogue/provider work
Administrator
GET
/ingestion/operations
Dashboard snapshot backed by durable ingestion operation counts, numbered stage_progress rows, current activity, review state, provider health, and recent batch summaries.
Effective administrator or precise Application permission
GET
/ingestion/batches
Recent ingestion batches.
Effective administrator or precise Application permission
GET
/ingestion/batches/{batchId}
Single ingestion batch summary.
Effective administrator or precise Application permission
GET
/ingestion/batches/{batchId}/items
Durable per-file item ledger for a batch, sourced from media_operations.
Effective administrator or precise Application permission
GET
/ingestion/watch-folder
Returns the derived current watch folder view from configured library source folders.
Required
/ingestion/operations.stage_progress contains numbered ingestion stage rows with stage_number, stage_key, label, completed_files, total_files, count_unit, percent_complete, active_count, queued_count, status_label, active_item_label, active_group_label, active_group_count, label_accuracy, artifact_label, artifact_count, detail_items, last_updated_time, and is_stale. count_unit identifies what the counts measure, such as files, albums, people, artwork assets, or links, so clients do not relabel grouped work as files. detail_items is an optional list of { label, value, tone?, icon? } rows populated by the Engine so clients do not hardcode provider math. Grouped provider work, such as batched Wikidata resolution, uses group labels instead of fake exact file labels unless per-file correlation is available.
The Dashboard renders Stages 1-8 as compact progress rows. Review/attention state remains in the snapshot for API consumers, but the Dashboard surfaces it through the top Need Review metric and the recent_batches review count rather than as another progress bar. recent_batches rows include batch id, status, timing, file totals, media-type counts, registered/review/failed counts, and artifact totals such as people, artwork, metadata, matched, and review.
Live system resource usage - CPU load, RAM pressure, active transcoding tasks
Required
GET
/ai/enrichment/progress
Background enrichment batch progress
Required
GET
/ai/enrich/tldr/{entityId}
Generate a TL;DR summary for a work using its description
Effective administrator or precise Application permission
GET
/ai/enrich/vibes/{entityId}
Generate vibe tags for a work
Effective administrator or precise Application permission
POST
/ai/enrich/search/intent
Parse a natural-language search query into structured field filters
Required
POST
/ai/enrich/extract-url
Extract metadata from a URL (book page, IMDB entry, etc.)
Effective administrator or precise Application permission
/ai/config preserves the complete settings graph, including models,
model_catalog, operational_roles, role_requirements, feature flags, and
scheduling/resource policy. /ai/resources includes numeric cpu_pressure;
clients should use that contract value rather than derive pressure from labels.
List built-in and dynamic plugins loaded by the Engine
Administrator or application with plugins.read
GET
/plugins/approved
Fetch the approved plugin discovery catalog from the configured GitHub source
Administrator or application with plugins.read
GET
/plugins/{pluginId}
Plugin detail, manifest metadata, settings, permissions, and load state
Administrator or application with plugins.read
POST
/plugins/{pluginId}/enable
Enable a plugin
Administrator or application with plugins.manage
POST
/plugins/{pluginId}/disable
Disable a plugin
Administrator or application with plugins.manage
PUT
/plugins/{pluginId}/settings
Save plugin user settings JSON
Administrator or application with plugins.manage
GET
/plugins/{pluginId}/manifest
Read dynamic plugin manifest JSON. Built-in manifests are compiled and are not returned here.
Administrator or application with plugins.read
PUT
/plugins/{pluginId}/manifest
Save dynamic plugin manifest JSON without changing plugin id
Administrator or application with plugins.manage
DELETE
/plugins/{pluginId}
Delete a dynamic plugin folder and saved plugin configuration
Administrator or application with plugins.manage
POST
/plugins/{pluginId}/health
Run plugin health checks
Administrator or application with plugins.read
GET
/plugins/{pluginId}/jobs
List recent durable plugin operation rows for one plugin.
Administrator or application with plugins.jobs.read
POST
/plugins/jobs/segment-detection/run
Run scheduled playback segment detector plugins immediately
Administrator or application with plugins.jobs.run
Plugin responses preserve manifest capabilities, permissions, tool
requirements and per-platform artifacts, AI permissions, settings schema,
health checks, durable job counters, and approved-catalog provenance. Dashboard
presentation types must not narrow that payload.
Delete one display-only audiobook chapter title override
Required
Player and playback contracts carry the complete queue/session state, delivery
manifest, resume data, tracks/subtitles/chapters, audiobook history and
bookmarks, chapter-title provenance, offline variants, diagnostics, and user
playback settings. Text-track contracts retain source and normalized formats,
timing mode, ownership/preference/export flags, confidence, and URL.
Real-time push events: ingestion progress, enrichment completion, MediaOperationChanged, CapabilityStateChanged, pipeline state changes, and review queue updates. Authentication required. Server-push only - clients do not send messages to the collection.
SignalR payloads use MediaEngine.Contracts.Realtime except for the exact
frozen LoreDeltaDiscoveredEvent and UniverseEnrichmentProgressEvent
Universe/Chronicle pair. New events and new payload fields are not covered by
that exception.
These routes belong to the Access replacement under final integration. Inbound credentials resolve the current Application; they do not carry a household role. Metadata, provider, and other outbound service secrets remain separate.
Method
Engine path
Responsibility
GET/POST/PUT
/access/accounts and /{accountId}
Managed account lifecycle and explicit account grants
GET/POST/PUT/DELETE
/access/applications and /{applicationId}
Application identity and registered permissions
POST/DELETE
/access/applications/{applicationId}/credentials and /{credentialId}
One-time credential issuance and revocation
GET/POST/PUT/DELETE
/access/applications/{applicationId}/webhooks and /{webhookId}
Validated destinations, event selection, and delivery status
Rotate the protected signing secret; display it once
SignalR
/application-events
Bounded subscription/replay with current principal and resource authorization
GET
/api/v1/playback/sessions
Current scoped playback sessions
GET
/api/v1/playback/history
Durable scoped playback history
GET
/api/v1/analytics/playback
Scoped playback totals
GET
/api/v1/analytics/users, /libraries, /devices
Aggregation only after scope filtering
Telemetry read operations have separate registered permissions; cross-session control remains unavailable. Delivery mode and transformed media properties remain unknown without trustworthy observed stream facts. Webhook retries use the same signed envelope and stable delivery identity; receivers must handle repeated deliveries idempotently. A replay gap requires a fresh authorized snapshot before resuming the stream.