Replay
Requires: control (operator+) for the endpoints that move a channel, mark an in or change the clip bin;
/api/replay/recordings,/api/replay/recorders,/api/replay/channel,/api/replay/clip/list,/api/replay/clip/thumbnailand/api/replay/tag/listonly read and require read (viewer+).
Endpoints for replays: the recordings Replay Recorder Targets make, the Replay Channel inputs that play them, and the project's clip bin. Each Replay Recorder Target records one angle, named after the recorder. A Replay Channel shows the recording of the replay recorder chosen, one angle, at a playhead, which plays, pauses, steps, shuttles, jumps to a timecode and changes to another recorder, another angle, while recording goes on. Every recorder gives one moment the same time-of-day timecode, so a timecode names the same moment on every angle, and changing the angle keeps the moment shown.
Choosing the channel. Name one with target (GUID) or targetname (its name); discover them with /api/objects/list?type=ReplayChannelInput. If you omit both, the project's only Replay Channel is used; with none, or more than one, the call returns 400 and you must name one. Only Replay Channels are considered, so a layer of the same name is not a conflict.
When a change shows. A control call moves the playhead at once and answers 202 Accepted; the picture follows as soon as the frame is decoded, usually on the next rendered frame or the one after. To know what is on screen, read /api/replay/channel: Playhead is the frame due and Shown the frame on screen. They differ while a frame is being decoded. After another replay recorder is chosen, Shown.ReplayRecorder stays the old one, at the same moment, until the new recorder's frame is shown. A change to the clip bin is complete when it is answered, so those calls answer 200 OK.
The clip bin. A clip is a named, tagged time range against the recordings: an in and an out, both time-of-day timecodes, its duration, and when and on which computer it was created. Saving a clip writes no video. Its timecodes name the same moments on every angle, so a clip plays on any angle: from the replay recorder it was cut from (ReplayRecorderId) unless another is asked for, and on the angle shown when it was cut from none. A clip is named by its Id, which /api/replay/clip/add answers and /api/replay/clip/list lists; names need not be unique. The clip bin and its tag list are saved in the project file, so save the project to keep them; a project that is not saved loses the clips made since it last was. A new project starts with the tags Goal, Save, Chance, Foul, Penalty, Yellow card, Red card and Highlight, and so does a project file saved before the clip bin existed. Any text is a tag: one not yet in the list is added to it, and one that differs from a listed tag only in case is that tag, spelled as listed.
Thumbnails. Every clip has a thumbnail: its in frame as a JPEG 480 pixels wide, from the angle the Replay Channel it was saved from showed when it was saved. It is made in the background once the clip is saved, and again when the clip's in or session changes. When the in was marked with /api/replay/markin, as the Replay window's Mark in does, a copy of the frame marked is kept, and a clip saved with that in from the same angle gets it as its thumbnail at once. Otherwise the in frame is read from the recording again, on the processor at low priority rather than on the graphics card that renders. A clip whose in is not recorded yet gets its thumbnail once that frame is recorded, if that happens within two minutes. /api/replay/clip/list says where each thumbnail stands, the replay.clip_thumbnail event says when one is ready, and /api/replay/clip/thumbnail serves it.
Thumbnails are kept on the computer that made them, in Composer's thumbnail cache in the user's local application data: %LOCALAPPDATA%\VindralComposer\ReplayThumbnails on Windows, ~/.local/share/VindralComposer/ReplayThumbnails on Linux. A Runtime running as a Windows service uses the service account's folder. Inside the cache there is a folder per project and, within it, one per Replay Channel (NoChannel for a clip saved without one), each file named after its clip, its in and its version:
ReplayThumbnails\<clip bin Id>\<Replay Channel Id>\<clip Id>_<in as HHMMSSFF>_<version>.jpg
Nothing deletes thumbnails, not even deleting their clip. Thumbnails removed from the cache by hand are made again the next time the project is loaded, as long as the recordings are kept; a thumbnail whose recording is gone cannot be made again. Export project and all assets in Desktop copies the project's thumbnails into a ReplayThumbnails folder beside the exported project. A project opened from there takes them into the cache, so they show on a computer that does not have the recordings.
Recording sessions. Replay Recorder Targets recording at the same time record into one session folder inside the Replay directory, named after the time the first of them started, such as 2026-10-12 143005, each into a subfolder of its own. A clip keeps the session it was marked in (Session), relative to the Replay directory, so it plays from that session's recordings, on any angle recorded in it, after a newer recording has started and after Composer has restarted, and moving the Replay directory with its sessions keeps the clips. The sessions the clip bin's clips were marked in are kept: their recordings stay available, and those not in memory, as after a restart or after loading a project, are opened from their files in the background when the project is loaded, so a clip played in the first moments after that may answer 404 saying they are still being opened. Playing a clip of an earlier session than the newest takes the channel to that session: steps, jumps, going to the start and other angles stay in it, and /api/replay/channel reports InEarlierSession, until the channel goes live. A clip without a session, one saved before clips kept theirs, plays from the newest recording.
At clip end. What a channel does when a clip has played to its end, once its last frame is on screen, is its AtClipEnd property, saved with the project: FreezeFrame (the default) pauses on that frame; NoOutput shows no picture, and Status reads No output, until the channel is played, stepped, cued or taken live; NextClip plays the clip after it in the clip bin's order, whatever its tag, at the speed the clip played at, from the replay recorder that clip was cut from, as /api/replay/clip/play plays it, and after the last clip pauses on its last frame; a next clip that cannot be played leaves the channel on the last frame; ReturnToLive goes live. A control made once the clip has played into its out but before that frame is on screen — a pause, a play, a stop, a step, a jump, going live or another clip — ends the clip there, and AtClipEnd is not acted on; choosing another angle keeps the moment, and the clip still ends as AtClipEnd says.
When loaded. What a channel shows once the project is loaded, from the first recording of its replay recorder until it is first played, paused, stepped, cued, taken live or given a clip, is its WhenLoaded property, saved with the project: NoOutput (the default) shows no picture, and Status reads No output, while the playhead follows live unseen, so whichever control comes first starts from the moment being recorded; FreezeFrame pauses on the newest frame recorded then; Live plays live. A project loaded into a running Composer, over this API or in Desktop, shows the same as when Composer starts with it: the recordings made before the load are no longer listed, also one a recorder is still closing, so /api/replay/recorders reports no recording until a recorder records again, and they play only as clips.
A clip's length, and midnight. A clip runs forward from its in to the next time the clock shows its out, so an out earlier in the day than the in is after midnight: 23:59:55:00 to 00:00:05:00 is a clip of ten seconds. Its Duration is that length, from the in frame to the out frame with both counted, written like a timecode: a clip whose in and out are the same frame lasts 00:00:00:01. When a clip runs past midnight, its duration has the final say on where it ends: played, it ends that many frames on from its in frame, and read from a project file, its out is counted from its in and its duration. A clip is at most 12 hours long, so an out a moment before the in is refused rather than taken for a clip of nearly a day. Timecodes and durations are counted at the project's frame rate, the rate the Replay Recorder Targets record at, and are checked against it when a clip is added or changed; the timecodes are kept as that rate writes them, with ; before the frames at 29.97 and 59.94. A change to the clip bin raises no event, only a clip's thumbnail does: read /api/replay/clip/list again after a change made elsewhere.
Timecodes are written HH:MM:SS:FF: time of day, at the recording's frame rate, counted as the SRT targets count the timecode they embed, so a timecode read off an SRT output finds the same frame here. At 29.97 and 59.94 fps they are drop-frame and read back as HH:MM:SS;FF; either separator is accepted. The labels drop-frame counting leaves out are refused, as no frame carries them: frames 00 and 01 at 29.97, 00 to 03 at 59.94, in second 0 of every minute not divisible by ten. A frame sequence is the number Composer gives every rendered frame; every angle recorded a frame under the same number, so it names one moment as exactly as a timecode does.
Live. Playing forward on a recording that is still being written stops a few frames behind the frame being rendered (the channel's Live delay (frames), 3 by default), the newest frames every recorder has surely written, and goes on from there at 1x: that is live. A step or a jump can go up to the newest frame recorded.
The same controls are on the component, for scripts and for anything that uses the generic endpoints: ReplayRecorder, Speed, CueTimecode and AtClipEnd through /api/setproperty, and PlayCommand, PauseCommand, StopCommand, StepBackCommand, StepForwardCommand, StepBack30Command, StepForward30Command, GoToStartCommand, GoLiveCommand and GoToCommand through /api/invokecommand. While no replay recorder is chosen the commands are disabled and do nothing. Otherwise they and CueTimecode report nothing back: a CueTimecode the recording does not have leaves the playhead where it was, and only the channel's log says why. CueTimecode holds a whole HH:MM:SS:FF or nothing: the colons may be left out and ; or . is taken as :, while a value that is not a whole timecode is refused and leaves it as it was, which the channel's log also says. Use the routes below to learn whether a jump was made. Setting Speed plays at that speed at once, except while the channel shows no picture (Status No output): it then stays as it is, and PlayCommand plays at that speed.
Following replays without polling. The replay events topic reports each recorder starting and stopping, with why one stopped by itself, its warning of a filling disk, each channel's replay recorder, status, speed and the frame it is still on, and each clip's thumbnail once it is ready. Over the WebSocket Components subscription, every change of what the two property panels show is pushed as it happens, the frame a channel shows and a recording's length and frame counts included; a channel's ReplayRecorder arrives as the chosen recorder's Id: all zeros when none is chosen, and when one is chosen by a name nothing has recorded yet, which replay.channel and /api/replay/channel name.
GET /api/replay/recordings
Lists the recordings that can be replayed now: those of the current recording session, and an older one while a Replay Channel shows it. A recording's files stay on disk when it is no longer listed. The recordings of a session a clip was marked in are kept and played from apart from this list, so they are not listed here.
Parameters: none.
Response: 200 OK — JSON:
{
"DateTime": "2026-10-09T19:37:20Z",
"Recordings": [
{
"ReplayRecorder": "Cam 1",
"ReplayRecorderId": "6cc00336-6ca4-4809-a44f-0e99aa0444a0",
"Folder": "D:\\Replay\\2026-10-09 213433\\Cam 1",
"IsRecording": true,
"StartedAt": "2026-10-09T19:34:33Z",
"Frames": 2749,
"FramesPerSecond": 50.0,
"FrameRate": { "Numerator": 50, "Denominator": 1 },
"Width": 1920,
"Height": 1080,
"Codec": "h264_nvenc",
"VideoCodec": "h264",
"AudioCodec": "pcm_s24le",
"AudioSampleRate": 48000,
"AudioChannels": 2,
"AudioBitsPerSample": 24,
"AudioBitRate": 0,
"FirstSequence": 1200,
"FirstTimecode": "21:34:33:08",
"NewestSequence": 3948,
"NewestTimecode": "21:35:28:06"
}
]
}
| Field | Meaning |
|---|---|
ReplayRecorder |
The recorder's name when the recording started |
ReplayRecorderId |
The Id of the recorder that made it; a renamed recorder keeps its Id, so this is what finds its recordings |
Folder |
Where the recording's segment files are |
IsRecording |
true while the recorder is still writing it |
Frames |
Frames recorded so far |
FramesPerSecond / FrameRate |
The frame rate, as a decimal and exactly: 30000 / 1001 for 29.97 |
Codec / VideoCodec |
The video encoder (h264_nvenc) and the format it writes (h264 or hevc) |
AudioCodec, AudioSampleRate, AudioChannels |
The sound: pcm_s24le in MXF or aac in MPEG-TS, 48 kHz stereo. AudioChannels is 0, and AudioCodec empty, for a recording without sound |
AudioBitsPerSample / AudioBitRate |
24 bits for PCM, 192000 bit/s for AAC; 0 where it does not apply |
FirstSequence / FirstTimecode |
The first frame recorded |
NewestSequence / NewestTimecode |
The newest frame recorded; while recording, it moves on every frame |
GET /api/replay/recorders
Lists the replay recorders a Replay Channel can show, one per angle, as the channel's Replay recorder dropdown lists them: the project's Replay Recorder Targets, whether they have recorded yet or not, then any recorder there are recordings of that is no longer in the project. An operator's page draws its angle buttons from this list, and marks the one whose ReplayRecorderId a channel reports.
Parameters: none.
Response: 200 OK — JSON:
{
"DateTime": "2026-10-12T10:15:02Z",
"ReplayRecorders": [
{
"ReplayRecorder": "Cam 1",
"ReplayRecorderId": "6cc00336-6ca4-4809-a44f-0e99aa0444a0",
"HasRecording": true,
"IsRecording": true
},
{
"ReplayRecorder": "Goal line",
"ReplayRecorderId": "7dd11447-7db5-4910-b55f-1f00bb1555b1",
"HasRecording": false,
"IsRecording": false
}
]
}
| Field | Meaning |
|---|---|
ReplayRecorder |
The recorder's name now; for a recorder no longer in the project, the name it recorded under |
ReplayRecorderId |
The recorder's Id, which /api/replay/recorder takes as well as its name; all zeros for an angle only known by the name its recordings were made under |
HasRecording |
Whether there is a recording of it to show |
IsRecording |
Whether its newest recording is still being written |
GET /api/replay/channel
Reads what a Replay Channel shows and where its playhead is, as of the last rendered frame. With no target / targetname, every Replay Channel in the project is reported.
Parameters:
| Parameter | Required | Description |
|---|---|---|
target / targetname |
No | Which channel; omit to report them all |
Response: 200 OK — JSON:
{
"DateTime": "2026-10-09T19:37:21Z",
"Channels": [
{
"Id": "c41abc44-788b-4817-a061-dbac86dc6ca0",
"Name": "Replay",
"ReplayRecorder": "Cam 1",
"ReplayRecorderId": "6cc00336-6ca4-4809-a44f-0e99aa0444a0",
"Status": "Paused",
"NoOutput": false,
"Speed": 0.0,
"IsLive": false,
"Playhead": { "Sequence": 2361, "Timecode": "21:34:57:21" },
"Shown": { "ReplayRecorder": "Cam 1", "Sequence": 2361, "Timecode": "21:34:57:21" },
"RecordingFolder": "D:\\Replay\\2026-10-09 213433\\Cam 1",
"IsRecording": true,
"FirstSequence": 1200,
"FirstTimecode": "21:34:33:08",
"NewestSequence": 3948,
"NewestTimecode": "21:35:28:06",
"Session": "2026-10-09 213433",
"InEarlierSession": false,
"PlayingClip": "00000000-0000-0000-0000-000000000000",
"ClipInSequence": -1,
"ClipOutSequence": -1,
"AtClipEnd": "FreezeFrame",
"FramesMissed": 0,
"LastError": ""
}
]
}
| Field | Meaning |
|---|---|
ReplayRecorder |
The replay recorder chosen, by the name it goes by now |
ReplayRecorderId |
Its Id, as /api/replay/recorders lists it; all zeros when none is chosen, and when one is chosen by a name nothing has recorded yet |
Status |
Live, Playing 0.5x, Reverse 1x, Paused, End of recording or Start of recording (playing, at the end or the start of a recording that has stopped), No output (after a stop, after a clip with AtClipEnd NoOutput, or since the project was loaded, with WhenLoaded NoOutput), or why there is nothing to show (no replay recorder chosen, no recording of it) |
NoOutput |
true while the channel shows no picture until it is played, paused, stepped, cued or taken live: Status is No output, after a stop, after a clip with AtClipEnd NoOutput, or since the project was loaded with WhenLoaded NoOutput. Also true for a channel stopped before it has had a recording, whose Status says there is no recording. A page that toggles play and pause should play a channel for which it is true, as its Speed is then only the speed it will play at |
Speed |
-2 to 2; 0 is paused. While NoOutput is true nothing plays, and this is the speed /api/replay/play without speed will play at: the Speed property set meanwhile, 0 for none (then 1x) |
Playhead |
The frame due now |
Shown |
The frame on screen, and the replay recorder whose recording it is from; Sequence is -1 and ReplayRecorder empty when nothing is shown |
IsRecording |
Whether the recording shown is still being written |
FirstSequence / FirstTimecode |
The first frame of the recording played, the earliest a playhead can go: a timeline's start. -1 and empty while there is no recording |
NewestSequence / NewestTimecode |
The newest frame of the recording played, the latest a step or a jump can go: a timeline's live end. While recording, it moves on every frame |
Session |
The recording session of the recording played, as a clip keeps it: relative to the Replay directory, or a full path for one outside it. Empty while there is no recording. Pass it as session to /api/replay/clip/add to mark a clip in the session the channel plays |
InEarlierSession |
true while a clip has taken the channel to a session before the newest recording's, where it stays until it goes live |
PlayingClip |
The Id of the clip of the clip bin the channel is playing, as /api/replay/clip/list lists it; all zeros while it plays none, and while a clip is cued or paused |
ClipInSequence / ClipOutSequence |
The in frame and the out frame of the clip the channel plays, or has cued or paused in, on the same scale as FirstSequence and NewestSequence: where a timeline marks the clip. Also set for a clip played by its in and out rather than from the clip bin. They are the clip's own frames, and either can lie before FirstSequence or after NewestSequence: the clip keeps them when another replay recorder is chosen, and that recorder's recording may hold only part of the clip, so draw the part of the clip that lies between the two. Both -1 while there is no clip, from the moment it plays to its end or is left by a step, a jump, going to the start, going live or a stop. Like PlayingClip, they follow a control at once rather than at the next rendered frame, but from a change of recording until a rendered frame shows the new one they read -1: just after a clip is played from another recording, such as one in an earlier session, or another replay recorder is chosen while a clip is in play |
AtClipEnd |
What the channel does when a clip has played to its end: FreezeFrame, NoOutput, NextClip or ReturnToLive |
FramesMissed |
Rendered frames that showed an earlier frame than the playhead while playing, because the frame due was not decoded in time. Above 0 during 1x playback means the GPU or the disk fell behind |
LastError |
Why the last read of a frame failed; empty once one succeeds |
GET /api/replay/play
Plays a Replay Channel at a speed: -2, -1, -0.5, -0.25, 0 (pause), 0.25, 0.5, 1 or 2. Any other value is taken to the nearest of these. A negative speed plays backwards. Playing forward into live goes on at 1x. At the end or the start of a recording that has stopped, the frame there is held at the speed set, and Status reads End of recording or Start of recording. A speed given plays at once, also while the channel shows no picture. Without speed it plays as the channel's Play button does: at 1x, or, while Status reads No output, at the speed the Speed property was set to meanwhile. A play made before the channel's recording has a frame, as just after its replay recorder starts, plays at its speed from live once there is one.
Parameters:
| Parameter | Required | Description |
|---|---|---|
target / targetname |
One of (or omit when the project has exactly one channel) | Which channel |
speed |
No | Playback speed, -2 to 2 (default 1, or the Speed set while the channel showed no picture) |
Response: 202 Accepted — <name>: speed <speed>, the speed taken. 400 when the channel cannot be chosen or speed is not a number.
GET /api/replay/pause
Pauses a Replay Channel on the frame it shows. While it plays that is the frame on screen, which can trail the playhead while a frame is being decoded. A pause made before the channel's recording has a frame, as just after its replay recorder starts, pauses at live once there is one, not on the recording's first frame.
Parameters: target / targetname as above.
Response: 202 Accepted — <name>: paused.
GET /api/replay/stop
Stops a Replay Channel: it ends the clip it plays and shows no picture, so the channel is transparent in its scene, and its sound fades out. Status reads No output until the channel is played, paused, stepped, cued or taken live. The playhead stays on the frame shown when it stopped, as a pause leaves it, so playing, pausing or stepping goes on from there. A stop made once a clip has played into its out, before that frame is on screen, ends the clip there, and AtClipEnd is not acted on. A channel stopped before it has had a recording shows nothing from its first recording on, its playhead following live unseen, as WhenLoaded NoOutput does. Setting the Speed property while stopped does not play the channel: it stays stopped, Speed reads the speed set, and PlayCommand or /api/replay/play without speed then plays at it.
Parameters: target / targetname as above.
Response: 202 Accepted — <name>: stopped.
GET /api/replay/step
Steps a number of recorded frames forward or back from the frame shown, and pauses. While it plays that is the frame on screen; paused, it is the frame cued, so steps sent faster than frames are shown add up. A frame that was not recorded is stepped over. A step stops at the first frame and at the newest frame recorded, including while recording goes on.
Parameters:
| Parameter | Required | Description |
|---|---|---|
target / targetname |
One of (or omit when the project has exactly one channel) | Which channel |
frames |
No | Frames to step: positive forward, negative back, not 0 (default 1) |
Response: 202 Accepted — <name>: stepped <frames> frame(s) and paused. 400 when frames is not a whole number other than 0. 404 when the channel has no recording to step in.
GET /api/replay/goto
Pauses a Replay Channel on the frame recorded at a time-of-day timecode or a frame sequence, or on the frame before it when that one was not recorded. A jump that cannot be made leaves the playhead where it was.
Parameters:
| Parameter | Required | Description |
|---|---|---|
target / targetname |
One of (or omit when the project has exactly one channel) | Which channel |
timecode |
One of timecode/sequence |
Time-of-day timecode, HH:MM:SS:FF |
sequence |
One of timecode/sequence |
Frame sequence |
Response: 202 Accepted — <name>: paused at <timecode or sequence>. 400 when neither or both are given, or the timecode cannot be read (hours over 23, minutes or seconds over 59, frames at or over the frame rate, or a label drop-frame counting leaves out). 404 when the recording has no frame there (before its first frame or after its newest) or there is no recording of the channel's replay recorder.
GET /api/replay/start
Pauses a Replay Channel on the first frame recorded.
Parameters: target / targetname as above.
Response: 202 Accepted — <name>: paused at the start. 404 when there is no recording of the channel's replay recorder.
GET /api/replay/live
Takes a Replay Channel live: it plays at 1x, its live delay behind the frame being rendered, in the newest recording, leaving the session a clip of an earlier one took it to. On a recording that has stopped, it pauses on the last frame.
Parameters: target / targetname as above.
Response: 202 Accepted — <name>: live.
GET /api/replay/recorder
Chooses the replay recorder a Replay Channel shows, another angle: the newest recording of the Replay Recorder Target that recorder names, by its name or its Id, at the same moment as before. The channel knows the recorder by its Id, so renaming it keeps it chosen. A name nothing has recorded yet is kept, and shown once it is recorded.
Parameters:
| Parameter | Required | Description |
|---|---|---|
target / targetname |
One of (or omit when the project has exactly one channel) | Which channel |
recorder |
Yes | The Replay Recorder Target's name or Id |
Response: 202 Accepted — <name>: replay recorder <recorder>, with (no recording of it yet) added when there is none. 400 when recorder is missing. 409 when the channel's replay recorder is locked against changes: nothing changes, and the reply names the recorder still chosen.
GET /api/replay/markin
Marks the frame a Replay Channel shows as a clip's in, as the Replay window's Mark in does: answers its timecode, the recording session and the replay recorder it is from, to pass to /api/replay/clip/add. The channel keeps the picture of that frame while it still shows it after the next rendered frame, as when it is paused or stepped there; a clip then saved from the channel with that in and session, from the same angle, gets that picture as its thumbnail at once rather than having its in frame read again (see Thumbnails). Marking changes nothing the channel shows or does.
Parameters:
| Parameter | Required | Description |
|---|---|---|
target / targetname |
One of (or omit when the project has exactly one channel) | Which channel |
Response: 200 OK — JSON:
{ "Timecode": "21:34:51:10", "Sequence": 2361, "Session": "2026-10-11 182030", "ReplayRecorder": "Cam 1" }
Timecode and Sequence are the frame on screen, Session the recording session of that frame, and ReplayRecorder the replay recorder whose recording the frame is from. Session is the one /api/replay/channel reports, except just after the channel has gone on to another session, while that session's first frame is still being read: the frame on screen is then still the session before's, and so is the mark. 404 when the channel shows no recorded frame.
GET /api/replay/clip/list
Lists the clips in the clip bin, in the order they were added; with tag, only the clips with that tag.
Parameters:
| Parameter | Required | Description |
|---|---|---|
tag |
No | Only the clips with this tag, whatever its case; omit or leave empty for every clip |
Response: 200 OK — JSON:
{
"DateTime": "2026-10-11T18:30:02Z",
"Clips": [
{
"Id": "0f8f6e0a-3c1d-4f57-9a43-5b2f0b1f7c21",
"Name": "Goal by 10",
"Tag": "Goal",
"In": "21:34:51:10",
"Out": "21:35:02:00",
"Duration": "00:00:10:41",
"CreatedDateTime": "2026-10-11T18:29:40.1234567Z",
"CreatedOnHostName": "REPLAY-01",
"Session": "2026-10-11 182030",
"ReplayChannelId": "5d7a9c1e-2b4f-4c88-9e10-7f3a2b6c4d90",
"ReplayRecorderId": "8e2b4f61-9a0c-4d3e-b7f5-1c6d8a2e3b47",
"ReplayRecorder": "Cam 1",
"ThumbnailReplayRecorderId": "8e2b4f61-9a0c-4d3e-b7f5-1c6d8a2e3b47",
"ThumbnailReplayRecorder": "",
"Thumbnail": "Ready",
"ThumbnailVersion": "3fa91c07",
"ThumbnailReason": ""
}
]
}
| Field | Meaning |
|---|---|
Id |
Names the clip in the other clip routes |
Tag |
Empty for a clip without one |
In / Out |
Time-of-day timecodes as the project's frame rate writes them, with ; before the frames at 29.97 and 59.94. An Out earlier in the day than the In is after midnight |
Duration |
From the in frame to the out frame, both counted, written like a timecode |
CreatedDateTime |
When the clip was added, UTC. It stays when the clip is changed |
CreatedOnHostName |
The name of the computer the clip was added on. It stays when the clip is changed, and when the project is opened on another computer |
Session |
The recording session the clip was marked in, relative to the Replay directory, as /api/replay/channel reports it; empty for a clip that plays from the newest recording |
ReplayChannelId |
The Replay Channel the clip was saved from, whose folder in the thumbnail cache keeps its thumbnail; all zeros for a clip saved from none. It stays when the clip is changed |
ReplayRecorderId |
The Replay Recorder Target the clip was cut from: the replay recorder, the angle, the channel showed when the clip was saved. The clip plays from it unless another is asked for (see /api/replay/clip/play). All zeros for an angle no replay recorder in the project records, which ReplayRecorder then names, and when none was known. It stays when the clip is changed |
ReplayRecorder |
The name that replay recorder went by when the clip was saved, so a recorder renamed or deleted since is still named; empty when none was known. In a project saved by an earlier version, a clip's recorder is taken from its thumbnail's angle, which kept a recorder's Id but not its name. It stays when the clip is changed |
ThumbnailReplayRecorderId |
The replay recorder, the angle, the thumbnail is taken from: the one the channel showed when the clip was saved. All zeros for an angle no replay recorder in the project records, which ThumbnailReplayRecorder then names, and when none was known; the thumbnail then comes from the first replay recorder that recorded the in frame. It stays when the clip is changed |
ThumbnailReplayRecorder |
The name of the angle the thumbnail is taken from when no replay recorder in the project records it, as for the recordings of a recorder since deleted; empty otherwise. It stays when the clip is changed |
Thumbnail |
Where the clip's thumbnail stands: Pending while it is being made or its in frame is not recorded yet, Ready to fetch from /api/replay/clip/thumbnail, Unavailable when it cannot be made |
ThumbnailVersion |
Changes with what the thumbnail shows: its session, its in and its angle. The replay.clip_thumbnail event and the thumbnail's ETag carry it, so a page can keep a thumbnail under its version |
ThumbnailReason |
Why the thumbnail cannot be made, such as no recording having a frame at the clip's in; empty otherwise |
GET /api/replay/clip/add
Adds a clip to the clip bin. It runs forward from the in, so an out earlier in the day than the in is after midnight. A tag not yet in the tag list is added to it. The clip is saved from the Replay Channel named, or the project's only one, and cut from the replay recorder that channel shows now, whose Id and name it keeps: its thumbnail is made in the background from that angle (see Thumbnails). Naming no channel in a project with several saves the clip from none, and its thumbnail comes from the first replay recorder that recorded the in frame.
Parameters:
| Parameter | Required | Description |
|---|---|---|
target / targetname |
No | The Replay Channel the clip is saved from; omit for the project's only one, or none when there are several |
name |
Yes | The clip's name |
in |
Yes | In point, a time-of-day timecode HH:MM:SS:FF |
out |
Yes | Out point, a time-of-day timecode HH:MM:SS:FF: the same as the in for a clip of one frame, earlier in the day than the in for a clip that runs past midnight |
tag |
No | Any text; omit or leave empty for no tag |
session |
No | The recording session the clip is marked in, as /api/replay/channel gives it in Session. Omit it for the session the recorders last recorded into; leave it empty for none, so the clip plays from the newest recording |
Response: 200 OK — the clip, as one entry of /api/replay/clip/list, with its Id and Duration, and its Thumbnail usually still Pending. 400 when name, in or out is missing or the name is empty, a timecode cannot be read at the project's frame rate (frames at or over the frame rate, or a label drop-frame counting leaves out), the clip would be longer than 12 hours, or target or targetname names no Replay Channel; nothing is added.
GET /api/replay/clip/thumbnail
Gets a clip's thumbnail: its in frame as a JPEG 480 pixels wide, its height following the picture's shape, from the angle the clip was saved from (see Thumbnails). Fetch it once /api/replay/clip/list says Ready or the replay.clip_thumbnail event has announced it. A thumbnail never changes under its version, so the response may be kept: its ETag is the clip's ThumbnailVersion, and it is sent with Cache-Control: private, max-age=31536000, immutable. Put the version in the URL, as in ?clip=<Id>&v=<ThumbnailVersion>, so that a browser fetches the new thumbnail once the clip's in changes. The apikey query parameter works here too, so an <img> tag can show it. The call is answered without waiting for a rendered frame, so a page may fetch many thumbnails at once.
Parameters:
| Parameter | Required | Description |
|---|---|---|
clip |
Yes | The clip's Id |
Response: 200 OK — the JPEG (Content-Type: image/jpeg). 400 when clip is missing or not an Id. 404 when no clip has the Id, or its thumbnail is not made yet (is not made yet) or cannot be made (cannot be made: and why).
GET /api/replay/clip/set
Renames a clip, retags it, changes its in or out, and its duration with them, or the recording session it was marked in. Only what is given changes: a rename keeps the tag, the timecodes and the session. An empty tag takes the clip's tag away, and an empty session its session; a new tag is added to the tag list. Everything is checked before anything changes, so a call refused leaves the clip as it was.
Parameters:
| Parameter | Required | Description |
|---|---|---|
clip |
Yes | The clip's Id |
name |
One of these five | New name |
tag |
One of these five | New tag; empty for none |
in |
One of these five | New in point, HH:MM:SS:FF |
out |
One of these five | New out point, HH:MM:SS:FF |
session |
One of these five | New recording session, relative to the Replay directory; empty for none |
Response: 200 OK — the clip as it is now. 400 when clip is missing or not an Id, none of the five is given, the name would be empty, a timecode cannot be read at the project's frame rate, or the clip would be longer than 12 hours. 404 when no clip has the Id.
GET /api/replay/clip/delete
Deletes a clip from the clip bin. Its tag stays in the tag list.
Parameters:
| Parameter | Required | Description |
|---|---|---|
clip |
Yes | The clip's Id |
Response: 200 OK — clip <name> deleted. 400 when clip is missing or not an Id. 404 when no clip has the Id.
GET /api/replay/clip/play
Plays a clip on a Replay Channel, from the recording of the session it was marked in: the newest recording for a clip without one, and for a clip of an earlier session, that session's, where the channel then stays until it goes live (see Recording sessions). It starts on the frame recorded at the clip's in and plays to the frame recorded at its out, the frame before either when that one was not recorded, and there does what the channel's AtClipEnd says: by default it pauses on it. The clip waits on its first frame until that frame is on screen, however long it takes to read, so the moment the clip was marked from is always shown; Status reads as playing meanwhile. A negative speed plays the clip backwards, from its out to its in, and 0 cues it, paused on its in, to play when a speed is set with /api/replay/play. Pausing and changing the speed keep the clip, so it still ends on its out; a step, a jump, going to the start, going live or another clip end it. The clip plays from the replay recorder it was cut from, its ReplayRecorderId (or, for an angle no replay recorder records, its ReplayRecorder), whichever angle the channel shows: the channel changes to that recorder, which stays chosen afterwards, as /api/replay/recorder chooses it. With recorder, the clip plays from that replay recorder instead, another angle. A clip cut from none plays on the angle shown. The recorder is chosen only once the clip is found in its recording, so a call that cannot play the clip leaves the angle as it was. Choosing another angle while the clip plays shows the same moment from there, and the clip plays on. The clip plays from the recording it was found in: if the replay recorder starts a new recording while the clip plays, the channel shows the new one once the clip has ended and its last frame has been on screen. A step, a jump, going to the start or going live meanwhile ends the clip and is made in the new recording: a jump to a moment the new recording does not have answers 404, and the clip plays on. A clip that runs past midnight, its out earlier in the day than its in, ends where its duration says: that many recorded frames on from its in frame. An out among the newest frames of a recording still being written is reached once it is live: the channel waits at live and then pauses on it.
Parameters:
| Parameter | Required | Description |
|---|---|---|
target / targetname |
One of (or omit when the project has exactly one channel) | Which channel |
clip |
Yes | The clip's Id |
recorder |
No | The Replay Recorder Target's name or Id; omit to play from the one the clip was cut from |
speed |
No | Playback speed, -2 to 2 (default 1), taken to the nearest the channel plays at; 0 cues the clip |
Response: 202 Accepted — <name>: playing clip <clip> on <recorder>, <in> to <out>, at speed <speed>, or <name>: clip <clip> cued on <recorder> at <in>. 400 when the channel cannot be chosen, clip is missing or not an Id, speed is not a number, recorder is empty, or a timecode of the clip cannot be read at the recording's frame rate (frames at or over the frame rate, or a label drop-frame counting leaves out). 404 when no clip has the Id, there is no recording of the replay recorder in the clip's session (or the session's recordings are still being opened, just after the project is loaded), or it has no frame at the clip's in, or at its out after the in (past midnight: the recording ends before the clip's duration does). 409 when the clip is to play from another replay recorder than the one chosen, the one recorder names or the clip's own, and the channel's replay recorder is locked. A call refused with 400, 404 or 409 leaves the angle and the playhead as they were.
GET /api/replay/tag/list
Lists the tag list, in the order the tags were added.
Parameters: none.
Response: 200 OK — JSON: { "DateTime": "2026-10-11T18:30:02Z", "Tags": ["Goal", "Save", "Chance", "Foul", "Penalty", "Yellow card", "Red card", "Highlight"] }.
GET /api/replay/tag/add
Adds a tag to the end of the tag list, unless a tag that differs from it only in case is there already.
Parameters:
| Parameter | Required | Description |
|---|---|---|
tag |
Yes | The text of the tag |
Response: 200 OK — JSON: { "Tag": "Offside", "Added": true, "Tags": [ … ] }: the tag as listed, whether it was added (false when it was there already, spelled as listed), and the tag list. 400 when tag is missing or empty.
Examples
All examples assume the host localhost:44433 and a channel named Replay 1; add ?apikey=<key> (or an apikey header) if your instance requires one.
# What can be replayed
curl "http://localhost:44433/api/replay/recordings"
curl "http://localhost:44433/api/replay/recorders"
# Cue Cam 2 at a moment, step two frames back, roll at half speed, then return to live
curl "http://localhost:44433/api/replay/recorder?targetname=Replay%201&recorder=Cam%202"
curl "http://localhost:44433/api/replay/goto?targetname=Replay%201&timecode=21:34:57:21"
curl "http://localhost:44433/api/replay/step?targetname=Replay%201&frames=-2"
curl "http://localhost:44433/api/replay/play?targetname=Replay%201&speed=0.5"
curl "http://localhost:44433/api/replay/live?targetname=Replay%201"
# What is on screen
curl "http://localhost:44433/api/replay/channel?targetname=Replay%201"
# Save a clip, tagged with a tag of your own, and play it from Cam 2 at half speed
curl "http://localhost:44433/api/replay/clip/add?name=Offside%20trap&tag=Offside&in=21:34:51:10&out=21:35:02:00"
curl "http://localhost:44433/api/replay/clip/play?targetname=Replay%201&clip=<Id>&recorder=Cam%202&speed=0.5"
# The goals so far, and a clip's out moved two seconds on
curl "http://localhost:44433/api/replay/clip/list?tag=Goal"
curl "http://localhost:44433/api/replay/clip/set?clip=<Id>&out=21:35:04:00"
# A clip marked and saved from Replay 1, paused on its in, and its thumbnail once it is ready
curl "http://localhost:44433/api/replay/markin?targetname=Replay%201"
curl "http://localhost:44433/api/replay/clip/add?targetname=Replay%201&name=Save&in=21:40:10:00&out=21:40:14:00"
curl -o save.jpg "http://localhost:44433/api/replay/clip/thumbnail?clip=<Id>&v=<ThumbnailVersion>"