Web Control API
The control surface is an HTTP web API, a raw TCP control socket for Stream Deck, and a vector:// link scheme.
What Vector Serves
Section titled “What Vector Serves”Vector listens on two ports. One serves the Web Control API once enabled, and the other the Stream Deck plugin over a raw TCP socket.
The API answers data — JSON, plain text, or an MJPEG stream.
Except for that route index, the routes are shared with Laika, so a script for one works on the other.
Enabling the API
Section titled “Enabling the API”The switch is the preference web_api_enabled, off by default. Three things turn it on. See Preferences for that window.
- The Enable Web API checkbox. Menu → File → Preferences → API tab, section Web Control API. The help under it shows REST API for remote control. Requires restart to apply changes.
PATCH /api/preferences. Settingweb_api_enabled,web_api_portorweb_api_bindhere rebinds the listener.- Installing the Stream Deck plugin. With
vector.sdPluginand a readablemanifest.jsonin the plugins folder, Vector sets the preference, saves it and rebinds.
VECTOR_STREAMDECK is a separate switch for the control socket.
Ports and Bind Address
Section titled “Ports and Bind Address”| Surface | Port | Where the number comes from |
|---|---|---|
| Web Control API | 1893 | the Port: field in Preferences |
| Stream Deck control socket | 1894 | the port the plugin dials |
The ports must differ. Change the web port under Menu → File → Preferences → API → Port:.
Menu → File → Preferences → API → Answer on: offers All network interfaces and This machine only.
All network interfaces binds 0.0.0.0:{port}, and anything that can reach the machine can drive Vector.
This machine only binds 127.0.0.1:{port}, and only a program on this machine can reach it.
The listener prints Web API listening on http://{addr}, and a change takes effect the same frame.
Add a firewall rule on a network you do not trust, and leave the API off when you do not need it.
Vector’s command line accepts four words, and --help lists them. Anything else is answered once and then ignored:
vector: ignoring unknown argument `--web-api-port` — try --helpEvery JSON answer has these headers.
| Header | Value |
|---|---|
Content-Type | application/json |
Cache-Control | no-store, no-cache, must-revalidate |
Access-Control-Allow-Origin | * |
Access-Control-Allow-Methods | GET, POST, PATCH, DELETE, OPTIONS |
Access-Control-Allow-Headers | Content-Type |
The MJPEG stream sends its own headers, including Access-Control-Allow-Origin: *. OPTIONS is answered for any path with 200, an empty body and the same headers.
/api/text*has its own preflight. There,OPTIONSis204, the body is{}, and the headers areAccess-Control-Allow-Origin: *andAccess-Control-Allow-Methods: GET, POST, OPTIONS.- Use a tool, not a browser page, for the index.
GET /apihas noAccess-Control-Allow-Origin, and a page on another origin can reach every other route.
The Route Index
Section titled “The Route Index”GET /api is Vector’s own route. It lists every route the API answers, shared and Vector’s own.
{ "product": "vector", "routes": [ {"method":"GET","origin":"shared","path":"/api/status","summary":"The application's status document."}, {"method":"GET","origin":"vector","path":"/api","summary":"This document: every route the API answers, shared and Vector's own."} ]}origin is "shared" or "vector", and shared routes come first. A route is listed only when the API answers it, so every path is callable.
The index has 42 entries: the 41 shared routes, and GET /api itself.
The Route Reference
Section titled “The Route Reference”Every route below is answered and listed by Vector, and the right-hand column is that route’s own summary text.
| Method | Path | What it does |
|---|---|---|
| GET | /api/status | The application’s status document. |
| GET | /api/snapshot | A headless snapshot of what is on screen. |
| GET | /api/sources | The sources discovery can see. |
| GET | /api/viewers | Every viewer’s assignment. |
| POST | /api/viewers/{index} | Assign a source to a viewer. |
| DELETE | /api/viewers/{index} | Clear a viewer. |
| POST | /api/viewers/{index}/caption | Set or clear a viewer’s caption. |
| POST | /api/viewers/{index}/bandwidth | Set a viewer’s bandwidth. |
| PATCH | /api/viewers/{index}/stereo_pairs | Set a viewer’s stereo pairs. |
| GET | /api/audio/input-devices | The audio input devices this machine has. |
| POST | /api/audio/output | Send a viewer’s audio to the output. |
| DELETE | /api/audio/output | Clear the output audio. |
| POST | /api/layout | Switch to a layout by name. |
| GET | /api/layouts | The layouts this install has. |
| GET | /api/layouts/designer | The designer layouts. |
| POST | /api/layouts/designer | Create a designer layout. |
| PATCH | /api/layouts/designer/rename | Rename a designer layout. |
| PATCH | /api/layouts/designer/{name} | Update a designer layout. |
| DELETE | /api/layouts/designer/{name} | Delete a designer layout. |
| GET | /api/output | The output’s status. |
| POST | /api/output | Enable or disable the output. |
| POST | /api/project/{index} | Project a viewer fullscreen. |
| DELETE | /api/project | Exit projection. |
| GET | /api/license | The license’s status. |
| POST | /api/license/activate | Activate a license key. |
| POST | /api/license/deactivate | Deactivate the license. |
| GET | /api/preferences | The application’s preferences. |
| PATCH | /api/preferences | Change the preferences. |
| POST | /api/still | Save a still of a source or the window. |
| POST | /api/shutdown | Shut the application down gracefully. |
| GET | /api/stream | The MJPEG preview stream. |
| GET | /api/text | The default text API value. |
| GET | /api/text/{id} | A text API value. |
| POST | /api/text/{id} | Set a text API value. |
| GET | /api/text_apis | The text API endpoints. |
| PUT | /api/text_apis | Replace the text API endpoints. |
| PATCH | /api/text_apis | Change the text API endpoints. |
| POST | /api/text_apis/{id}/send | Send a value to a text API endpoint. |
| POST | /api/iso/start | Start ISO recording a viewer. |
| POST | /api/iso/stop | Stop ISO recording. |
| GET | /api/iso/status | The ISO recording state. |
Notes on Individual Routes
Section titled “Notes on Individual Routes”GET /api/viewersshows 5 slots.POST /api/shutdownanswers{"message":"Headless shutdown requested","ok":true}, and nothing else. Quit from the File menu, or close the window.- The three
/api/iso/*routes.POST /api/iso/startandPOST /api/iso/stopanswer501{"error":"ISO recording not enabled in this build","ok":false}, andGET /api/iso/statusanswers200{"recording":[]}. GET /api/statushaswebrtcasfalsein Vector, alongsidelicensed,versionand the output flags.POST /api/outputneedsenabled, which sets the direction. A request that omitsenabled, spells it differently, or gives a value that is not true or false is refused with400{"error":"Missing 'enabled' field","ok":false}.trueswitches it on,falseswitches it off.
Routes Outside the Index
Section titled “Routes Outside the Index”Nine routes the API answers stay out of the index: /api/launchpads, /api/launchpad, /api/launchpad/ndi, /api/launchpad/decklink, /api/launchpad/enabled, /api/launchpad/audio and /api/discovery. Names are filtered by path, so both methods of /api/launchpad and of /api/discovery go with them.
A client that calls one of these gets the same answer Laika gives. A Vector session does have one pad, with the layout and its sources, so /api/launchpads shows it. /api/discovery uses your NDI® configuration file.
The MJPEG Stream
Section titled “The MJPEG Stream”GET /api/stream is the HTTP listener’s one server-side push. It answers HTTP/1.1 200 OK with its own headers: the media type multipart/x-mixed-replace with boundary=frame, plus Access-Control-Allow-Origin: *, Cache-Control: no-cache, no-store and Connection: keep-alive. A part goes out every ~100 ms, each Content-Type: image/jpeg with its Content-Length.
The connection stays open until the client disconnects.
With the API enabled and a page live, Vector scales the composed picture to 960x540 and encodes a JPEG. The stream is the composed page at half resolution, without any scope tile’s picture.
The Control Socket
Section titled “The Control Socket”This is where the Stream Deck plugin talks, over a raw TCP socket, not HTTP. Vector exchanges one JSON object per line.
The version gate is the protocol number, 2. Any other number in a Hello is refused, with a sentence giving the number Vector uses. A line may not exceed 64 KiB, and a longer one is answered message longer than 65536 bytes.
Three replies:
{"kind":"ok","id":1,"feedback":{"active":false}}{"kind":"error","id":1,"message":"no action is named `Quit`"}{"kind":"options","id":1,"options":[{"value":"studio","label":"Studio A"}],"selected":"studio"}And one push:
{"kind":"state","context":"<tile>","feedback":{"active":false}}Every feedback object has active, plus title, readout and indicator when they exist. A push goes to every connected surface. Vector checks each subscribed tile continually and pushes only on a change. With no client it sends nothing and stores nothing.
A Poll subscribes its tile, a Release unsubscribes, and a surface that disconnects has all its tiles forgotten.
The Stream Deck Plugin
Section titled “The Stream Deck Plugin”The plugin is a compiled program that Stream Deck runs directly. It calls itself Fetch Media Tools | Vector, and it needs Stream Deck 6.0 or later, on macOS 10.15 or Windows 10.
Install it from Menu → File → Install Stream Deck Plugin. The entry is offered even where Stream Deck is not installed, and answers “Stream Deck is not installed on this machine”.
A click installs at the click and opens the Stream Deck Plugin window, one label and one Close button, showing Nothing has been installed yet. before anything is asked.
| Platform | Install location |
|---|---|
| macOS | $HOME/Library/Application Support/com.elgato.StreamDeck/Plugins/vector.sdPlugin |
| Windows | %APPDATA%\Elgato\StreamDeck\Plugins\vector.sdPlugin |
Any click on the entry installs. It replaces whatever is there, older or newer. The refusals you can meet are the protocol number above and a stale saved port, answered the saved control port {n} is not the port this socket uses, so 1894 is being dialled instead.
The plugin offers 19 actions plus the connection tile Vector Connection, from a catalogue of 93 in six categories. One more, Load layout at index, is retired but still served. Eight of them work on a dial, and the dial turns four properties.
| Dial property | Range | Default | Step per detent |
|---|---|---|---|
Global brightness | 0.0-4.0 | 1.5 | 0.05 |
Scope Zoom Reset | 0.25-8.0 | 1.0 | 0.25 |
Waveform zoom | 0.5-20.0 | 1.0 | 0.5 |
Snapshot blend | 0-100 | 50 | 5 |
A readout is the value, as 50 % or 1.00x, with an indicator from 0 to 100. A turn sets the value on the scope it is aimed at, the same value the scope properties pane edits and the layout saves. Global brightness is per-scope, and a dial is how you reach it. It scales the finished trace, while Gain magnifies what the scope measured.
The vector:// URI Scheme
Section titled “The vector:// URI Scheme”The grammar is vector://action/<identifier>?index=<n>&text=<value>, with index first. You can leave the action/ segment out. Unknown keys and repeated keys are malformed, and every byte outside A-Za-z0-9-_.~ is percent-encoded.
| Link | Meaning |
|---|---|
vector://action/Quit | the Quit action |
vector://Quit | the short form of the same thing |
vector://action/LoadLayoutIndex?index=2 | load the layout at index 2 |
vector://action/LoadLayout?text=Studio%20A | load the layout called Studio A |
vector://action/ConnectDeviceAtSlot?index=3&text=DeckLink%201 | connect a named device at slot 3 |
Refused forms include "", "Quit", an omniscope:// link, vector://, vector://action/, vector://other/Quit, vector://action/Quit?index=0, vector://action/Quit?index=abc, an unknown key such as ?slot=2, a repeated index or text, and a truncated percent escape.
Put a vector:// link on a Stream Deck key. A mapping has the link, and Stream Deck hands it to Vector. No operating system registers the scheme, so a link pasted into a browser does nothing.
The Error Shape
Section titled “The Error Shape”The web API answers with one JSON object and a status from one table.
{"error":"Not found","ok":false}| Status | Meaning | Example body |
|---|---|---|
400 | bad request | Missing 'source' field |
402 | payment or license | license required, in the table, though nothing you can reach returns it |
404 | no such route | Not found |
405 | wrong method | Method not allowed |
409 | conflict | the route’s own sentence |
500 | internal error | the route’s own sentence |
501 | not implemented | {"error":"ISO recording not enabled in this build","ok":false} |
503 | shutting down | App shutting down |
504 | timeout | Timeout |
Some sentences come from the API itself, before any route acts. All are 400 except Method not allowed, which is 405. They include Missing 'name' field, Missing 'source' field, Missing 'viewer' field, Missing 'license_key' field, Missing 'old_name' or 'new_name', Missing 'name' or 'enabled', Invalid viewer index, Method not allowed, 'pairs' must be an array of 8 booleans, Missing or invalid 'mode' (use global/full/proxy), Missing text API endpoint id and Missing layout name.
The control socket uses the same idea in its own shape: {"kind":"error","id":<n>,"message":"<sentence>"}. Each sentence wraps the interpolated name in backticks.
no action is named `{id}``{action}` needs {a} {expected}`{action}` does not take a {supplied}`{action}` was given an empty {expected}`{action}` takes an index in {min}..={max}, not {value}`{action}` has no dial behaviour, so it cannot be put on a dial`{uri}` is not a Vector action linkthis build cannot perform `{action}`this build cannot perform `{action}`: {reason}`{action}` could not be performed: {reason}The dispatcher also answers no button is bound to channel {channel} and no dial is bound to channel {channel}, and the connection answers the two protocol sentences.
Worked Examples
Section titled “Worked Examples”curl http://localhost:1893/apicurl http://localhost:1893/api/status
curl -X POST http://localhost:1893/api/viewers/0 \ -H "Content-Type: application/json" \ -d '{"source":"CAMERA-1"}'
curl http://localhost:1893/api/licenseThe payloads and their fields mean the same in both products. Working with Scopes covers the scope tiles you are driving, and QC and the Error Log covers what Vector records about them.