Local API

<- back to doc root  

What the Local API does

Audio Forge includes a small built-in server that runs on your machine. It lets external tools, like an Elgato Stream Deck plugin, a custom script, or any app on your PC, control playback over HTTP or WebSocket, with no extra software required.

If you already use the MQTT integration, the Local API supports the exact same commands. The difference is that it runs entirely on your machine with zero setup: no broker to install, no network to configure.

Enabling the Local API

Go to Settings → External Control → Local API and make sure the feature is enabled (it’s on by default).

You can configure:

  • Enabled toggle (default: on).
  • Port number (default: 8329).
  • Listen on LAN toggle (default: off). When on, devices on your local network can reach the API. When off, only software on the same machine can connect.
  • API Key (auto-generated). Required when connecting from another device on the network. You can copy it or regenerate it from Settings.

Once enabled, Audio Forge listens on http://localhost:8329. If “Listen on LAN” is turned on, it listens on all network interfaces instead.

Quick start

Open a terminal and try:

# See what's playing right now
curl http://localhost:8329/api/state

# List every library and category
curl http://localhost:8329/api/catalog

# Play a category
curl -X POST http://localhost:8329/api/command ^
  -H "Content-Type: application/json" ^
  -d "{\"command\":\"play\",\"section\":\"Music\",\"categoryName\":\"Battle\"}"

That’s it, if Audio Forge is running with the Local API enabled, you’ll get JSON responses immediately.

HTTP endpoints

MethodPathDescription
GET/api/stateCurrent playback state
GET/api/catalogFull library and category catalog
POST/api/commandExecute a command (JSON body)

GET /api/state

Returns a JSON snapshot of what Audio Forge is doing right now:

{
  "libraryUuid": "...",
  "libraryName": "[Default]",
  "musicPlaying": true,
  "ambiancePlaying": false,
  "musicVolume": 0.75,
  "ambianceVolume": 0.5,
  "musicCategories": [{"uuid": "...", "name": "Battle"}],
  "ambianceCategories": [{"uuid": "...", "name": "Rain"}],
  "lastEcho": {"uuid": "...", "name": "Thunder", "at": "2025-06-01T12:34:56Z"},
  "categoryVolume": {"<uuid>": 0.8}
}

GET /api/catalog

Returns every library with its Music and Ambiance categories:

{
  "activeLibraryUuid": "...",
  "libraries": [
    {
      "uuid": "...",
      "name": "[Default]",
      "music": [{"uuid": "...", "name": "Battle"}],
      "ambiance": [{"uuid": "...", "name": "Rain"}]
    }
  ]
}

POST /api/command

Send a JSON command in the request body. On success:

{"ok": true, "message": "play ok"}

On error:

{"ok": false, "error": "category not found"}

WebSocket

For tools that want real-time updates (like a Stream Deck plugin that needs to show current state), connect a WebSocket to:

ws://localhost:8329/ws

Sending commands

Send the same JSON command objects you would POST to /api/command:

{"command": "play", "section": "Music", "categoryName": "Battle"}

The server replies with a result message for each command.

Receiving updates

The server automatically pushes updates whenever something changes. Each message has a type field:

typeWhen it’s sentWhat it contains
stateOn connect + whenever playback changesSame as GET /api/state
catalogOn connect + when libraries changeSame as GET /api/catalog
resultAfter each command you sendok, message or error, requestId
echoWhen an echo sound is triggereduuid, name, at

State updates are debounced (250 ms) so you won’t be flooded during rapid changes like volume sweeps.

Commands

All commands use the same JSON schema as the MQTT integration. Every command accepts an optional requestId string you can use to match responses.

play

Play or resume a section, or select a specific category.

{"command": "play", "section": "Music"}
{"command": "play", "section": "Music", "categoryName": "Battle"}
{"command": "play", "section": "Ambiance", "categoryUuid": "<uuid>"}
  • Without a category: resumes the whole section.
  • With a category: selects and plays it.
  • Ambiance uses toggle semantics. Playing an already-active Ambiance category turns it off.

pause

{"command": "pause", "section": "Music"}

stop

{"command": "stop", "section": "Ambiance"}

setActiveLibrary

Switch the active library by UUID or name.

{"command": "setActiveLibrary", "libraryName": "My Library"}
{"command": "setActiveLibrary", "libraryUuid": "<uuid>"}

setVolume

Set the volume for a whole section (0.0 to 1.0), or for a specific category within a section.

{"command": "setVolume", "section": "Music", "value": 0.65}
{"command": "setVolume", "section": "Ambiance", "value": 0.5,
  "categoryName": "Rain", "transitionMs": 1000}

The optional transitionMs controls how quickly the volume fades to the new level (default: 500 ms).

playEcho

Trigger a one-shot Echo sound.

{"command": "playEcho", "categoryName": "Thunder"}
{"command": "playEcho", "section": "Ambiance", "categoryUuid": "<uuid>"}

setCategoryEnabled

Explicitly enable or disable a category in a section.

{"command": "setCategoryEnabled", "section": "Ambiance",
  "categoryName": "Rain", "enabled": true}

restoreState

Restore a previously saved state from a share link created by the app.

{"command": "restoreState", "link": "slashpaf://..."}

setConfig

Forward a configuration setting to the MQTT integration (if connected). Useful for advanced automation.

{"command": "setConfig", "key": "someKey", "value": "someValue"}

Error handling

If a command can’t be executed (for example, a category name doesn’t exist or a required field is missing), the response will contain "ok": false and an error message describing what went wrong.

{"ok": false, "error": "category not found"}
{"ok": false, "error": "section required"}
{"ok": false, "error": "invalid json"}

Security

By default the Local API binds to 127.0.0.1 (localhost only) and is not reachable from other devices on your network.

If you enable Listen on LAN, the API becomes reachable from other devices. In that case an API key is required for all non-localhost requests. The key is auto-generated and shown in Settings → External Control → Local API; pass it with every request using one of:

  • Header: Authorization: Bearer <your-api-key>
  • Query parameter: ?apiKey=<your-api-key>

Examples:

# Using the Authorization header
curl http://192.168.0.10:8329/api/state ^
  -H "Authorization: Bearer your-api-key-here"

# Using the query parameter
curl "http://192.168.0.10:8329/api/state?apiKey=your-api-key-here"

# Sending a command from another device
curl -X POST http://192.168.0.10:8329/api/command ^
  -H "Authorization: Bearer your-api-key-here" ^
  -H "Content-Type: application/json" ^
  -d "{\"command\":\"play\",\"section\":\"Music\",\"categoryName\":\"Battle\"}"

# WebSocket with API key
wscat -c "ws://192.168.0.10:8329/ws?apiKey=your-api-key-here"

Localhost requests never require a key, even when LAN mode is on.

You can regenerate the API key at any time from Settings → External Control → Local API. Any previously connected clients will need the new key.