Request and response bodies are JSON. Endpoints that take a body expect Content-Type: application/json. The base URL throughout this page is http://headend.local:8080; substitute the address and the port you configured with --port.
Channel identifiers in paths are the numeric id from the configuration file. They are reused after a delete, so a script that caches an identifier across a reconfiguration can act on the wrong channel.
Version, commit and build time of the running binary. Deliberately unauthenticated, so a monitoring system can confirm what is deployed on every appliance in an estate without being given credentials to change anything on them.
Restart one channel, for a supervisor running on the appliance itself. The optional ?reason= is written to the log, so a restart there says why it happened. It returns the channel's name and whether a running engine was restarted. The supplied watchdog uses it to recover one stuck channel without restarting the whole container and taking every healthy channel off air with it.
This endpoint needs no login. Instead, it refuses any connection that does not come from a loopback address (127.0.0.1 or ::1), with 403 and a log line naming the caller. That keeps the admin password out of a file on disk for the watchdog to read. Reaching the endpoint already requires a shell on the appliance.
Create a channel. The body is the same shape as one entry under streams in the configuration file. The assigned identifier is returned.
PUT
/api/channels/{id}
Replace the settings of an existing channel. A running channel is restarted if the change affects how it runs.
DELETE
/api/channels/{id}
Remove a channel. Stops it first. The identifier becomes available for reuse.
POST
/api/channels/{id}/restart
Restart one channel without touching the others. The first thing to try when a single channel is misbehaving and the rest are healthy.
State
POST
/api/streams/{id}/active
Enable or disable a channel. A disabled channel keeps its configuration but is never started.
POST
/api/streams/{id}/force-always-on
Toggle the always-on override. On, the channel runs continuously; off, it runs when viewer detection sees somebody watching.
Inspection
GET
/api/channels
A short list of every channel: id, name, destination address and port, and epg_id. Enough to discover which channels exist. It does not return a channel's full settings — source, engine, profile, supervision — so a script that needs those reads the configuration file.
GET
/api/channels/{id}/ts-quality
Transport-stream quality for one channel: the latest sample, running totals of continuity, transport and PCR errors, and the last 30 minutes of history at 5-second intervals. Also says whether monitoring is running for the channel and how its PCR timing is measured. See Transport-stream quality.
GET
/api/channels/{id}/logs
Recent engine output for one channel. This is where a source error appears in the words the origin actually used, rather than as a generic failure.
GET
/api/channels/resolutions
The resolution currently selected for each channel, and whether it is an exact match for the requested profile or the closest rung the origin offered. Served from cache; it does not block on origin fetches.
Preview
POST
/api/channels/{id}/preview
Start a browser-viewable preview of a channel.
POST
/api/channels/{id}/preview/stop
Stop it. Previews consume real CPU, so stop them explicitly rather than relying on closing the page.
Add an XMLTV source. Body: {"name": "...", "url": "..."}. Returns the new source's id. The source's channel list is fetched in the background.
PUT
/api/epg/sources/{id}
Rename a source or point it at a new URL, with the same body. Stored data is kept and replaced on the next sync.
DELETE
/api/epg/sources/{id}
Remove a source and its stored guide data. Channels mapped to it keep their epg_id.
POST
/api/epg/sources/{id}/sync
Fetch the source now and store programmes for mapped channels. Synchronous: the response reports the real result, and on a large guide it can take minutes.
Mapping and data
GET
/api/epg/channels
Every source, keyed by its id, with its name, URL, last fetch and status, and the guide identifiers it offers.
POST
/api/epg/map
Map a channel to a guide identifier. Body: {"stream_id": 3, "tvg_id": "rtl.de"}. An empty tvg_id clears the mapping. Saved to the channel's epg_id.
GET
/api/epg/now
What is on now and what is next, for every mapped channel in one call. A mapped channel with nothing airing is present with now empty, which is different from an unmapped channel, which is absent.
The appliance's licence state: state with a readable message and detail, can_stream, the device_code support will ask for, and a support contact. If the last sync refused channels the licence server sent, the reasons are included too. The licence token itself is never returned.
POST
/api/licence/retry
Check in with the licence server now instead of waiting for the next scheduled attempt. Use it after fixing whatever blocked the licence, such as a lapsed subscription or a firewall rule. It returns at once; poll /api/licence for the outcome.
Viewer state for every channel in one call. Use this for polling rather than looping over channels, which multiplies the request count by the size of the line-up.
GET
/api/streams/{id}/viewers
Detail for one channel: the list of client addresses currently joined, the count, a flag for whether an IGMP querier is present on the segment, and a flag for whether detection is degraded.
System metrics and per-channel metrics in one document: CPU, memory, uptime, per channel state and throughput. Cached for about two seconds server-side, so polling faster than that returns the same numbers and only costs you request handling.
GET
/api/network-stats
Interface-level counters and reachability results for the hosts in ping_hosts.
GET
/api/udp-bitrate
Measured bitrate actually leaving the network port, per channel. This is the engine-agnostic liveness signal: it reflects what is on the wire rather than what a decoder claims it is producing.
Administration
PUT
/api/settings
Change global settings. Send only the keys to change; anything omitted keeps its current value. PUT only — there is no GET variant, so read current settings from the configuration file.
GET
/api/network-interfaces
Every IPv4 address on the appliance, for choosing udp_device: address, interface, whether the interface is up, its subnet, and whether the address was assigned by DHCP. Loopback and link-local addresses are left out, because neither is ever the right choice. Down interfaces are listed, not hidden, because a down interface is exactly what you need to see.
POST
/api/restart-all
Restart every channel. Starts are staggered by startup_stagger_seconds, so the whole line-up does not come back at once and fail together.
POST
/api/restart-app
Restart the Castmux application. It replies first, then shuts down cleanly, and the container's restart policy starts it again within a few seconds. Every channel is briefly off air. Unlike /api/reboot, it needs nothing on the host, so it works in any container deployment.
POST
/api/reboot
Reboot the appliance. Every channel is off air for the duration; this is not a remedy for a channel fault.
List the configured multiplexes with their RF parameters and current state. Unlike channels, multiplexes do have a read endpoint.
POST
/api/dvb/muxes
Create a multiplex.
PUT
/api/dvb/muxes/{id}
Update RF parameters or signalling identifiers. Changing frequency, modulation or symbol rate interrupts the carrier.
DELETE
/api/dvb/muxes/{id}
Remove a multiplex. The channels it carried are not deleted.
POST
/api/dvb/muxes/{id}/active
Start or stop transmission of the carrier.
Contents and operations
POST
/api/dvb/muxes/{id}/channels
Assign the channels carried, and their order. The order determines service identifiers, so reordering re-identifies services and can require televisions to re-tune. Append rather than insert where you have the choice.
GET
/api/dvb/muxes/{id}/logs
Multiplexer and modulator output for this carrier.
POST
/api/dvb/muxes/{id}/hot-swap
Exchange the contents of a running multiplex without dropping the carrier, so sets stay tuned rather than reporting no signal during the change.
POST
/api/dvb/muxes/{id}/suspend
Suspend the multiplex temporarily, for maintenance on the modulator or the cable network, without deleting the configuration.
Set which channel is presented on the HDMI port and how it is rendered.
GET
/api/hdmi/outputs
What this appliance can drive: vos (video drivers), drm_devices (display devices) and audio_devices (each a value and label, as the player itself lists them), plus the current selection. The values are exactly what /api/hdmi/config accepts.
A worked example: log in, create three channels, create a multiplex, and assign the channels to it in the order they should appear. This is the whole commissioning sequence for a small site, and it is worth scripting because typing three channels into a form is where transposed multicast addresses come from.
#!/usr/bin/env bash
set-euopipefail
BASE="http://headend.local:8080"
JAR="$(mktemp)"
# 1. log in ------------------------------------------------------------
Two details in that script are not incidental. The sleep 3 between creations matches startup_stagger_seconds: three HLS origins opened simultaneously is the exact condition the stagger exists to avoid, and a build script is the easiest way to recreate it. And the channels are assigned in one call rather than three, because assignment order sets the service identifiers — building the list first and posting it once means the line-up is numbered as intended on the first attempt.
Errors are JSON. A failed request returns an appropriate status code and a JSON body describing the problem. An HTML response means the request was not authenticated and was redirected to the login page — check the cookie jar before debugging the body.
PUT /api/settings merges. Send only the settings you want to change; anything omitted keeps its current value. A change to the engine parameters, udp_device, default_engine, enable_dummy_listeners, overwrite_srt_latency or overwrite_pkt_size restarts every active channel, because those settings shape how each engine runs. Other settings apply without a restart.
Rate limiting is not a substitute for cached data./api/metrics is cached for about two seconds; a dashboard polling it every 250 ms produces eight identical answers and no extra information.
A full read API, whole-configuration export and import, and API tokens in place of session cookies are all roadmap. They are listed with the rest on the roadmap page.