Layer-level CasparCG AMCP commands exposed as JSON POST routes. These are the direct playout controls (single channel/layer). Full look takes use POST /api/scene/take instead.
Caspar: required (503 if AMCP is down).
Implementation: src/api/routes-amcp.js · AMCP client: src/caspar/amcp-basic.js · Command builder: src/caspar/amcp-command-plan.js
AMCP mapping table: docs/reference/amcp-mapping.md
For batched/raw AMCP, logging, restart, and diagnostics see playback-and-amcp.md.
POST http://127.0.0.1:4200/api/<endpoint>
Content-Type: application/json
Most layer commands accept:
| Field | Type | Default | Description |
|---|---|---|---|
channel |
number | 1 |
Caspar channel |
layer |
number | — | Caspar layer (channel-layer, e.g. 10 → 1-10). Omit only where AMCP allows channel-only (e.g. PRINT) |
Clip commands (play, load, loadbg) add:
| Field | Type | Description |
|---|---|---|
clip |
string | Media path, route://…, ndi://…, [HTML] …, or URL (see Clip strings) |
loop |
boolean | Append LOOP to AMCP |
transition |
string | MIX, PUSH, WIPE, SLIDE, STING, … (CUT omitted on wire) |
duration |
number | Transition length in frames |
tween |
string | Easing name (e.g. linear, easeInOutCubic) |
auto |
boolean | AUTO — background plays when foreground ends (loadbg / play) |
parameters |
string | Extra AMCP tokens (STING template path, etc.) |
audioFilter |
string | Raw FFmpeg AF filter string |
audioRoute |
string | Shorthand: 1+2 (default), 3+4, 5+6, … → mapped to AF |
playbackMatrixPOST /api/play, /api/stop, /api/clear, and /api/amcp/batch return the AMCP result object plus a server-side tracker snapshot:
{
"data": "…",
"playbackMatrix": {
"1-10": {
"channel": 1,
"layer": 10,
"clip": "AMB",
"startedAt": 1717512345678,
"durationMs": 120000,
"playing": true,
"loop": false,
"isRoute": false
}
}
}
The tracker updates on play (records clip) and stop/clear (removes layer). OSC mode can also feed the matrix; see src/state/playback-tracker.js.
WebSocket clients receive playback.matrix state changes when the matrix updates.
playbackMatrixPOST /api/load, /api/loadbg, /api/pause, /api/resume, /api/call, /api/swap, /api/add, /api/remove return the raw AMCP JSON only (no matrix merge).
| Status | When |
|---|---|
400 |
Missing commands on batch routes, missing cmd on /api/raw, etc. |
502 |
Caspar rejected the command (upstream failure) |
503 |
Caspar not connected |
| Endpoint | Playback tracker | Live scene broadcast |
|---|---|---|
play |
Records layer | May invalidate/broadcast if channel is a program channel |
stop, clear |
Clears layer | Same |
load, loadbg |
— | Same |
| Others | — | — |
Direct AMCP on a program channel can desync UI “live look” state from Caspar; the server broadcasts sceneLive when routing marks the channel as program.
POST /api/playMaps to PLAY <channel>-<layer> <clip> ….
{
"channel": 1,
"layer": 10,
"clip": "AMB",
"loop": true,
"transition": "MIX",
"duration": 25,
"tween": "linear"
}
Empty clip: PLAY with no clip string is valid (resume / transition on existing layer) — omit clip or use "".
curl -s -X POST http://127.0.0.1:4200/api/play \
-H 'Content-Type: application/json' \
-d '{"channel":1,"layer":10,"clip":"AMB","loop":true}'
const res = await fetch(`${apiBase}/api/play`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ channel: 1, layer: 10, clip: 'AMB', loop: true }),
})
const data = await res.json()
// data.playbackMatrix
| Body | Approximate AMCP |
|---|---|
{ "channel":1, "layer":10, "clip":"AMB", "loop":true } |
PLAY 1-10 AMB LOOP |
{ "channel":1, "layer":10, "clip":"clip.mov", "transition":"MIX", "duration":25, "tween":"linear" } |
PLAY 1-10 clip.mov MIX 25 linear |
{ "channel":1, "layer":10, "clip":"[HTML] http://host/page", "audioRoute":"3+4" } |
PLAY 1-10 [HTML] "http://host/page" AF … |
POST /api/loadMaps to LOAD — loads into foreground without necessarily starting playback (Caspar semantics).
Same clip/transition/audio fields as play, except auto is not passed from the HTTP handler.
curl -s -X POST http://127.0.0.1:4200/api/load \
-H 'Content-Type: application/json' \
-d '{"channel":1,"layer":10,"clip":"still.png","loop":true}'
Typical workflow: load or loadbg → play or call to air.
POST /api/loadbgMaps to LOADBG — loads into background stack for later CALL or mixed PLAY.
Supports auto (background auto-starts when foreground ends).
curl -s -X POST http://127.0.0.1:4200/api/loadbg \
-H 'Content-Type: application/json' \
-d '{
"channel": 1,
"layer": 20,
"clip": "next.mov",
"transition": "MIX",
"duration": 25,
"tween": "easeInOutCubic",
"auto": true
}'
Then take background to air:
curl -s -X POST http://127.0.0.1:4200/api/call \
-H 'Content-Type: application/json' \
-d '{"channel":1,"layer":20}'
Or crossfade with play on the same layer after loadbg.
POST /api/pause · POST /api/resumeMaps to PAUSE / RESUME.
{ "channel": 1, "layer": 10 }
curl -s -X POST http://127.0.0.1:4200/api/pause \
-H 'Content-Type: application/json' \
-d '{"channel":1,"layer":10}'
POST /api/stop · POST /api/clearMaps to STOP / CLEAR.
Both update playbackMatrix (layer removed from tracker).
curl -s -X POST http://127.0.0.1:4200/api/stop \
-H 'Content-Type: application/json' \
-d '{"channel":1,"layer":10}'
CLEAR removes producers and mixer state on the layer (stronger than stop).
POST /api/callMaps to CALL <channel>-<layer> [function] [params] — brings background to foreground (Caspar CALL semantics).
| Field | Type | Description |
|---|---|---|
fn |
string | Optional CALL function name |
params |
string | Optional parameter string appended to command |
curl -s -X POST http://127.0.0.1:4200/api/call \
-H 'Content-Type: application/json' \
-d '{"channel":1,"layer":20}'
POST /api/swapMaps to SWAP <ch1>-<layer1> <ch2>-<layer2> [TRANSFORMS].
| Field | Type | Description |
|---|---|---|
channel |
number | First channel (default 1) |
layer |
number | First layer |
channel2 |
number | Second channel |
layer2 |
number | Second layer |
transforms |
boolean | Append TRANSFORMS when true |
{
"channel": 1,
"layer": 10,
"channel2": 1,
"layer2": 20,
"transforms": true
}
POST /api/add · POST /api/removeChannel consumer producers (outputs), not clip layers.
POST /api/add| Field | Type | Description |
|---|---|---|
channel |
number | Caspar channel |
consumer |
string | Consumer type (e.g. decklink, screen) |
params |
string | Consumer parameter string |
index |
number | Optional ADD <channel>-<index> |
{
"channel": 1,
"consumer": "decklink",
"params": "device 1",
"index": 0
}
POST /api/remove| Field | Type | Description |
|---|---|---|
channel |
number | Caspar channel |
consumer |
string | Consumer id (when not using index) |
index |
number | REMOVE <channel>-<index> |
| Form | Transport | Notes |
|---|---|---|
clip.mov, media/still.png |
Usually typed casparcg-connection |
Quoted if spaces |
[HTML] … |
Raw AMCP | Browser/HTML producer |
ndi://… |
Raw | NDI source |
route://1-10 |
Raw | Routed layer |
http(s)://… |
Typed or [HTML] |
Non-video extensions forced to HTML |
STING transitions require transition: "STING" and parameters with template path.
Structured AMCP over WebSocket uses the same fields as HTTP bodies (type: "play", channel, layer, clip, …). See smoke test dispatchStructuredAmcp in tools/smoke/highascg-health-api-amcp.test.js.
| Doc | Content |
|---|---|
| scene-take.md | Multi-layer look take (LOADBG/PLAY pipeline) |
| playback-and-amcp.md | /api/amcp/batch, raw, print, restart, logs |
| mixer-cg-scene.md | MIXER, CG, FTB |
| project-state-media.md | /api/state, /api/channels, media list |
openapi.yaml — schemas PlayRequest, LayerTransportRequest, PlaybackResponse, AmcpBatchRequest.