All Synapse API endpoints are under the /api/ncp/ path. Every endpoint requires authentication via Bearer token and is rate-limited. Errors follow the RFC 7807 problem details format.
All requests must include an Authorization header:
Authorization: Bearer <access_token>
The authenticated user must be a member of an organization. The organization_id is resolved automatically from the user’s membership.
All errors return RFC 7807 problem details:
{
"type": "https://httpstatuses.io/400",
"title": "Bad Request",
"status": 400,
"detail": "Validation failed",
"errors": [...]
}
GET /api/ncp/devices
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
device_class | string | - | Filter by device class |
status | string | - | Filter by status (online, offline, error, maintenance, discovering, pending_approval) |
site_id | UUID | - | Filter by site |
network_segment_id | UUID | - | Filter by network segment |
search | string | - | Full-text search on display_name (max 200 chars) |
approved | boolean | - | Filter by approval status |
include_deleted | boolean | false | Include soft-deleted devices |
page | integer | 1 | Page number (min 1) |
limit | integer | 50 | Items per page (min 1, max 100) |
sort_by | string | created_at | Sort field: display_name, status, health_score, last_heartbeat_at, device_class, created_at |
sort_order | string | desc | asc or desc |
Response:
{
"data": {
"data": [
{
"id": "uuid",
"display_name": "Studio A - Camera 1",
"device_class": "ptz_camera",
"status": "online",
"health_score": 95,
"ip_address": "10.0.1.50",
"capabilities": { "video_input": true, "ptz": true },
"approved": true,
"created_at": "2026-02-08T12:00:00Z"
}
],
"total": 42,
"page": 1,
"limit": 50,
"has_more": false
}
}
POST /api/ncp/devices
Request Body:
{
"display_name": "Studio A - Camera 1",
"device_class": "ptz_camera",
"ip_address": "10.0.1.50",
"mac_address": "00:1A:2B:3C:4D:5E",
"hostname": "cam1.studio-a.local",
"capabilities": { "video_input": true, "ptz": true, "tally": true },
"site_id": "uuid",
"physical_location": "Studio A, Position 1",
"failover_mode": "automatic",
"backup_device_id": "uuid"
}
Required fields: display_name, device_class
Response: 201 Created with the full device object.
GET /api/ncp/devices/{id}
Response: 200 OK with the full device object, or 404 Not Found.
PATCH /api/ncp/devices/{id}
Request Body: Any subset of updatable fields:
{
"display_name": "Updated Name",
"capabilities": { "video_input": true, "ptz": true, "tally": true },
"failover_mode": "manual"
}
Response: 200 OK with the updated device object.
DELETE /api/ncp/devices/{id}
Sets deleted_at and deleted_by. The device is excluded from listings unless include_deleted=true.
Response:
{
"data": { "message": "Device decommissioned", "id": "uuid" }
}
POST /api/ncp/devices/{id}/approve
Approves a pending device for routing.
Response: 200 OK with the approved device object.
POST /api/ncp/devices/{id}/control
Request Body:
{
"command": "ptz_move",
"params": { "pan": 45, "tilt": -10, "zoom": 2.5, "speed": 0.5 }
}
Response:
{
"data": {
"success": true,
"response": { "acknowledged": true }
}
}
GET /api/ncp/devices/{id}/tally
Response:
{
"data": {
"state": "program",
"production_id": "uuid",
"production_name": "Evening News"
}
}
GET /api/ncp/discovery
Returns the 50 most recent discovery sessions, ordered by created_at descending.
Response:
{
"data": [
{
"id": "uuid",
"protocols_scanned": ["mdns", "dante_discovery"],
"devices_found": 12,
"devices_approved": 8,
"status": "completed",
"started_at": "2026-02-08T10:00:00Z",
"completed_at": "2026-02-08T10:02:30Z"
}
]
}
POST /api/ncp/discovery
Request Body:
{
"protocols": ["mdns", "dante_discovery", "usb_enumeration"],
"segment_ids": ["uuid"]
}
Required fields: protocols (array with at least one entry)
Response: 201 Created with the discovery session object.
GET /api/ncp/discovery/{id}
Response: 200 OK with the discovery session object.
GET /api/ncp/routing
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
source_device_id | UUID | Filter by source device |
dest_device_id | UUID | Filter by destination device |
signal_type | string | video, audio, data, or mixed |
status | string | active, inactive, error, or pending |
production_id | UUID | Filter by production |
Response:
{
"data": [
{
"id": "uuid",
"source_device_id": "uuid",
"source_port": "output_1",
"source_protocol": "ndi",
"dest_device_id": "uuid",
"dest_port": "input_1",
"dest_protocol": "ndi",
"signal_type": "video",
"status": "active",
"latency_ms": 3.2,
"bitrate_kbps": 150000,
"packet_loss_rate": 0.0001
}
]
}
POST /api/ncp/routing
Request Body:
{
"source_device_id": "uuid",
"source_port": "output_1",
"source_protocol": "ndi",
"dest_device_id": "uuid",
"dest_port": "input_1",
"dest_protocol": "srt",
"signal_type": "video",
"audio_channel_map": { "1": "1", "2": "2" },
"production_id": "uuid",
"show_profile_id": "uuid"
}
Required fields: source_device_id, source_port, source_protocol, dest_device_id, dest_port, dest_protocol, signal_type
Response: 201 Created with the route object, or 422 Unprocessable Entity if validation fails.
GET /api/ncp/routing/matrix
Returns the full routing matrix with all source/destination combinations and their states.
Response:
{
"data": {
"sources": [...],
"destinations": [...],
"cells": [
[
{
"source_device_id": "uuid",
"dest_device_id": "uuid",
"state": "active",
"route_id": "uuid",
"bridge_type": null,
"signal_type": "video"
}
]
]
}
}
POST /api/ncp/routing/validate
Request Body:
{
"source": {
"device_id": "uuid",
"port": "output_1",
"protocol": "ndi"
},
"dest": {
"device_id": "uuid",
"port": "input_1",
"protocol": "srt"
}
}
Response:
{
"data": {
"compatible": true,
"requires_bridge": true,
"bridge_type": "ndi_to_srt",
"requires_vlan_routing": false,
"estimated_latency_ms": 12,
"reason": null
}
}
GET /api/ncp/topology
Returns the full topology as a node-edge graph.
Response:
{
"data": {
"nodes": [
{ "id": "uuid", "type": "site", "label": "New York Studio", "data": {...} },
{ "id": "uuid", "type": "segment", "label": "Prod VLAN 100", "data": {...} },
{ "id": "uuid", "type": "device", "label": "Camera 1", "data": {...} }
],
"edges": [
{ "id": "uuid", "source": "site-uuid", "target": "segment-uuid", "type": "membership" },
{ "id": "uuid", "source": "device-uuid-1", "target": "device-uuid-2", "type": "route" }
]
}
}
GET /api/ncp/topology/sites
POST /api/ncp/topology/sites
Request Body:
{
"name": "Los Angeles Studio",
"address": "100 Broadcast Way, LA, CA 90001",
"timezone": "America/Los_Angeles",
"metadata": {}
}
Required fields: name
Response: 201 Created
GET /api/ncp/topology/sites/{id}
PATCH /api/ncp/topology/sites/{id}
GET /api/ncp/topology/segments
POST /api/ncp/topology/segments
Request Body:
{
"site_id": "uuid",
"name": "Production Video VLAN",
"vlan_id": 100,
"subnet": "10.0.100.0/24",
"gateway": "10.0.100.1",
"multicast_enabled": true,
"ptp_enabled": true,
"igmp_snooping": true
}
Required fields: name
Response: 201 Created
GET /api/ncp/topology/segments/{id}
PATCH /api/ncp/topology/segments/{id}
GET /api/ncp/health/summary
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
site_id | UUID | Filter summary to a specific site |
Response:
{
"data": {
"total_devices": 42,
"by_status": {
"online": 35,
"offline": 4,
"error": 2,
"maintenance": 1
},
"by_device_class": {
"ptz_camera": 8,
"ndi_source": 12,
"dante_device": 10,
"encoder": 5,
"desktop_node": 7
},
"avg_health_score": 87.5,
"critical_health_count": 3
}
}
| Code | Meaning |
|---|---|
200 | Success |
201 | Created |
400 | Bad Request (validation error) |
401 | Unauthorized (missing or invalid token) |
403 | Forbidden (no organization membership) |
404 | Not Found |
422 | Unprocessable Entity (business logic validation failed) |
429 | Rate Limited |
500 | Internal Server Error |