The documentation described a system that no longer exists, and in two places described the opposite of what the code now does. - The layout section documented `<recording-root>/users/<owner-id>/<rel>` for private recordings and `shared/<rel>` for shared ones. That resolver was deleted: recordings are stored owner-independently at `<recording-root>/<rel>`, because one physical file is shared by every user who asked for it. The organised layouts and the component sanitisation rules are now documented as they are implemented. - The config reference claimed persisted queue recovery "is tolerant of corruption" and "starts with an empty transfer queue instead of aborting server boot". The queue now fails closed: a damaged database is rebuilt from the recovery history, and a database ahead of every surviving history refuses to start. An operator following the old text would have expected silent recovery from a condition that is deliberately fatal. - `download.read` / `download.write` were still listed as grantable permissions after their removal, and `recording.write` after its split. bin/dvr_doctor.sh looked for `downloads_state.json` and summarised it with jq. That file never existed under this name, and the queue it stood for is now a B+Tree, so the section printed "(absent)" and skipped its summary exactly when an operator needed it. It now reports the repository and its recovery generations: the CURRENT pointer, the retained generation pair, journal sizes, the fail-closed case where a database has no history, and a warning when the recovery directory shares a filesystem with the database — which survives a corrupt file but not the loss of the volume it exists to protect against. CHANGELOG records the three breaking changes: the non-migrating queue, the permission split, and the moved recording files.
17 KiB
REST API Cookbook
This page contains copy-paste examples for the Tuliprox Web UI REST API.
The examples use:
curlfor HTTP requestsjqfor token extraction and pretty-printing JSON
Requirements
- Tuliprox is running in server mode
- the Web UI REST API is enabled
curlandjqare installed- you have a valid Web UI username and password
Base URLs
By default, the login endpoint is:
http://localhost:8901/auth/token
If you configured a custom Web UI path such as web, the login endpoint becomes:
http://localhost:8901/web/auth/token
The protected REST API lives below:
http://localhost:8901/api/v1
or, with a Web UI path:
http://localhost:8901/web/api/v1
Example 1: Get a JWT token
#!/bin/bash
BASE_URL="http://localhost:8901"
USERNAME="admin"
PASSWORD="12345678"
TOKEN=$(curl -s -X POST "$BASE_URL/auth/token" \
-H 'accept: application/json' \
-H 'content-type: application/json' \
--data-raw "{\"username\":\"$USERNAME\",\"password\":\"$PASSWORD\"}" | jq -r '.token')
if [ "$TOKEN" = "null" ] || [ -z "$TOKEN" ]; then
echo "Error: no token available"
exit 1
fi
echo "$TOKEN"
If your local setup uses HTTPS with a self-signed certificate, add --insecure to the curl commands.
Example 2: Query server status
#!/bin/bash
BASE_URL="http://localhost:8901"
USERNAME="admin"
PASSWORD="12345678"
TOKEN=$(curl -s -X POST "$BASE_URL/auth/token" \
-H 'accept: application/json' \
-H 'content-type: application/json' \
--data-raw "{\"username\":\"$USERNAME\",\"password\":\"$PASSWORD\"}" | jq -r '.token')
curl -s -X GET "$BASE_URL/api/v1/status" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" | jq .
Typical use:
- verify that login works
- use as a simple health and diagnostics check
- inspect active users, active provider connections, cache state, and current server time
From this point onward, the examples use:
TOKEN="PUT_YOUR_TOKEN_HERE"
This is only to keep the examples shorter and easier to copy-paste. Example 1 above already shows how to obtain the JWT token, so it is not repeated in every script block below.
Example 3: List active streams
#!/bin/bash
BASE_URL="http://localhost:8901"
TOKEN="PUT_YOUR_TOKEN_HERE"
curl -s -X GET "$BASE_URL/api/v1/streams" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" | jq .
Typical use:
- inspect currently active streams
- see who is connected
- correlate user activity with provider slot pressure
Example 4: Query stream history summary
#!/bin/bash
BASE_URL="http://localhost:8901"
TOKEN="PUT_YOUR_TOKEN_HERE"
curl -s -X GET "$BASE_URL/api/v1/stream-history/summary?from=2026-04-01&to=2026-04-03" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" | jq .
Typical use:
- get an aggregated view over recent stream activity
- inspect disconnect patterns and provider churn
- verify that stream history collection is working
Example 5: Query QoS snapshots
#!/bin/bash
BASE_URL="http://localhost:8901"
TOKEN="PUT_YOUR_TOKEN_HERE"
curl -s -X GET "$BASE_URL/api/v1/qos-snapshots" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" | jq .
Typical use:
- inspect per-stream reliability snapshots
- compare
24h,7d, and30dquality windows - prepare later failover and ranking analysis
Example 6: Inspect a download target before queueing
#!/bin/bash
BASE_URL="http://localhost:8901"
TOKEN="PUT_YOUR_TOKEN_HERE"
URL_TO_DOWNLOAD="https://example.invalid/file.mp4"
curl -s -G "$BASE_URL/api/v1/file/download/info" \
--data-urlencode "url=$URL_TO_DOWNLOAD" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" | jq .
Typical use:
- inspect filename and metadata before queueing
- verify that the remote file is reachable
- test
recording.readaccess
Example 7: Queue a file download
#!/bin/bash
BASE_URL="http://localhost:8901"
TOKEN="PUT_YOUR_TOKEN_HERE"
curl -s -X POST "$BASE_URL/api/v1/file/download" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data-raw '{
"url": "https://example.invalid/file.mp4",
"filename": "example.mp4",
"input_name": null,
"priority": null
}' | jq .
Typical use:
- queue a normal background download
- confirm that duplicate queue requests return the existing task instead of creating a second one
- test
recording.manageaccess
Example 8: Trigger a playlist update
#!/bin/bash
BASE_URL="http://localhost:8901"
TOKEN="PUT_YOUR_TOKEN_HERE"
curl -s -X POST "$BASE_URL/api/v1/playlist/update" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data-raw '["main"]'
Typical use:
- trigger a manual processing run for one or more targets
- verify
playlist.writepermission - integrate Tuliprox into external automation
Example 9: Preview a Stalker live input in the Web UI playlist API
#!/bin/bash
BASE_URL="http://localhost:8901"
TOKEN="PUT_YOUR_TOKEN_HERE"
curl -s -X POST "$BASE_URL/api/v1/playlist/live" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data-raw '{"Input":"stalker"}' | jq .
Typical use:
- verify that a configured Stalker input handshakes and returns playlist preview rows
- confirm that the Web UI playlist explorer can inspect Stalker live content
- distinguish preview/catalog problems from later playback-resolution problems
Notes:
- Replace
"stalker"with the configured input name. - The preview endpoint returns catalog items. Actual playback can still require Stalker
create_linkresolution later, depending on yourstalker_pre_resolve_playback/stalker_runtime_resolve_playbacksettings. - If a Stalker item has not been materialized yet, Tuliprox no longer exposes the raw portal
cmdas the playlist URL. Runtime playback resolves a real media URL later through the reverse-proxy path. - Expired temp links are refreshed automatically when
stalker_runtime_resolve_playbackis enabled. Only portal-specific extra header/cookie requirements remain a possible follow-up. - Runtime refresh only produces
http/httpsplayback URLs; Stalkerrtmp:///rtsp://commands are rejected explicitly rather than proxied half-supported.
Example 10: Dry-run a filter expression against a target
#!/bin/bash
BASE_URL="http://localhost:8901"
TOKEN="PUT_YOUR_TOKEN_HERE"
curl -s -X POST "$BASE_URL/api/v1/playlist/filter/preview" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data-raw '{"target": 1, "filter": "Group ~ \"^DE.*\" AND NOT Title CONTAINS \"Shopping\"", "limit": 10}' | jq .
Typical use:
- test a filter DSL expression against a target's stored playlist before writing it into
source.yml - see matched/total counts overall and per cluster (live/vod/series)
- inspect sample matched and excluded channels to verify the expression does what you expect
Notes:
targetis the numeric target id (same id the playlist explorer uses).filtersupports the full filter DSL including!TEMPLATE!references from your configured templates.- Optional
limitcaps the sample lists (default 25, max 50); optionalmatch_as_asciimirrors the target option. - An invalid filter expression returns HTTP 422 with
{"error": "...", "line": n, "column": n};line/columnarenullfor semantic errors without a source position (e.g. an invalid regex value). - The preview reads the target's already-processed playlist; it never contacts providers or triggers an update.
Available /api/v1 Endpoints
This is a compact operator-oriented overview of the /api/v1 REST API groups currently registered by the backend.
System and diagnostics
| Method | Path | Purpose |
|---|---|---|
GET |
/healthcheck |
Liveness probe — 200 while the process is running. Used by Docker via the --healthcheck CLI flag. |
GET |
/ready |
Readiness probe — 200 when at least one input group has spare connection capacity; 503 when every input group is exhausted. |
GET |
/api/v1/status |
Server status, version, active users, provider connections, cache state |
GET |
/api/v1/streams |
Current active streams |
GET |
/api/v1/ipinfo |
External IPv4/IPv6 check if configured |
GET |
/api/v1/stream-history |
Query raw persisted stream history |
GET |
/api/v1/stream-history/summary |
Aggregated stream history summary |
GET |
/api/v1/qos-snapshots |
List QoS snapshots |
GET |
/api/v1/qos-snapshots/{stream_identity_key} |
QoS detail for one stream |
GET |
/api/v1/geoip/update |
Trigger GeoIP DB update |
Downloads and recordings
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/file/download/info |
Inspect remote file/download info |
POST |
/api/v1/file/download |
Queue a file download |
POST |
/api/v1/file/record |
Queue a live recording |
POST |
/api/v1/file/download/pause |
Pause a queued or active download |
POST |
/api/v1/file/download/resume |
Resume a paused download |
POST |
/api/v1/file/download/cancel |
Cancel a queued or active download |
POST |
/api/v1/file/download/remove |
Remove a task from the download database |
POST |
/api/v1/file/download/retry |
Retry a failed download |
GET |
/api/v1/recording/tasks |
List visible DVR tasks |
POST |
/api/v1/recording/tasks |
Create a DVR recording task from server-owned source ids |
PATCH |
/api/v1/recording/tasks/{id} |
Edit an upcoming DVR recording |
POST |
/api/v1/recording/tasks/{id}/cancel |
Cancel an active, queued, or scheduled DVR recording |
DELETE |
/api/v1/recording/tasks/{id} |
Delete a finished DVR recording through the safe deletion lifecycle |
POST |
/api/v1/recording/conflicts/preview |
Advisory conflict preview (severity, optional provider scope, overlap segments) |
GET |
/api/v1/recording/quota |
Read the caller's private quota and shared DVR usage |
GET |
/api/v1/recording/rules |
List visible recurring recording rules |
POST |
/api/v1/recording/rules |
Create a weekly recurring recording rule |
PATCH |
/api/v1/recording/rules/{id} |
Edit a recurring recording rule |
DELETE |
/api/v1/recording/rules/{id}?future=retain|cancel |
Delete a recurring recording rule |
DVR WebSocket protocol
The DVR layer ships its own scoped protocol messages on the same WebSocket connection as the rest of the
backend. The frontend sends ProtocolMessage::RecordingSnapshotRequest to subscribe and receives:
ProtocolMessage::RecordingSnapshotResponse { revision, tasks }— the full filtered task list for the caller's session, sent on connect and after every mutation the session is permitted to see.ProtocolMessage::RecordingDeltaResponse { revision, tasks }— a smaller diff when only a few tasks changed.
The frontend never polls. After a successful POST /api/v1/recording/tasks it relies on the next
RecordingSnapshotResponse to update its view. Rule lists are refreshed by hooking into
EventMessage::RecordingSnapshot and re-calling GET /api/v1/recording/rules; a dedicated
RecordingRulesChanged event is a planned follow-up but not currently shipped.
DVR conflict preview
POST /api/v1/recording/conflicts/preview is advisory only — the response carries a severity bucket
(none / soft / hard) plus optional provider scope and overlap segments, but the server does not
reject the create call based on it. The frontend renders the preview as a hint next to the recording
form's scheduled interval, never as a hard block.
POST /api/v1/file/record (deprecated)
The legacy POST /api/v1/file/record endpoint is admin-gated and scheduled for removal in the next
major version. It returns recording_forbidden for non-admin principals. New code should call
POST /api/v1/recording/tasks with a CreateRecordingTaskBody payload.
Playlist and web-player helpers
| Method | Path | Purpose |
|---|---|---|
POST |
/api/v1/playlist/live |
Query live playlist content for the Web UI, including Stalker inputs |
POST |
/api/v1/playlist/vod |
Query VOD playlist content |
POST |
/api/v1/playlist/series |
Query series playlist content |
POST |
/api/v1/playlist/resolve_url |
Resolve provider-backed stream URLs |
POST |
/api/v1/playlist/update |
Trigger target updates |
POST |
/api/v1/playlist/epg |
Query EPG data for the Web UI |
POST |
/api/v1/playlist/series_info/{virtual_id}/{provider_id} |
Series metadata lookup |
POST |
/api/v1/playlist/series/episode/{virtual_id} |
Episode item lookup |
POST |
/api/v1/playlist/filter/preview |
Dry-run a filter DSL expression against a target's stored playlist |
GET |
/api/v1/playlist/resource/{resource} |
Public resource access for playlist-related assets |
Configuration
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/config |
Read effective configuration |
GET |
/api/v1/config/batchContent/{input_id} |
Inspect batch input content |
POST |
/api/v1/config/xtream/login-info |
Test or inspect Xtream login information |
POST |
/api/v1/config/main |
Save config.yml |
POST |
/api/v1/config/sources |
Save source.yml |
GET |
/api/v1/config/apiproxy |
Read api-proxy.yml |
PUT |
/api/v1/config/apiproxy |
Save api-proxy.yml |
API proxy users
| Method | Path | Purpose |
|---|---|---|
POST |
/api/v1/user/{target} |
Create a target user |
PUT |
/api/v1/user/{target} |
Update or move a target user |
DELETE |
/api/v1/user/{target}/{username} |
Delete a target user |
Library
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/library/status |
Local library status |
POST |
/api/v1/library/scan |
Trigger a library scan |
GET |
/api/v1/library/thumbnail/{uuid} |
Read a generated thumbnail |
RBAC management
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/rbac/users |
List Web UI users |
POST |
/api/v1/rbac/users |
Create a Web UI user |
PUT |
/api/v1/rbac/users/{username} |
Update a Web UI user |
DELETE |
/api/v1/rbac/users/{username} |
Delete a Web UI user |
GET |
/api/v1/rbac/groups |
List permission groups |
POST |
/api/v1/rbac/groups |
Create a permission group |
PUT |
/api/v1/rbac/groups/{name} |
Update a permission group |
DELETE |
/api/v1/rbac/groups/{name} |
Delete a permission group |
GET |
/api/v1/rbac/permissions |
List available permissions |
Permissions
With Web UI authentication enabled, many endpoints require matching permissions such as:
system.readsystem.writeplaylist.readplaylist.writeconfig.readconfig.writesource.readsource.writeuser.readuser.writelibrary.readlibrary.writerecording.readrecording.createrecording.managerecording.delete
If a request is rejected, verify the logged-in Web UI user's RBAC group assignments first.
See also: the full DVR Operator Reference for the authorization matrix, identity-registry bootstrap, token refresh, recurring-rule matching + DST, cross-store reconciliation, at-most-once notification protocol, and the migration checklist.