Pipelines API¶
Complete reference for pipeline management endpoints.
Pipeline IDs¶
Pipeline IDs must follow these rules:
- Allowed characters: Letters (
a-z,A-Z), digits (0-9), dashes (-), underscores (_) - Maximum length: 64 characters
- IDs with invalid characters return
400 VALIDATION_ERROR
Create Pipeline¶
POST /flex/pipeline
SimplePipeline¶
curl -k -X POST https://<encoder-ip>:3539/flex/pipeline \
-H "Content-Type: application/json" \
-d '{
"id": "camera-1",
"mode": "simple",
"on_demand": true,
"source": {
"uri": "rtsp://192.168.1.100:8554/live",
"latency_ms": 200
},
"encoding": {
"codec": "h264",
"bitrate": 750,
"width": 1920,
"height": 1080,
"fps": 30,
"quality": 5
},
"output": {
"uri": "rtsp://0.0.0.0:8731/camera-1"
}
}'
MultiViewPipeline¶
curl -k -X POST https://<encoder-ip>:3539/flex/pipeline \
-H "Content-Type: application/json" \
-d '{
"id": "quad-view",
"mode": "multiview",
"on_demand": true,
"inputs": [
{
"label": "North Gate",
"source": { "uri": "rtsp://192.168.1.100:8554/live", "latency_ms": 200 },
"overlay": { "text": "North Gate" }
},
{
"label": "South Gate",
"source": { "uri": "file:///dev/video0" },
"transform": { "grayscale": true }
}
],
"encoding": {
"codec": "av1",
"bitrate": 750,
"width": 1920,
"height": 1080,
"fps": 30,
"quality": 5
},
"output": {
"uri": "rtsp://0.0.0.0:8731/quad-view"
}
}'
Inputs can mix source types -- for example, one RTSP stream and one local camera (file:///dev/videoN). Each V4L2 camera device can only be used by one input at a time (within the pipeline and across all running pipelines).
Per-input options:
| Field | Type | Description |
|---|---|---|
source | object | Required. Source URI and latency |
label | string | Optional display name |
transform | object | Mirror, rotate (multiples of 90), grayscale |
crop | object | Crop edges: top, right, bottom, left (pixels) |
overlay | object | Per-input text/timestamp overlay |
Layout is computed automatically: 1 input = full, 2 = side-by-side, 3-4 = 2x2 grid.
Multiview pipelines always use AV1 encoding at quality preset 5. The encoding block configures bitrate, resolution, and FPS.
AdvancedPipeline¶
curl -k -X POST https://<encoder-ip>:3539/flex/pipeline \
-H "Content-Type: application/json" \
-d '{
"id": "custom-pipeline",
"mode": "advanced",
"on_demand": true,
"raw_pipeline": "videotestsrc ! videoconvert ! <flex:h264 /> ! rtph264pay ! udpsink host=239.1.1.1 port=5004"
}'
Encoder Macros¶
Use self-closing <flex:codec /> tags to insert optimized encoder + parser chains without manual codec configuration:
curl -k -X POST https://<encoder-ip>:3539/flex/pipeline \
-H "Content-Type: application/json" \
-d '{
"id": "macro-pipeline",
"mode": "advanced",
"on_demand": true,
"raw_pipeline": "videotestsrc ! queue ! videoconvert ! <flex:av1 bitrate=500 /> ! fakesink"
}'
Supported macros:
| Macro | Description |
|---|---|
<flex:av1 /> | AV1 encoder + parser |
<flex:h264 /> | H.264 encoder + parser |
<flex:h265 /> | H.265 encoder + parser |
<flex:codec2 /> | Codec2 vocal audio encoder |
- Video macro
bitrateis optional, in kbps (default: 500, range: 5–5,000) - Codec2 macro
modeis optional (default: 2400, valid: 3200, 2400, 1600, 1400, 1300, 1200) - Multiple macros can be used in one pipeline
- Macros are expanded server-side at pipeline start; the unexpanded tags are stored and returned in responses
- License checks are enforced per macro codec
Response¶
201 Created
{
"id": "camera-1",
"mode": "simple",
"on_demand": true,
"state": "ready",
"started_by": "api",
"persisted": false,
"source": { ... },
"encoding": { ... },
"output": { ... },
"klv": {
"available": false
}
}
| Field | Description |
|---|---|
started_by | What triggered the pipeline (set automatically) |
persisted | Whether the pipeline is in persistent storage |
Errors¶
| Status | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid configuration |
| 402 | LICENSE_ERROR | Codec not licensed |
| 402 | LICENSE_LIMIT_EXCEEDED | Stream limit reached |
| 409 | PIPELINE_CONFLICT | Output conflicts with running pipeline |
| 504 | PIPELINE_START_TIMEOUT | Non-on-demand pipeline failed to start |
List Pipelines¶
GET /flex/pipeline
Query Parameters¶
| Parameter | Type | Description |
|---|---|---|
mode | string | Filter by simple, multiview, or advanced |
on_demand | boolean | Filter by on-demand status |
state | string | Filter by state |
Examples¶
# All pipelines
curl -k https://<encoder-ip>:3539/flex/pipeline
# Only simple pipelines
curl -k "https://<encoder-ip>:3539/flex/pipeline?mode=simple"
# Only playing pipelines
curl -k "https://<encoder-ip>:3539/flex/pipeline?state=playing"
Response¶
[
{
"id": "camera-1",
"mode": "simple",
"state": "playing",
...
},
{
"id": "camera-2",
"mode": "simple",
"state": "ready",
...
}
]
Get Pipeline¶
GET /flex/pipeline/{id}
Response¶
{
"id": "camera-1",
"mode": "simple",
"on_demand": true,
"state": "playing",
"source": {
"uri": "rtsp://192.168.1.100:8554/live",
"latency_ms": 200
},
"encoding": {
"codec": "h264",
"bitrate": 750,
"width": 1920,
"height": 1080,
"fps": 30,
"quality": 5
},
"output": {
"uri": "rtsp://0.0.0.0:8731/camera-1"
},
"klv": {
"available": true,
"state": "playing"
}
}
Errors¶
| Status | Code | Description |
|---|---|---|
| 404 | NOT_FOUND | Pipeline doesn't exist |
Update Pipeline¶
PUT /flex/pipeline/{id}
Replaces the entire pipeline configuration. Pipeline must be stopped first.
curl -k -X PUT https://<encoder-ip>:3539/flex/pipeline/camera-1 \
-H "Content-Type: application/json" \
-d '{
"id": "camera-1",
"mode": "simple",
"source": { ... },
"encoding": {
"codec": "h265",
"bitrate": 1500,
...
},
"output": { ... }
}'
Errors¶
| Status | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid configuration |
| 404 | NOT_FOUND | Pipeline doesn't exist |
| 409 | CONFLICT | Pipeline is running |
Patch Pipeline¶
PATCH /flex/pipeline/{id}
Partially updates runtime fields for an existing pipeline. At least one of geolocation or encoding is required.
Patch Geolocation¶
Update the geolocation without replacing the entire pipeline. This is applied instantly with no stream interruption.
curl -k -X PATCH https://<encoder-ip>:3539/flex/pipeline/camera-1 \
-H "Content-Type: application/json" \
-d '{
"geolocation": {
"latitude": 38.8977,
"longitude": -77.0365,
"altitude": 100.0,
"heading": 45.0
}
}'
You can also use an MGRS coordinate:
curl -k -X PATCH https://<encoder-ip>:3539/flex/pipeline/camera-1 \
-H "Content-Type: application/json" \
-d '{
"geolocation": {
"mgrs": "18SUJ2337006519",
"altitude": 100.0
}
}'
Set to null to remove geolocation:
curl -k -X PATCH https://<encoder-ip>:3539/flex/pipeline/camera-1 \
-H "Content-Type: application/json" \
-d '{"geolocation": null}'
Patch Encoding¶
Update encoding parameters on a running pipeline. Supported for SimplePipeline and MultiViewPipeline only.
Stream Restart
Changing bitrate, dimensions, or FPS restarts the stream. The pipeline is stopped, updated, and started again, causing a brief interruption. If the restart fails, the pipeline is automatically rolled back to its previous configuration.
curl -k -X PATCH https://<encoder-ip>:3539/flex/pipeline/camera-1 \
-H "Content-Type: application/json" \
-d '{
"encoding": {
"bitrate": 500,
"width": 1280,
"height": 720,
"fps": 15
}
}'
All encoding fields are optional — only include the fields you want to change:
# Update only bitrate
curl -k -X PATCH https://<encoder-ip>:3539/flex/pipeline/camera-1 \
-H "Content-Type: application/json" \
-d '{"encoding": {"bitrate": 300}}'
You can combine geolocation and encoding in a single request:
curl -k -X PATCH https://<encoder-ip>:3539/flex/pipeline/camera-1 \
-H "Content-Type: application/json" \
-d '{
"geolocation": {"latitude": 38.8977, "longitude": -77.0365},
"encoding": {"bitrate": 750}
}'
Patchable encoding fields:
| Field | Type | Range | Description |
|---|---|---|---|
bitrate | integer | 5–5,000 kbps | Target bitrate |
width | integer | >= 1 | Output width in pixels |
height | integer | >= 1 | Output height in pixels |
fps | integer | 1–120 | Frame rate |
Patch Errors¶
| Status | Code | Description |
|---|---|---|
| 400 | FORMAT_ERROR | Unsupported field or invalid type |
| 400 | VALIDATION_ERROR | Value out of range |
| 402 | LICENSE_ERROR | Codec not licensed or stream limit exceeded |
| 404 | NOT_FOUND | Pipeline doesn't exist |
| 409 | PIPELINE_CONFLICT | Encoding patch on AdvancedPipeline, or output conflict |
| 503 | GSTREAMER_UNAVAILABLE | Backend unavailable |
| 504 | PIPELINE_START_TIMEOUT | Pipeline didn't reach playing state after restart |
Delete Pipeline¶
DELETE /flex/pipeline/{id}
Query Parameters¶
| Parameter | Type | Description |
|---|---|---|
delete_persisted | boolean | Also remove from persistent storage |
# Delete pipeline and remove from persistent storage
curl -k -X DELETE "https://<encoder-ip>:3539/flex/pipeline/camera-1?delete_persisted=true"
Response¶
204 No Content on success.
Errors¶
| Status | Code | Description |
|---|---|---|
| 404 | NOT_FOUND | Pipeline doesn't exist |
| 409 | CONFLICT | Pipeline is running |
Play Pipeline¶
PUT /flex/pipeline/{id}/play
Response¶
Returns the updated pipeline with state: "playing".
Errors¶
| Status | Code | Description |
|---|---|---|
| 402 | LICENSE_ERROR | Codec not licensed |
| 402 | LICENSE_LIMIT_EXCEEDED | Stream limit reached |
| 404 | NOT_FOUND | Pipeline doesn't exist |
| 409 | PIPELINE_CONFLICT | Output conflicts with running pipeline |
| 500 | GSTREAMER_ERROR | GStreamer failed |
| 503 | GSTREAMER_UNAVAILABLE | GStreamer unavailable |
| 504 | PIPELINE_START_TIMEOUT | Didn't reach PLAYING state |
Stop Pipeline¶
PUT /flex/pipeline/{id}/stop
Response¶
Returns the updated pipeline with state: "ready".
Get Pipeline Status¶
GET /flex/pipeline/{id}/status
Same response as Get Pipeline.
Subscribe to Status Events¶
GET /flex/pipeline/{id}/status/events
Server-Sent Events stream for real-time updates.
Events¶
pipeline_status - State changed:
pipeline_not_found - Pipeline deleted:
pipeline_error - Error occurred:
Keepalive - Sent every 30 seconds:
Framegrab Control¶
Start Framegrab¶
PUT /flex/pipeline/{id}/framegrab/start
curl -k -X PUT https://<encoder-ip>:3539/flex/pipeline/camera-1/framegrab/start \
-H "Content-Type: application/json" \
-d '{"interval_seconds": 5, "quality": 90}'
Stop Framegrab¶
PUT /flex/pipeline/{id}/framegrab/stop
KLV Control¶
Start KLV Extraction¶
PUT /flex/pipeline/{id}/klv/start
Stop KLV Extraction¶
PUT /flex/pipeline/{id}/klv/stop
Pipeline Persistence¶
Store pipelines to survive container restarts. Only pipelines with on_demand=false can be stored.
Store Pipeline¶
POST /flex/pipeline/{id}/store
curl -k -X POST https://<encoder-ip>:3539/flex/pipeline/camera-1/store \
-H "Content-Type: application/json" \
-d '{
"auto_start": true,
"description": "Main entrance camera"
}'
| Field | Description |
|---|---|
auto_start | Auto-start on container startup |
description | Human-readable description |
Update Stored Pipeline¶
PATCH /flex/pipeline/{id}/store
Update stored pipeline metadata or definition. Supports partial updates.
# Update metadata only
curl -k -X PATCH https://<encoder-ip>:3539/flex/pipeline/camera-1/store \
-H "Content-Type: application/json" \
-d '{"auto_start": false}'
# Update encoding settings
curl -k -X PATCH https://<encoder-ip>:3539/flex/pipeline/camera-1/store \
-H "Content-Type: application/json" \
-d '{"encoding": {"bitrate": 1500}}'
Remove from Storage¶
DELETE /flex/pipeline/{id}/store
Removes from persistent storage without affecting the active pipeline.
Errors¶
| Status | Code | Description |
|---|---|---|
| 404 | PIPELINE_NOT_FOUND | Pipeline doesn't exist |
| 404 | PIPELINE_NOT_PERSISTED | Pipeline not in storage |
| 409 | PIPELINE_CONFLICT | Pipeline has on_demand=true |
Pipeline ID Rules¶
Pipeline IDs must match: ^[a-zA-Z0-9_-]+$
- Alphanumeric characters
- Dashes (
-) - Underscores (
_)
Invalid characters cause a 400 error.
Resource Conflicts¶
The API prevents multiple pipelines from using exclusive resources, which would cause failures or data corruption.
Conflict Rules¶
| Resource | Conflict Condition |
|---|---|
| V4L2 Camera | Same device path (e.g., /dev/video0) |
| UDP unicast | Same destination host:port |
| UDP multicast | Same multicast group:port (unless different network_interface or inline udp://iface@host:port) |
| Local RTSP | Same port |
| File output | Same file path |
Note: Network sources (RTSP, UDP) and file inputs can be shared by multiple pipelines - only V4L2 cameras are exclusive. For MultiViewPipeline, each input's V4L2 device must be unique both within the pipeline and across all running pipelines.
Examples¶
Conflict - Both pipelines use the same camera:
Conflict - Both pipelines output to the same UDP multicast group:
No Conflict - Different multicast groups:
No Conflict - Same multicast group but different interfaces:
// Pipeline 1: udp://239.1.1.1:5004, network_interface: eth0
// Pipeline 2: udp://239.1.1.1:5004, network_interface: eth1 ← OK
Error Responses¶
Camera conflict:
{
"message": "Camera /dev/video0 is already in use by pipeline camera-1.",
"code": "PIPELINE_CONFLICT"
}
Output conflict: