Control TCast from a network device

TCast runs a small control API on your Mac. Anything on your network that can send a web request can drive the show: a browser tab, a tablet, a show-control system, a lighting desk with an HTTP macro, a shell script, or Companion's built-in Generic HTTP connection.

Commands are plain HTTP GETs, so there's nothing to install and nothing to build. A WebSocket carries the state back if you want live feedback.

If you're driving it from a Stream Deck, use the Companion module instead. It talks to this same API, with clip dropdowns and button tally already wired. From an iPhone or iPad, use the TCast Remote app.

Remote control is a Pro feature. On the free tier the whole pane is disabled and shows a Pro lock. See Licensing.

Settings ▸ Remote Control, with Allow Wi-Fi connections, the port, and the addresses


1. Turn on remote control in TCast

  1. Open Settings (⌘,) and select the Remote Control pane.
  2. Under Wi-Fi, turn on Allow Wi-Fi connections.
  3. Note the Port (default 7341) and the address shown under Address. Use the Copy button next to it.
    • The This Mac address is for something running on the same machine.
    • Use the On your network address from any other device. Not localhost.

That address is the base URL for everything below. Open it in a browser to check you can reach it: TCast answers with a one-line banner.

http://192.168.1.42:7341/
TCast control API — see /api/state

2. Set a password

Under Password, set an Optional control password. When it's set, every read route, every command, and the feedback socket require it. Send it either way:

Left blank, the control API has no authentication and anyone on the network can drive the show. Either set a control password or keep it on a private, trusted network, exactly like the OBS output server.


Commands

Every command is a GET. A command that works answers 200 with {"ok":true}. A bad clip reference or an unknown route answers 404, a malformed value answers 400, and both carry an error string.

Transport commands sent to an idle program do nothing and still report success, the same way the keyboard shortcuts behave.

Clips

Route What it does
/api/trigger/index/{n}Take the Nth clip live, 1-based, in stable board order (playlists top to bottom, then Unsorted). Unaffected by searching in the app.
/api/trigger/id/{id}Take a specific clip live by its id, which you read from /api/state. Percent-encode it.
/api/clip/next, /api/clip/prevStep through the board.

Triggering a clip that's already live takes it off, so one button can serve as both.

Transport

Route What it does
/api/transport/play, /pause, /toggle, /restartTransport for the primary layer (media if live, else audio).
/api/transport/seek?by={seconds}Jump forward or back. Negative goes back.
/api/transport/seek?to={seconds}Jump to an absolute position.
/api/transport/{layer}/play, /pause, /toggle, /restartThe same, aimed at one explicit layer.
/api/transport/{layer}/seek?by=, ?to=Seek one explicit layer.

{layer} is one of background, media, overlay, audio.

Program

Route What it does
/api/black/on, /off, /toggleCut to or from black.
/api/clear/allClear every layer.
/api/clear/{layer}Clear one layer.

Audio

Route What it does
/api/volume/set?db={n}Set master volume in dB. Clamped to the fader's range.
/api/volume/step?by={n}Nudge master volume by N steps of 3 dB. Negative goes down.
/api/volume/{layer}?v={0-1}Set one layer's cue volume, 0 to 1.
/api/mute/on, /off, /toggleMute and unmute master audio.

Setting or nudging the master volume also unmutes.


Reading state

Route Returns
/api/stateThe full snapshot: what's live on each layer, transport position, master volume, mute, black, every clip on the board, and the playlists.
/api/clipsJust the clip list, as {"clips":[…]}.
/api/thumb/{id}The thumbnail image for one clip.

Each clip in the snapshot carries its index (the number /api/trigger/index/ wants), id, name, kind, layer, playlist, duration, and live.

The API sends Access-Control-Allow-Origin: *, so a web page you write yourself can call it directly from the browser without a proxy.


Live updates

Polling /api/state works, but there's a WebSocket at the same address if you want to be told instead of asking:

ws://192.168.1.42:7341/

You get the full state snapshot the moment you connect, another one whenever anything structural changes, and a lighter transport message about once a second while something is playing. Send {"type":"hello"} at any time to ask for a fresh snapshot.

The password gate applies to the socket too. Pass it as ?k= on the URL, since you can't set headers from a browser's WebSocket.


Troubleshooting

The Remote Control pane is disabled

Nothing answers on the port

Everything answers 401

A trigger answers 404

Anyone can control it