Developers / API v1

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.

PowerShell
$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"
JavaScript
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

MethodPathBehavior
GET/buttonsCatalogue across playlists, playback flags and execution IDs.
GET/statusService state and active/paused buttons.
GET/executions/{executionId}Audio/video execution state and terminal reason.
POST/buttons/{id}/playStart, leave playing content active, or resume paused content. Optional run overrides.
POST/buttons/{id}/restartEnd the previous execution and start a new one. Optional run overrides.
POST/buttons/{id}/pauseRequires executionId; optional fadeOutMs.
POST/buttons/{id}/resumeRequires executionId; optional fadeInMs. Never restarts a terminated run.
POST/buttons/{id}/stopOptional executionId protects a newer run; optional fadeOutMs overrides the inherited audio fade.
POST/buttons/{id}/dmx/syncAlign 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.

POST /buttons/{id}/pause
{ "executionId": "MEDIA-EXECUTION-GUID", "fadeOutMs": 500 }
POST /buttons/{id}/resume
{ "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.

GET /executions/{executionId}
{
  "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.

StateendReasonMeaning
completednatural_endSource or configured End reached.
stoppedexplicit_stopExplicit stop or restart.
stoppedreplacedReplaced without preserving pause.
failedplayback_errorDecoder/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.

POST /buttons/{id}/stop
{ "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.

PowerShell: stop the saved execution with a two-second fade
$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"
HTTP 200: fade accepted, not yet finished
{
  "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.

POST /buttons/{id}/play or /buttons/{id}/restart
{
  "externalSequencing": true,
  "loop": true,
  "loopCrossfadeMs": 1500
}
OptionValueBehavior
looptrueRepeat this media, even with externalSequencing enabled.
loopfalseDisable the saved button loop for this run. Playlist advancement still applies unless externalSequencing is true.
loopOmitted / nullFollow saved playback rules; externalSequencing suppresses the saved button loop.
loopCrossfadeMsInteger 0–60000Override the audio overlap duration. Zero requests a hard loop; a positive value works even with Mix on transition off.
loopCrossfadeMsOmitted / nullWith 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.

POST /buttons/{id}/dmx/sync
{
  "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" }
HTTPTypical causeClient action
400Invalid ID/body; execution_id_requiredCorrect the request. Pause/resume require an ID.
403Host rejected or license restrictionCheck numeric IP/port and plan access.
404Button/execution not foundCheck catalogue or retention; do not advance.
405Wrong HTTP methodUse POST for commands.
409app_busy / command_in_progressRetry later with the same execution ID.
409stale_execution / execution_not_resumableReconcile state; do not silently start a new run.
413 / 415Body too large / json_requiredLimit JSON to 1024 bytes; set its Content-Type.
422Missing media or no DMX timelineFix the button configuration.
500 / 503Command failed / application unavailableRe-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.

Confirm action