Alchemist docs
Alchemist documentation

Find a page.

Type at least two characters.

    Docs menu

    Reference

    API Reference

    REST and SSE API reference for Alchemist.

    Source revision aeebc48

    Authentication

    Canonical client routes live under /api/v1. The older /api routes remain available as compatibility aliases for the web UI and existing scripts, but new external clients should use /api/v1.

    All protected API routes require the alchemist_session auth cookie established via POST /api/v1/auth/login, or an Authorization: Bearer <token> header.

    Machine-readable contract: OpenAPI spec

    Maintainers should run just api-contract after changing API routes. The release checklist runs the same contract check.

    The stdio Model Context Protocol server is documented separately in MCP Server. It is read-only and does not expose HTTP mutation routes.

    API errors use application/problem+json, include an X-Request-Id response header, and retain the legacy error.code / error.message object for older client parsers:

    {
      "type": "urn:alchemist:problem:auth-required",
      "title": "Unauthorized",
      "status": 401,
      "detail": "Unauthorized",
      "instance": "/api/v1/engine/status",
      "code": "AUTH_REQUIRED",
      "request_id": "2c168dbf-6f29-41eb-a8e7-0fd563f76fd4",
      "error": {
        "code": "AUTH_REQUIRED",
        "message": "Unauthorized"
      }
    }

    Mutation routes that do not return a resource return a typed JSON body:

    { "ok": true }

    POST /api/v1/auth/login

    Establish a session. Returns a Set-Cookie header.

    Request:

    {
      "username": "admin",
      "password": "..."
    }

    POST /api/v1/auth/logout

    Invalidate current session and clear cookie.

    GET /api/v1/settings/api-tokens

    List metadata for configured API tokens.

    POST /api/v1/settings/api-tokens

    Create a new API token. The plaintext value is only returned once.

    Request:

    {
      "name": "Prometheus",
      "access_level": "read_only"
    }

    access_level supports:

    • read_only — observability GET/HEAD endpoints only
    • arr_webhook — only POST /api/v1/webhooks/arr
    • jellyfin — Jellyfin plugin endpoints: system info, readiness, SSE events, job details, and POST /api/v1/jobs/enqueue
    • full_access — all authenticated API routes

    DELETE /api/v1/settings/api-tokens/:id

    Revoke a token.


    Settings

    GET /api/v1/settings/bundle

    Fetch the full settings projection used by setup and the settings UI.

    PUT /api/v1/settings/bundle

    Persist the full settings bundle. Fails with 409 when ALCHEMIST_CONFIG_MUTABLE=false.

    POST /api/v1/settings/config/validate

    Parse and validate candidate TOML without saving it. Returns a redacted summary and conservative warnings for high-risk settings.

    GET|POST /api/v1/settings/system

    Read or update runtime-facing system settings: conversion upload limit, converted-download retention, telemetry switch, engine mode, metrics switch, update channel/check settings, and ARR path translations.

    GET|POST /api/v1/settings/hardware

    Read or update hardware preference, device path, CPU fallback, CPU encoding, and CPU preset. Updating hardware settings refreshes runtime hardware state and cache.

    GET|PUT|POST /api/v1/settings/notifications

    Read notification schedule/quiet-hours state, update global notification timing, or add a target.

    Global fields:

    • daily_summary_time_local
    • quiet_hours_enabled
    • quiet_hours_start_local
    • quiet_hours_end_local

    Targets use target_type, provider-specific config_json, events, and enabled.

    POST /api/v1/settings/notifications/test

    Send a test notification using a target payload without saving it.


    ARR webhook ingress

    POST /api/v1/webhooks/arr

    Accepts Sonarr/Radarr webhook payloads with eventType=Download, resolves a media path, applies optional system.arr_path_translations, then reuses the standard enqueue-by-path pipeline (same dedupe/output guards as manual enqueue).

    Webhook setup:

    1. Create an API token with access_level: "arr_webhook".
    2. In Sonarr/Radarr add a Webhook notification pointing to: http://<alchemist-host>:3000/api/v1/webhooks/arr
    3. Add header: Authorization: Bearer <token>.
    4. Ensure the payload includes import path fields (episodeFile, movieFile, importedEpisodeFiles, or importedMovieFiles).

    Container path mapping (if Arr sees different mount paths than Alchemist):

    [system]
    arr_path_translations = [
      { from = "/container/media", to = "/mnt/media" }
    ]

    Jellyfin plugin integration

    The Jellyfin plugin uses a dedicated jellyfin API token. That token can enqueue media, keep an SSE connection open, and fetch completed job details without granting full settings or engine-control access.

    Allowed routes:

    • GET /api/v1/system/info
    • GET /api/v1/ready
    • GET /api/v1/events
    • GET /api/v1/jobs/:id/details
    • POST /api/v1/jobs/enqueue

    Plugin setup:

    1. Create an API token with access_level: "jellyfin".
    2. Configure the plugin with the Alchemist URL and token.
    3. Add forward path translations for Jellyfin-to-Alchemist enqueue paths.
    4. Add reverse path translations for Alchemist-to-Jellyfin refresh paths, or leave them empty to invert the forward translations.
    5. Enable the event listener and refresh-on-completion options when ready.

    Conversion

    The Convert workflow is an experimental single-file utility. It reuses the normal analyzer/planner/executor path, but tracks staged uploads in conversion_jobs.

    POST /api/v1/conversion/uploads

    Upload one source file. The maximum size is system.conversion_upload_limit_gb GiB.

    POST /api/v1/conversion/preview

    Return normalized settings, generated FFmpeg command text, and a structured source/output/estimate summary.

    POST /api/v1/conversion/jobs/:id/start

    Queue the uploaded conversion job.

    GET /api/v1/conversion/jobs/:id

    Fetch current conversion job state.

    GET /api/v1/conversion/jobs/:id/download

    Download completed output. After download, cleanup retention is governed by system.conversion_download_retention_hours.

    DELETE /api/v1/conversion/jobs/:id

    Delete the conversion record and any staged artifacts that are safe to remove.


    Jobs

    GET /api/v1/jobs

    List jobs with filtering and pagination.

    Params: limit, page, status, search, sort_by, sort_desc, archived. The search value matches job paths plus stored decision and failure explanation text.

    GET /api/v1/jobs/:id/details

    Fetch full job state, metadata, logs, and stats.

    DELETE /api/v1/jobs/:id

    Delete a terminal job. The legacy alias remains POST /api/jobs/:id/delete.

    POST /api/v1/jobs/:id/cancel

    Cancel a queued or active job.

    POST /api/v1/jobs/:id/restart

    Restart a terminal job (failed/cancelled/completed).

    POST /api/v1/jobs/:id/priority

    Update job priority.

    Request: {"priority": 100}

    POST /api/v1/jobs/batch

    Bulk action on multiple jobs.

    Request:

    {
      "action": "restart|cancel|delete",
      "ids": [1, 2, 3]
    }

    POST /api/v1/jobs/restart-failed

    Restart all failed or cancelled jobs.

    POST /api/v1/jobs/clear-completed

    Archive all completed jobs from the active queue.

    POST /api/v1/jobs/clear-history

    Archive terminal completed, failed, cancelled, and skipped jobs from the active table.


    Engine

    GET /api/v1/engine/status

    Get current operational status and limits.

    Response fields include:

    • status: running, paused, or draining
    • manual_paused
    • scheduler_paused
    • draining
    • blocked_reason: manual_paused, scheduled_pause, draining, workers_busy, or null

    POST /api/v1/engine/pause

    Pause the engine. Active jobs continue; no new jobs are claimed.

    POST /api/v1/engine/resume

    Resume the engine.

    POST /api/v1/engine/drain

    Enter drain mode (finish active jobs, don’t start new ones).

    POST /api/v1/engine/stop-drain

    Cancel drain mode without changing scheduler pause state.

    POST /api/v1/engine/restart

    Pause briefly, cancel in-flight work, clear drain state, then resume claiming jobs.

    POST /api/v1/engine/mode

    Switch engine mode or apply manual overrides.

    Request:

    {
      "mode": "background|balanced|throughput",
      "concurrent_jobs_override": 2,
      "threads_override": 0
    }

    Statistics

    GET /api/v1/stats/aggregated

    Total savings, job counts, and global efficiency.

    GET /api/v1/stats/queue-eta

    Queue-wide ETA estimate from recent completed encode samples.

    Returns:

    {
      "remaining_jobs": 3,
      "est_seconds_remaining": 3600,
      "sample_size": 4
    }

    GET /api/v1/stats/daily

    Encode activity history for the last 30 days.

    GET /api/v1/stats/savings

    Detailed breakdown of storage savings.


    System

    GET /api/v1/system/hardware

    Detected hardware backend and codec support matrix. During startup this may be served from a valid hardware detection cache before a background refresh updates runtime state.

    GET /api/v1/system/hardware/probe-log

    Full logs from the startup hardware probe.

    GET /api/v1/system/resources

    Live telemetry: CPU, Memory, GPU utilization, and uptime.

    GET /api/v1/system/update

    Return update metadata for the configured channel (stable, rc, or nightly), including install type, verification status, self-update eligibility, and package-manager guidance.

    POST /api/v1/system/update/check

    Force-refresh update metadata from GitHub Releases.

    POST /api/v1/system/update/install

    Start a verified update for eligible direct Linux/macOS binary installs. Active jobs trigger drain mode first. Docker, Homebrew, AUR, Windows, source, and unknown installs return guided update instructions instead of replacing files.

    POST /api/v1/system/backup

    Download a gzip-compressed SQLite backup produced through SQLite’s online backup path. Requires full access.

    POST /api/v1/system/backup/validate-restore

    Validate an uploaded .db.gz snapshot before restore. The endpoint decompresses the backup to a temporary file, verifies that it is an Alchemist SQLite database, and returns schema metadata without mutating the live database. Requires full access.

    GET /api/v1/ready

    Readiness check. Returns 503 with structured error code DATABASE_UNAVAILABLE if SQLite is not usable.

    GET /metrics

    Prometheus metrics endpoint. Disabled unless system.metrics_enabled=true.


    Library

    GET /api/v1/library/intelligence

    Storage-focused recommendations: duplicates, remux opportunities, wasteful audio layouts, and commentary/descriptive-track cleanup candidates.

    GET /api/v1/library/health

    Current Library Doctor summary.

    POST /api/v1/library/health/scan

    Start a full health scan. Overlapping full-library scans are rejected.

    GET /api/v1/library/health/issues

    List current health issues.


    Events (SSE)

    GET /api/v1/events

    Real-time event stream.

    Emitted Events:

    • status: Job state changes.
    • progress: Real-time encode statistics.
    • decision: Skip/Transcode logic results.
    • log: Engine and job logs.
    • config_updated: Configuration hot-reload notification.
    • scan_started / scan_completed: Library scan status.

    Authoritative source

    This page is generated from the editable Markdown in the Alchemist repository.

    View docs/content/api.md at aeebc48

    Documentation © Brooklyn Halmstad. Licensed under CC BY-SA 4.0.

    A Dead Signal Works project.