Local HTTP API
Connect another application to the Media Board desktop player. All paths below are relative to the configured API base URL.
Connection & security
In the desktop app, open Settings > More settings > Local API, enable the API and press Apply. Keep Media Board open.
http://127.0.0.1:47831/api/v1
The default is local to this PC. For LAN access, bind to this PC's specific LAN IPv4 address and use that address from the client. Ports 1024–65535 are supported. Use the numeric IP, not localhost or a hostname; wildcard addresses are not supported. The server does not automatically choose another port if binding fails.
LAN access may require a Windows private-network firewall rule for the selected TCP port, restricted to trusted clients. No firewall rule is added automatically. Update the address if DHCP changes it.
Quick start
Read the catalogue, copy a button ID, then send a command. IDs are GUIDs, not titles. The desktop also exposes the ID in Configure > Button code.
$api = 'http://127.0.0.1:47831/api/v1'
Invoke-RestMethod "$api/buttons"
$id = 'REPLACE-WITH-BUTTON-CODE'
Invoke-RestMethod -Method Post "$api/buttons/$id/play"
Invoke-RestMethod -Method Post "$api/buttons/$id/stop"
const api = 'http://127.0.0.1:47831/api/v1';
async function request(path, body) {
const response = await fetch(api + path, body === undefined ? {} : {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
const result = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${result.error}`);
return result;
}
const catalogue = await request('/buttons');
const id = 'REPLACE-WITH-BUTTON-CODE';
const run = await request(`/buttons/${id}/play`, {
externalSequencing: true
});
// Save run.executionId for audio/video; poll it to confirm playback.
Browser address-bar navigation works for GET /buttons and GET /status. Playback commands require POST. These samples control your real desktop player when run; this documentation does not send commands automatically.
Endpoints
| Method | Path | Behavior |
|---|---|---|
| GET | /buttons | Catalogue across playlists, playback flags and execution IDs. |
| GET | /status | Service state and active/paused buttons. |
| GET | /executions/{executionId} | Audio/video execution state and terminal reason. |
| POST | /buttons/{id}/play | Start, leave playing content active, or resume paused content. Optional run overrides. |
| POST | /buttons/{id}/restart | End the previous execution and start a new one. Optional run overrides. |
| POST | /buttons/{id}/pause | Requires executionId; optional fadeOutMs. |
| POST | /buttons/{id}/resume | Requires executionId; optional fadeInMs. Never restarts a terminated run. |
| POST | /buttons/{id}/stop | Optional executionId protects a newer run; optional fadeOutMs overrides the inherited audio fade. |
| POST | /buttons/{id}/dmx/sync | Align an active DMX timeline with an external clock. |
Responses are JSON. Send Content-Type: application/json with JSON bodies (maximum 1024 bytes). Bodyless play/restart/stop remain supported. Navigation buttons cannot be played. Renaming a button preserves its ID; duplicating creates a new one. Query the catalogue again after import or restore.
Playback executions
A new audio/video start returns HTTP 202 with an execution ID. This acknowledges the request, not successful decoding. Images and DMX-only buttons do not have a multimedia execution ID.
{
"result": "accepted",
"id": "BUTTON-GUID",
"executionId": "MEDIA-EXECUTION-GUID",
"dmxAllowed": true,
"dmxExecutionId": null
}
Pause and resume the same ID. A fade is asynchronous; playback advances during fade-out and pauses at the position reached at its end.
{ "executionId": "MEDIA-EXECUTION-GUID", "fadeOutMs": 500 }
{ "executionId": "MEDIA-EXECUTION-GUID", "fadeInMs": 500 }
Fade values are integer milliseconds, 0–60000. An explicit value, including zero, takes precedence over the button's positive fade time, then playlist crossfade if Mix is enabled, then zero. Resume cancels a pending pause. Repeated pause/resume requests are harmless for the same active run.
Explicit fade-out overrides desktop settings. For both pause and stop, a positive fadeOutMs requests that audio fade even when the button's Fade out is zero and the playlist's Mix on transition is disabled. Supply the duration per request as integer milliseconds (1–60000); no fixed client duration is required. Zero means immediate; omitting the parameter inherits the saved settings. This applies to audio and the audio portion of video, without modifying saved settings. An already paused run is not resumed to perform a fade, and repeated requests do not restart an in-progress pause or stop fade.
{
"executionId": "MEDIA-EXECUTION-GUID",
"buttonId": "BUTTON-GUID",
"state": "paused",
"positionMs": 12500,
"durationMs": 180000,
"endReason": null
}
Position is absolute within the source file. Duration is the source duration (zero if unknown); a configured End may finish earlier. Poll every 500–1000 ms. Active states are starting, playing, pausing, paused and fading_out. Fade-in is reported as playing.
| State | endReason | Meaning |
|---|---|---|
| completed | natural_end | Source or configured End reached. |
| stopped | explicit_stop | Explicit stop or restart. |
| stopped | replaced | Replaced without preserving pause. |
| failed | playback_error | Decoder/output failure. |
Terminal snapshots remain queryable for at least 10 minutes, even after removal from Now playing. They are held in memory and lost when the application closes. Active and paused runs do not expire.
Stop with fade-out
For a safe stop, send { "executionId": "MEDIA-EXECUTION-GUID" } to the stop endpoint. An obsolete ID cannot stop a newer run. Without a body, stop targets the button's current content.
{ "executionId": "MEDIA-EXECUTION-GUID", "fadeOutMs": 500 }
Stop inherits the same fade-out precedence as pause, including for bodyless requests: explicit fadeOutMs (0–60000, zero means immediate), otherwise positive button Fade out, otherwise the owning playlist Crossfade duration if Mix is enabled, otherwise zero. No extra minimum is imposed. Paused media stops immediately; a pending pause is cancelled.
HTTP 200 returns result fading_out during the fade. The execution then becomes stopped / explicit_stop, never natural_end, with no loop or playlist advancement. Repeated stops do not extend the fade. Pause/resume and play cannot cancel a pending stop; use restart for a new execution, protected from the old callbacks. Immediate and inactive stops return stopped and already_stopped, respectively.
A positive fadeOutMs forces the stop fade even with Mix on transition disabled and button Fade out set to zero. Its duration is supplied independently on each API call; it does not change the button or playlist configuration. Omit it only when you want inheritance, or send zero for immediate stop.
Video uses an audio fade, not a visual dissolve. DMX stops immediately with its normal stop/keep-alive behavior; no lighting fade is applied. Images and DMX-only buttons stop immediately.
$api = 'http://127.0.0.1:47831/api/v1'
$id = 'REPLACE-WITH-BUTTON-CODE'
$executionId = 'REPLACE-WITH-SAVED-EXECUTION-ID'
$body = @{ executionId = $executionId; fadeOutMs = 2000 } | ConvertTo-Json
Invoke-RestMethod -Method Post "$api/buttons/$id/stop" `
-ContentType 'application/json' -Body $body
Invoke-RestMethod "$api/executions/$executionId"
{
"result": "fading_out",
"id": "BUTTON-GUID",
"executionId": "MEDIA-EXECUTION-GUID"
}
Poll GET /executions/{executionId} every 500–1000 ms until terminal state. Completion is state: "stopped" with endReason: "explicit_stop". Set fadeOutMs: 0 for immediate stop, or omit fadeOutMs to inherit the desktop settings. Send integer milliseconds, not strings or fractions.
External sequencing
For a quiz background or another externally managed sequence, send these options with each new play/restart:
{
"pauseWhenReplaced": true,
"allowOverlap": false,
"externalSequencing": true
}
pauseWhenReplaced preserves this background when replaced. allowOverlap: false requests replacement through the mixer. Normal transition settings still govern crossfades.
externalSequencing: true disables automatic Sequence/Shuffle advancement, playlist wrapping and button looping for this audio/video execution only. It persists across pause/resume, does not change saved settings and does not affect DMX looping. A new manual execution uses the usual saved rules.
Options supplied to play on an already active execution are ignored. Use restart only when you intentionally want a new execution; use resume with the saved ID to continue an existing one.
Advance only on completed + natural_end. Never treat stopped, failed, HTTP 404, a timeout or a network error as the end of a track. Do not fall back to play/restart when resume fails.
Loop & crossfade
Repeat a single audio/video button using its configured Start-to-End range (or source duration when End is unset). These options apply only to the new execution and never modify the saved button lock or playlist settings.
{
"externalSequencing": true,
"loop": true,
"loopCrossfadeMs": 1500
}
| Option | Value | Behavior |
|---|---|---|
| loop | true | Repeat this media, even with externalSequencing enabled. |
| loop | false | Disable the saved button loop for this run. Playlist advancement still applies unless externalSequencing is true. |
| loop | Omitted / null | Follow saved playback rules; externalSequencing suppresses the saved button loop. |
| loopCrossfadeMs | Integer 0–60000 | Override the audio overlap duration. Zero requests a hard loop; a positive value works even with Mix on transition off. |
| loopCrossfadeMs | Omitted / null | With Mix on transition enabled, use positive button Fade out, otherwise playlist Crossfade duration. With Mix off, use zero. |
loopCrossfadeMs does not enable looping by itself. Send a JSON number, not a string; fractions and out-of-range values are rejected with HTTP 400.
Repetitions retain one executionId and do not emit completed/natural_end between loops. Pause/resume preserves the loop and ID. Stop with the saved executionId to end that run. The associated DMX timeline is not restarted or synchronized by a media loop.
Both the desktop padlock and API loops support an audio crossfade. By default it follows Mix on transition: a positive button Fade out, otherwise the playlist Crossfade duration. Set "loopCrossfadeMs": 1500 on play/restart to override it, or zero for a hard loop. Integer values 0–60000 are supported. Duration is captured at start and limited to half the media range and a 32 MiB decoded-head cache. Each repetition continues after the head already consumed in the overlap. Pause and replacement fades remain active. Video audio is mixed too, but the video image seeks rather than dissolving.
Options supplied to play on an already active execution are ignored. Use restart only when you intentionally want a new execution; use resume with the saved ID to continue an existing one.
External DMX clock
Start the button and retain dmxExecutionId. It is separate from the audio/video executionId. Send that DMX ID in the sync request's executionId field.
{
"executionId": "DMX-EXECUTION-GUID",
"sequence": 1,
"positionMs": 12500,
"isPlaying": true,
"forceSeek": false
}
Only forceSeek is optional. Sequence must be a nonnegative, strictly increasing integer per DMX execution. Position is a finite nonnegative time in milliseconds from the external video's start, not a wall-clock timestamp.
Send every 200–250 ms and immediately on pause, resume or seek. Use forceSeek after intentional seeks. Corrections occur above 100 ms of drift, on playback state changes or forced seeks.
Without updates, DMX continues with its local clock, or stays paused if the last message paused it. The next message can realign it. Sync changes only that DMX timeline, not audio/video or other buttons. Looping timelines wrap the time; non-looping timelines complete at their end. Sync never implicitly starts a timeline.
On success, HTTP 200 returns executionId, sequence, corrected, positionMs, driftMs and isPlaying. This is not network-latency or wireless-hardware compensation.
Errors & retries
{ "error": "stale_execution" }
| HTTP | Typical cause | Client action |
|---|---|---|
| 400 | Invalid ID/body; execution_id_required | Correct the request. Pause/resume require an ID. |
| 403 | Host rejected or license restriction | Check numeric IP/port and plan access. |
| 404 | Button/execution not found | Check catalogue or retention; do not advance. |
| 405 | Wrong HTTP method | Use POST for commands. |
| 409 | app_busy / command_in_progress | Retry later with the same execution ID. |
| 409 | stale_execution / execution_not_resumable | Reconcile state; do not silently start a new run. |
| 413 / 415 | Body too large / json_required | Limit JSON to 1024 bytes; set its Content-Type. |
| 422 | Missing media or no DMX timeline | Fix the button configuration. |
| 500 / 503 | Command failed / application unavailable | Re-query state; wait for availability. |
Commands are serialized without an unbounded queue; reads remain available. If a start response is lost, inspect status before deciding what to do: the start may already have happened. Never blindly retry restart.
For DMX busy/initializing errors, retry with a fresh position and higher sequence. For stale_execution or dmx_not_active, stop sending and explicitly decide whether to start again. See the reference for the complete DMX error list.
Reference downloads
Markdown references can be shared with an integration developer or coding agent. They are packaged from the project's API documentation.
Validate your complete workflow with the current desktop build and actual output hardware before using an integration live.