Skip to content

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 bitrate is optional, in kbps (default: 500, range: 5–5,000)
  • Codec2 macro mode is 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}

curl -k https://<encoder-ip>:3539/flex/pipeline/camera-1

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}

curl -k -X DELETE https://<encoder-ip>:3539/flex/pipeline/camera-1

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

curl -k -X PUT https://<encoder-ip>:3539/flex/pipeline/camera-1/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

curl -k -X PUT https://<encoder-ip>:3539/flex/pipeline/camera-1/stop

Response

Returns the updated pipeline with state: "ready".

Get Pipeline Status

GET /flex/pipeline/{id}/status

curl -k https://<encoder-ip>:3539/flex/pipeline/camera-1/status

Same response as Get Pipeline.

Subscribe to Status Events

GET /flex/pipeline/{id}/status/events

Server-Sent Events stream for real-time updates.

curl -k -N https://<encoder-ip>:3539/flex/pipeline/camera-1/status/events

Events

pipeline_status - State changed:

event: pipeline_status
data: {"id":"camera-1","state":"playing",...}

pipeline_not_found - Pipeline deleted:

event: pipeline_not_found
data: {"pipeline_id":"camera-1"}

pipeline_error - Error occurred:

event: pipeline_error
data: {"message":"Connection lost"}

Keepalive - Sent every 30 seconds:

: keepalive

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

curl -k -X PUT https://<encoder-ip>:3539/flex/pipeline/camera-1/framegrab/stop

KLV Control

Start KLV Extraction

PUT /flex/pipeline/{id}/klv/start

curl -k -X PUT https://<encoder-ip>:3539/flex/pipeline/camera-1/klv/start

Stop KLV Extraction

PUT /flex/pipeline/{id}/klv/stop

curl -k -X PUT https://<encoder-ip>:3539/flex/pipeline/camera-1/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.

curl -k -X DELETE https://<encoder-ip>:3539/flex/pipeline/camera-1/store

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:

// Pipeline 1: source file:///dev/video0
// Pipeline 2: source file:///dev/video0  ← 409 Conflict

Conflict - Both pipelines output to the same UDP multicast group:

// Pipeline 1: udp://239.1.1.1:5004
// Pipeline 2: udp://239.1.1.1:5004  ← 409 Conflict

No Conflict - Different multicast groups:

// Pipeline 1: udp://239.1.1.1:5004
// Pipeline 2: udp://239.1.1.2:5004  ← OK

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:

{
  "message": "Output UDP multicast 239.1.1.1:5004 is already in use by pipeline camera-1.",
  "code": "PIPELINE_CONFLICT"
}