Remote Control¶
Control the Milestone XProtect Smart Client remotely via a REST API with interactive Swagger UI documentation. External systems, automation scripts, or control room software can switch views, display cameras, change workspaces, and more - all over HTTP.
Quick Start¶
- Install the plugin and open Smart Client
- Go to Settings > Remote Control
- The server starts automatically on
127.0.0.1:9500 - Click Open Swagger UI to explore the API interactively
- Copy your API token and click Authorize in Swagger UI to authenticate
API Endpoints¶
All endpoints require a Bearer token in the Authorization header. Use the Swagger UI Authorize button or pass it directly:
Discovery¶
| Method | Path | Description |
|---|---|---|
GET |
/api/views |
List all views with FQID and path |
GET |
/api/cameras |
List all cameras with FQID and group path |
GET |
/api/workspaces |
List all workspaces |
GET |
/api/windows |
List Smart Client windows |
GET |
/api/status |
Server status and current SC mode |
Use the id field from discovery endpoints in all action requests.
Actions¶
| Method | Path | Description |
|---|---|---|
POST |
/api/views/switch |
Switch to a view |
POST |
/api/cameras/show |
Show N cameras with auto-layout |
POST |
/api/cameras/set |
Set a camera in a specific view slot |
POST |
/api/workspaces/switch |
Switch workspace |
POST |
/api/application/control |
Application commands |
POST |
/api/windows/close |
Close window(s) |
POST |
/api/clear |
Clear/blank the view |
Dynamic Camera Layout¶
Send an array of camera IDs to /api/cameras/show and a grid view is automatically created:
The smallest grid layout that fits is selected automatically (1x1, 1x2, 2x2, 2x3, 3x3, up to 4x5 = 20 cameras max). Grid views are created in a "Remote Control" folder under Private Views.
Application Commands¶
Available values for POST /api/application/control:
| Command | Description |
|---|---|
ToggleFullscreen |
Toggle fullscreen mode |
EnterFullscreen |
Enter fullscreen |
ExitFullscreen |
Exit fullscreen |
ShowSidePanel |
Show the side panel |
HideSidePanel |
Hide the side panel |
Maximize |
Maximize the window |
Minimize |
Minimize the window |
Restore |
Restore the window |
Delayed Clear¶
Clear a view after a delay (useful for temporarily showing cameras then returning to blank):
Maximum delay is 300 seconds (5 minutes).
Settings¶
Open Settings > Remote Control in the Smart Client.
Server Status¶
Shows whether the server is running, the listen URL, and any errors. Buttons:
- Restart Server - Applies settings and restarts the HTTP server
- Open Swagger UI - Opens the interactive API documentation in the browser
Network Configuration¶
| Setting | Description | Default |
|---|---|---|
| Listen Interface | Network interface to bind to | 127.0.0.1 (loopback) |
| Port | HTTP port | 9500 |
| Use HTTPS | Enable TLS encryption | Off |
| PFX Certificate | Certificate file for HTTPS (.pfx format) |
- |
| PFX Password | Password for the PFX file | - |
Listen Interface
The default 127.0.0.1 only allows connections from the local machine. Select All Interfaces (0.0.0.0) or a specific IP to allow remote access. When using 0.0.0.0, ensure your firewall is configured appropriately.
HTTPS
When HTTPS is disabled, API tokens are sent in plaintext over the network. Use HTTPS when the listen interface is not loopback, or ensure the network is trusted.
API Tokens¶
Tokens authenticate API requests. At least one token is always required.
- Add Token - Generate a new random 256-bit token
- Copy - Copy the token value to clipboard
- Remove - Delete a token (cannot remove the last one)
Security¶
- Authentication: All
/api/*endpoints require a valid Bearer token. Swagger UI is accessible without authentication for convenience. - CORS: Only same-origin requests are allowed from browsers. Non-browser HTTP clients (scripts, automation) work from any machine.
- Token storage: API tokens and PFX passwords are encrypted at rest using Windows DPAPI.
- Default loopback: The server defaults to
127.0.0.1, limiting access to the local machine until explicitly configured otherwise.
Example: Python¶
import requests
BASE = "http://localhost:9500"
TOKEN = "your-token-here"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
# List cameras
cameras = requests.get(f"{BASE}/api/cameras", headers=HEADERS).json()
for cam in cameras:
print(f"{cam['name']} ({cam['id']})")
# Show first 4 cameras in a 2x2 grid
ids = [cam["id"] for cam in cameras[:4]]
requests.post(f"{BASE}/api/cameras/show",
json={"cameraIds": ids}, headers=HEADERS)
# Clear after 10 seconds
requests.post(f"{BASE}/api/clear",
json={"delaySeconds": 10}, headers=HEADERS)
Example: Go¶
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const (
base = "http://localhost:9500"
token = "your-token-here"
)
func api(method, path string, body any) (map[string]any, error) {
var reqBody io.Reader
if body != nil {
b, _ := json.Marshal(body)
reqBody = bytes.NewReader(b)
}
req, _ := http.NewRequest(method, base+path, reqBody)
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
return result, nil
}
func main() {
// List cameras
resp, _ := api("GET", "/api/cameras", nil)
fmt.Println(resp)
// Show 2 cameras in auto-layout
api("POST", "/api/cameras/show", map[string]any{
"cameraIds": []string{"cam-guid-1", "cam-guid-2"},
})
// Clear after 10 seconds
api("POST", "/api/clear", map[string]any{
"delaySeconds": 10,
})
}
Example: PowerShell¶
$base = "http://localhost:9500"
$headers = @{ Authorization = "Bearer your-token-here" }
# List views
$views = Invoke-RestMethod -Uri "$base/api/views" -Headers $headers
$views | Format-Table name, path
# Switch to first view
$body = @{ viewId = $views[0].id } | ConvertTo-Json
Invoke-RestMethod -Uri "$base/api/views/switch" -Method POST `
-Headers $headers -ContentType "application/json" -Body $body