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
| Transport | Address | Notes |
|---|---|---|
| Unix socket | /tmp/goxlr.socket | Windows uses a named pipe: @goxlr.socket |
| HTTP | POST /api/command | Content-Type: application/json, command in the body |
| WebSocket | /api/websocket | The only one that also gets the live patch stream |
The command set is identical on all three.
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.rs — DaemonRequest 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
| Response | When |
|---|---|
| "Ok" | A GoXLRCommand succeeded. Bare string. |
| { "Error": "..." } | Anything failed. |
| HttpSettings | Reply to GetHttpState. |
| DaemonStatus | Reply to GetStatus. |
| Patch | A JSON Patch on the websocket stream. |
How Rust types land in JSON
- A struct becomes a
key: valueobject. boolbecomes a JS boolean.Optionbecomes the value ornull— used constantly for features the Mini doesn’t have.HashMap,BTreeMapandEnumMapall become objects keyed by the first type. Somixers: HashMap<String, MixerStatus>arrives as"mixers": { "SERIAL_NUMBER": { ... } }.Vecbecomes an array.
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.
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.