How to Control Backstage via the JSON Remote Control Protocol

Last updated 28 July 2026

How to Control Backstage via the JSON Remote Control Protocol

Drive Backstage playback, audio, calibration, and node-graph endpoints from an external control system (Crestron, AMX, Q-SYS, or a custom application) using a structured JSON protocol over TCP or UDP.

Looking for a different protocol? Backstage supports several control interfaces. Choose the one that fits your integration:

  • OSC -- standard Open Sound Control messages. Best for systems that already speak OSC. See How to Control Backstage via OSC.

  • REST API -- HTTP request/response. Best for web-based dashboards or stateless queries. See Integrate with Backstage via the REST API.

  • UDP trigger strings -- lightweight fire-and-forget playback triggers via the StepperCommander node. See Trigger Playback via UDP.

  • JSON Remote Control Protocol (this page) -- structured JSON messages over TCP/UDP with typed domains, named addressing, and asynchronous live feedback. Best when you need full bidirectional control of sequencers, steppers, broadcast schedulers, calibrators, audio outputs, and node-graph endpoints from a single connection.


Before you start

  • Backstage is running with at least one sequence, stepper, or other entity you want to control
  • You know the IP address of the Backstage server on your control network
  • Your control system can send TCP or UDP messages (plain JSON text, newline-terminated)
  • If using the reference control-panel app: .NET 10 SDK installed on a Windows machine

Steps

1. Enable the protocol in Settings

Open Settings and expand the Remote Control section. Check Enable Show-Control Protocol. This starts the TCP server immediately.

By default, the TCP server listens on port 7400 on all network interfaces. UDP is disabled by default. To enable UDP, check UDP and confirm the port (default 7401).

[USER-ACTION-REQUIRED: Attach a screenshot of the Settings > Remote Control section here]

2. Restrict access with the IP allowlist

In the same Remote Control section, enter allowed client IPs or CIDR ranges in the IP allowlist field (comma-separated, e.g. 10.0.0.5, 192.168.1.0/24). Leave empty to allow connections from any address.

If you want to require authentication, enter a string in the Auth token field. Clients must then send an auth message before any control commands are accepted.

3. Connect over TCP

From your control system, open a TCP connection to the Backstage server on port 7400 (or your configured port). The protocol uses newline-delimited JSON (NDJSON) -- send exactly one JSON object per line, terminated with a newline.

If an auth token is configured, authenticate first:

{"domain":"sys","action":"auth","args":{"token":"your-token"}}

Verify the connection with a ping:

{"domain":"sys","action":"ping"}

You should receive:

{"ok":true,"result":{"pong":true}}

Quick manual test: Use ncat <host> 7400, then paste a request line and press Enter.

4. Connect over UDP (optional)

If UDP is enabled, send datagrams to port 7401. Each datagram carries one or more newline-separated JSON messages. UDP is connectionless -- responses and live feedback go back to the sender's address, or to a fixed host:port if configured in the UDP feedback dropdown.

UDP is best for fire-and-forget commands on a trusted control LAN. For reliable bidirectional communication with live feedback, use TCP.

5. Address entities by name

Every entity in Backstage is addressed by the exact name you see in the application (case-sensitive). There are no numeric IDs or indices to track.

Use the list action on any domain to discover current names:

{"domain":"seq","action":"list"}

Response:

{"ok":true,"result":{"items":[{"name":"Show A","state":"Stopped","time":0,"duration":120},{"name":"Show B"}]}}

Then target a specific entity by name:

{"domain":"seq","action":"play","target":"Show A"}

The protocol covers these domains:

Domain

Controls

sys

Session management: ping, version, auth, subscribe/unsubscribe

step

Steppers: play, pause, stop, next, goto step

seq

Sequencers: play, pause, stop, seek, jump, tempo, fade

bcast

Broadcast Schedulers: enable/disable, force entry, clear

cal

Calibrator Lite: start alignment, start black level, cancel, restore snapshots

output

Audio Output mixers: volume (master or per-channel), mute

mixer

Audio Matrix Mixers: volume (master or per-channel), mute

node

Node-graph endpoints: send values in, read values out

6. Subscribe to live feedback

For real-time status updates, subscribe to one or more domains:

{"domain":"sys","action":"subscribe","args":{"domains":["seq","step","output"]}}

Omit the domains argument to subscribe to all domains. After subscribing, Backstage pushes:

  1. An immediate snapshot of all current status

  2. Live events whenever a subscribed entity's state changes

  3. A periodic heartbeat (~3 seconds) that re-sends current status, so any dropped message self-heals

Events arrive as unsolicited messages on the same TCP connection:

{"type":"event","domain":"seq","status":{"name":"Show A","state":"Playing","time":12.5,"duration":120,"looped":false,"tempo":1.0,"fade":1.0}}

7. Use Remote Control nodes in the Node Editor

For deeper integration with the node graph, use the dedicated bridge nodes in the Network category:

  • Network:Remote Control In -- receives values sent via the node domain. Set a Command name in the node; external systems send to this name. The node outputs the value as a String, as a Float (parsed number), and fires a Bang on every received message.

  • Network:Remote Control Out -- publishes values from the node graph to external subscribers. Set a Feedback name in the node; subscribers receive updates when the input value changes.

Additionally, any labelled datatype node (Float, Int, String, Color, Bang) automatically becomes a control endpoint when you give it a non-empty Label. Clearing the label hides it from the protocol.

Use node.list to discover all available endpoints:

{"domain":"node","action":"list"}

Response includes a dir field indicating the endpoint's direction: in (settable only), out (readable only), or inout (both).

Send a value to an endpoint:

