Files
tuliprox/docs/src/rest-api-cookbook.md
T
DarkBreakpoint 67d75d7ab4 docs(dvr): correct the operator reference and repoint the doctor
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.
2026-09-01 09:23:52 -05:00

17 KiB

REST API Cookbook

This page contains copy-paste examples for the Tuliprox Web UI REST API.

The examples use:

  • curl for HTTP requests
  • jq for token extraction and pretty-printing JSON

Requirements

  • Tuliprox is running in server mode
  • the Web UI REST API is enabled
  • curl and jq are 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, and 30d quality 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.read access

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.manage access

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.write permission
  • 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_link resolution later, depending on your stalker_pre_resolve_playback / stalker_runtime_resolve_playback settings.
  • If a Stalker item has not been materialized yet, Tuliprox no longer exposes the raw portal cmd as 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_playback is enabled. Only portal-specific extra header/cookie requirements remain a possible follow-up.
  • Runtime refresh only produces http/https playback URLs; Stalker rtmp:// / 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:

  • target is the numeric target id (same id the playlist explorer uses).
  • filter supports the full filter DSL including !TEMPLATE! references from your configured templates.
  • Optional limit caps the sample lists (default 25, max 50); optional match_as_ascii mirrors the target option.
  • An invalid filter expression returns HTTP 422 with {"error": "...", "line": n, "column": n}; line/column are null for 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.read
  • system.write
  • playlist.read
  • playlist.write
  • config.read
  • config.write
  • source.read
  • source.write
  • user.read
  • user.write
  • library.read
  • library.write
  • recording.read
  • recording.create
  • recording.manage
  • recording.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.