Docs

The API.

Three transports, the JSON message shapes, the live patch stream, and the two traps everyone hits.

The Utility is API-driven all the way down. goxlr-client and the web UI are not special — they are both just clients sending the same JSON messages you can send.

Three ways in

TransportAddressNotes
Unix socket/tmp/goxlr.socketWindows uses a named pipe: @goxlr.socket
HTTPPOST /api/commandContent-Type: application/json, command in the body
WebSocket/api/websocketThe only one that also gets the live patch stream

The command set is identical on all three.

Start at the socket

Even if you intend to use HTTP. The web server can be disabled or moved to another port by the user; the socket is always there while the daemon runs. Connect to it, send GetStatus, and read http_settings out of the response to find out where — and whether — the web server is listening.

Socket framing

Messages are length-prefixed:

[message length: unsigned 32-bit, big-endian][JSON message]

Strictly request/response — read the length, then read exactly that many bytes. In Rust, the interprocess crate plus the repo’s own ipc crate will do it, and client/src/main.rs is the reference implementation.

WebSocket

Requests are processed in the order received, but responses can come back out of order. So every request carries an id (unsigned 32-bit) which is echoed on its response:

{ "id": 72, "data": "GetStatus" }

{ "id": 72, "data": { "Status": { ... } } }

Message shapes

There is no formal schema document; the shapes come from the ipc crate’s src/lib.rsDaemonRequest for requests, DaemonResponse for responses, and GoXLRCommand for device commands. Argument enums live in the types crate; struct definitions in the ipc crate’s device.rs.

Serialisation follows four rules. An enum constant becomes its own name as a JSON string; with no parameters that’s all you send. One parameter becomes an object keyed by the constant. More than one becomes an object whose value is an array.

"GetStatus"

{ "OpenPath": "Profiles" }

{ "Command": [Serial, GoXLRCommand] }

{ "Command": ["SERIAL_NUMBER", { "SetFader": ["A", "Mic"] }] }

The first parameter of Command is the device serial number, which you get from GetStatus. SetFader takes a fader name and then a channel name.

Responses

ResponseWhen
"Ok"A GoXLRCommand succeeded. Bare string.
{ "Error": "..." }Anything failed.
HttpSettingsReply to GetHttpState.
DaemonStatusReply to GetStatus.
PatchA JSON Patch on the websocket stream.

How Rust types land in JSON

  • A struct becomes a key: value object.
  • bool becomes a JS boolean.
  • Option becomes the value or null — used constantly for features the Mini doesn’t have.
  • HashMap, BTreeMap and EnumMap all become objects keyed by the first type. So mixers: HashMap<String, MixerStatus> arrives as "mixers": { "SERIAL_NUMBER": { ... } }.
  • Vec becomes an array.
Don’t assume keys exist

EnumMap<A,B> guarantees every possible key of type A is present. HashMap and BTreeMap do not. Some HashMaps may become EnumMaps in future releases, so code defensively either way.

The live patch stream

Anything that changes the device state — you turning a physical knob, another client sending a command — emits a JSON Patch to every connected websocket client. The patches apply to the same Status object you got from GetStatus.

Three reasonable ways to apply them: mutate your struct directly (fast-json-patch in JS, reflection in typed languages); keep the raw GetStatus JSON around, patch that, and re-deserialize; or serialize, patch, deserialize each time. There’s a Rust reference project in FrostyCoolSlug/goxlr-obs-fader-sync.

Two traps

Your own writes come back to you. In addition to the Ok response, your change arrives as a patch like everyone else’s — and it can lag behind the commands you’re sending. Bind a slider straight to Status and spam it, and the component will fight itself.

On the full-size GoXLR the motorised faders are authoritative. Set a volume from 0 to 255 and the hardware emits a stream of intermediate events on the way, and may overshoot or undershoot the target — which the daemon cannot correct. The web UI’s approach is to ignore patch events for a slider while the user is dragging it, then write the last-sent value into Status on release, accepting a slight desync as the lesser problem.

Fastest way to learn the formats

Open the web UI with your browser’s devtools console and watch the websocket traffic while you poke at the device.

Written from the GoXLR Utility project’s own documentation (The GoXLR Utility API) and reproduced here in our own words, so you don’t have to leave the site. The project’s originals remain the authority if anything here goes stale.