# HTTP API The MVP exposes a JSON API under `/api`. It is currently intended for private, authenticated network environments and has no built-in user authentication. Do not expose it directly to the internet. ## System | Method | Path | Purpose | | --- | --- | --- | | `GET` | `/api/health` | Container health and version | | `GET` | `/api/dashboard` | Counts and recent workspace state | | `GET` | `/api/settings` | Core settings and media root | | `PUT` | `/api/settings` | Update supported core settings | ## Plugins | Method | Path | Purpose | | --- | --- | --- | | `GET` | `/api/plugins` | Available plugins and administrator state | | `POST` | `/api/plugins/:id/install` | Validate settings, install, and activate a discovered plugin atomically | | `DELETE` | `/api/plugins/:id` | Uninstall and deactivate a plugin while retaining its settings | | `POST` | `/api/plugins/:id/enable` | Legacy route: enabling installs; disabling uninstalls | | `PUT` | `/api/plugins/:id/config` | Save manifest-declared settings | | `POST` | `/api/plugins/:id/test` | Test the configured connection | | `POST` | `/api/plugins/reload` | Rescan plugin directories | Password fields are returned as `••••••••`. Sending that sentinel back preserves the stored value. ## Discovery and library | Method | Path | Purpose | | --- | --- | --- | | `GET` | `/api/discover?q=name` | Search all enabled identity plugins, group identities, and report each provider status | | `POST` | `/api/performers/import` | Import one candidate or a grouped set of provider matches | | `POST` | `/api/performers` | Create a manual performer and its media directory | | `GET` | `/api/performers` | List performers | | `GET` | `/api/performers/:id` | Performer, sources, and items | | `PATCH` | `/api/performers/:id` | Edit name, aliases, or image and rename its media directory | | `POST` | `/api/performers/:id/refresh` | Refresh identity, image, and URLs from connected plugins | | `POST` | `/api/performers/:id/discover-sources` | Run enabled source-discovery plugins | | `POST` | `/api/performers/:id/sources` | Add a URL and optionally select a compatible scraper plugin | | `DELETE` | `/api/performers/:id` | Delete metadata only or metadata plus associated media files | Grouped import body: ```json { "matches": [ { "pluginId": "org.easyx.boobpedia", "candidate": { "externalId": "Example Performer", "name": "Example Performer", "aliases": ["Example"] } }, { "pluginId": "org.easyx.wikidata", "candidate": { "externalId": "Q123", "name": "Example Performer" } } ] } ``` The former single-match `{ "pluginId", "candidate" }` body remains supported. Discovery provider errors are returned in the `providers` array and do not make the whole search fail. ## Sources and downloads | Method | Path | Purpose | | --- | --- | --- | | `PATCH` | `/api/sources/:id` | Edit a URL, select its scraper, opt into scraping, automate, or reschedule it | | `DELETE` | `/api/sources/:id` | Remove a source URL and its tracked items | | `POST` | `/api/sources/:id/sync` | List current media through its selected scraper plugin | | `GET` | `/api/items` | List up to 500 recent media items | | `POST` | `/api/items/:id/queue` | Queue an available or failed item | `pluginId` records the provider that discovered a URL. It is provenance and is never replaced by scraper assignment. New URLs always have no `scraperPluginId` and remain `Reference only`; installing a plugin never assigns it to existing sources. `scraperPluginId` is selected manually for one URL and must name an installed plugin that declares `media-listing` and whose `sourceUrlPatterns` match the URL. `scrapeEnabled` explicitly includes or excludes only that URL from scheduled scraping. Clearing `scraperPluginId` also disables scraping. `syncIntervalSeconds` is the per-URL schedule. The selected plugin can publish a recommended default and a safe minimum. Values down to five seconds are supported for live-aware plugins. The legacy `syncIntervalMinutes` patch field remains accepted and is converted to seconds. Example source update: ```json { "scraperPluginId": "com.example.feed", "scrapeEnabled": true, "autoDownload": false, "syncIntervalSeconds": 1800 } ``` Successful API responses are JSON. Errors use: ```json { "error": "Operator-friendly message" } ```