{"domain":"node","action":"send","target":"houselights","args":{"value":"0.5"}}

8. Use the reference control-panel application

A standalone Backstage Control Panel ships with Backstage (in tools/BackstageControlPanel/). It is a C# WPF application that speaks the full protocol and serves as both a testing tool and a reference implementation for integrators.

To run it:

cd tools/BackstageControlPanel
dotnet build -c Release
dotnet run -c Release

The control panel has:

  • A connection bar at the top: enter Host, Port, and Token, then click Connect. The panel auto-reconnects if the connection drops.

  • Seven tabs for each entity type: Steppers, Sequencers, Broadcast Schedulers, Calibrators, Output (audio), Matrix Mixer (audio), and Nodes. Each tab lists entities with live status and provides control buttons.

  • A Protocol JSON log panel at the bottom (collapsed by default). Expand it to see every sent and received JSON message in real time. Use Pause to freeze the log, Clear to reset it, or Copy All to copy the full log to the clipboard.

[USER-ACTION-REQUIRED: Attach a screenshot of the Backstage Control Panel application here]


Options explained

Settings (Remote Control section)

Setting

Description

Values / Range

Enable Show-Control Protocol

Master on/off for the JSON control protocol

Checkbox (off by default)

Bind interface

Network interface the protocol listens on

Dropdown: Any (0.0.0.0) or a specific NIC

TCP

Enable or disable the TCP transport

Checkbox (on by default when protocol is enabled)

TCP Port

Port number for the TCP server

1--65535 (default 7400)

UDP

Enable or disable the UDP transport

Checkbox (off by default)

UDP Port

Port number for UDP receive

1--65535 (default 7401)

UDP feedback

Where to send UDP responses and events

Dropdown: Reply to sender / Fixed host:port

Feedback host

IP address for fixed-target UDP feedback (visible when "Fixed host:port" selected)

IP address

Feedback port

Port for fixed-target UDP feedback

1--65535 (default 7402)

CRLF line endings

Use CRLF instead of LF as line terminator in responses

Checkbox (on by default)

Auth token

Require clients to authenticate before issuing commands

Text (empty = no auth)

IP allowlist

Restrict which IPs/CIDRs may connect

Comma-separated (empty = allow any)

Feedback rate (Hz)

How often the server checks for and pushes status changes

1--60 Hz (default 10)

Key message types

Message

Description

Example

sys.ping

Connection health check

{"domain":"sys","action":"ping"}

sys.version

Protocol version query

Returns {"app":"Backstage","proto":1}

sys.subscribe

Start receiving live status events

{"domain":"sys","action":"subscribe","args":{"domains":["seq","step"]}}

step.goto

Jump to a specific step (1-based)

{"domain":"step","action":"goto","target":"Intro","args":{"step":3}}

seq.play

Start playback of a sequencer

{"domain":"seq","action":"play","target":"Show A"}

seq.jump

Seek to an absolute time position (seconds)

{"domain":"seq","action":"jump","target":"Show A","args":{"time":42.5}}

seq.tempo

Set playback speed (1.0 = normal)

{"domain":"seq","action":"tempo","target":"Show A","args":{"value":0.5}}

output.volume

Set audio output volume (linear, 1.0 = unity)

{"domain":"output","action":"volume","target":"Main Out","args":{"volume":0.8}}

mixer.mute

Mute/unmute a matrix mixer

{"domain":"mixer","action":"mute","target":"FOH Matrix","args":{"mute":true}}

node.send

Push a value into a node-graph endpoint

{"domain":"node","action":"send","target":"houselights","args":{"value":"0.5"}}

cal.align

Start camera calibration alignment

{"domain":"cal","action":"align","target":"Dome"}


Common Mistakes

  • Forgetting the newline -- Every JSON message must be terminated with a newline character (LF, or CR+LF). Without it, the server buffers the partial line and never processes the message. Make sure your control system appends the line terminator to each message it sends.

  • Case mismatch on target names -- Entity names are matched exactly (case-sensitive). "Show A" and "show a" are different targets. Use the list action to confirm the exact name spelling.

  • Sending control commands before authenticating -- If an auth token is configured in Backstage, every command returns error 401 until you send a valid sys.auth message. If you get unexpected 401 errors, check whether a token is set in Settings > Remote Control > Auth token.

  • Assuming control responses confirm the action completed -- A successful response ("accepted": true) only means Backstage received and queued the command. The action is applied asynchronously. Subscribe to live events or poll with status to confirm the actual state change.

  • Using UDP on an untrusted network -- UDP is connectionless and source-spoofable. Only use it on a dedicated, trusted control LAN. For production integrations, prefer TCP.


Tips

  • Use the id field in your requests (any JSON value -- a number, string, or UUID). Backstage echoes it back in the response, which lets async clients correlate requests with responses when multiple messages are in flight.

  • The Backstage Control Panel app's Protocol JSON log is the fastest way to debug integration issues. Pause the log, reproduce the problem, then inspect the exact JSON exchanged.

  • Ports 7400/7401 were chosen to avoid conflicts with common AV protocols (NDI, Dante, ST 2110, Q-SYS, Crestron, AMX). If your site runs DDS/RTPS middleware, which also uses the 7400 range, change the port in Settings to avoid a clash.

  • The periodic heartbeat (~3 s) re-sends current status to all subscribers. This means a dropped event frame self-heals within a few seconds -- you do not need to implement your own polling loop.

  • You can give any Datatype:Float, Datatype:Int, Datatype:String, Datatype:Color, or Datatype:Bang node a non-empty Label to expose it as a control endpoint without adding dedicated Remote Control nodes. This is convenient for quick integrations where you want to control an existing graph variable externally.