The API is a local Express server. It accepts strict JSON bodies, rejects unknown request keys, returns JSON errors, supports server-sent job events, and streams media outputs with HTTP byte-range support.
The default base URL is:
http://localhost:4000
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Returns API health. |
GET |
/ready |
Returns redacted operational readiness for startup checks. |
GET |
/api/capabilities |
Reports FFmpeg, whisper.cpp, and yt-dlp capability status. |
/health is intentionally lightweight liveness. /ready reports required startup checks such as managed storage, manifest load state, FFmpeg/FFprobe availability, core encoding capabilities, and storage pressure without exposing local paths, executable paths, filenames, raw commands, or manifest content. Required failures return 503 with state: "not_ready". Warnings such as low storage return 200 with state: "degraded". Optional tools such as yt-dlp and whisper.cpp are reported separately and are not required for core readiness.
| Method | Path | Purpose |
|---|---|---|
POST |
/api/videos |
Upload and admit a source video through multipart form data. |
POST |
/api/videos/url |
Import a validated YouTube URL through configured yt-dlp. |
GET |
/api/videos/:id/source |
Stream the source video inline. |
GET |
/api/videos/:id/download |
Download the original source video. |
PATCH |
/api/videos/:id |
Rename the source display filename. Body: { "originalName": "name.mp4" }. |
DELETE |
/api/videos/:id |
Delete a source video and its associated managed files. |
| Method | Path | Purpose |
|---|---|---|
POST |
/api/videos/:id/jobs |
Create an optimization job from optional encoding settings. |
POST |
/api/videos/:id/pair |
Create the default website pair: compatible MP4 and modern WebM. |
POST |
/api/videos/:id/sample |
Create a short sample encode. Body can include sampleSeconds plus optimization settings. |
POST |
/api/videos/:id/poster |
Create a poster image. Body: { "atSeconds": 3 }. |
GET |
/api/jobs/:id |
Get a job by ID. |
GET |
/api/jobs/:id/events |
Subscribe to job updates with server-sent events. |
PATCH |
/api/jobs/:id |
Rename a job output. Body: { "outputFileName": "name.mp4" }. |
POST |
/api/jobs/:id/cancel |
Cancel a queued or running job. |
DELETE |
/api/jobs/:id |
Delete a job and its managed artifacts. |
POST |
/api/jobs/:id/reveal |
Ask the desktop environment to reveal the output file. |
| Method | Path | Purpose |
|---|---|---|
GET |
/api/jobs/:id/output |
Stream a completed output inline for preview. |
GET |
/api/jobs/:id/download |
Download a completed output. |
GET |
/api/jobs/:id/sidecar |
Download a sidecar output such as .srt. |
Preview endpoints support valid Range headers, including suffix ranges used by browser video playback.
| Method | Path | Purpose |
|---|---|---|
POST |
/api/videos/:id/subtitles |
Create a caption-generation job when whisper.cpp is configured. |
GET |
/api/jobs/:id/captions |
Read generated caption text. |
PUT |
/api/jobs/:id/captions |
Replace generated VTT captions. Body: { "vtt": "WEBVTT..." }. |
POST |
/api/jobs/:id/mux-subtitles |
Create a remux job that embeds captions into a completed video output. |
| Method | Path | Purpose |
|---|---|---|
POST |
/api/videos/:id/package |
Create a website ZIP package. Body can include selected jobIds and package metadata. |
Package metadata supports optional title, description, language, and filenamePrefix.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/history |
Return the persisted local library snapshot. |
POST |
/api/history/delete |
Bulk-delete selected videoIds and jobIds. |
GET |
/api/storage |
Return managed storage status. |
POST |
/api/storage/cleanup |
Remove stale temporary managed files. |
Validation failures return a structured error:
{
"error": "Request validation failed.",
"code": "VALIDATION_ERROR",
"details": [{ "path": "crf", "message": "Number must be less than or equal to 63" }]
}The API may also return 404 for missing videos/jobs/outputs, 413 for upload/body limits, and 507 when storage policy rejects work because capacity is too low.
The API has no authentication. Keep it bound to loopback unless you explicitly need trusted LAN access. Do not expose it directly to the public internet.