Live Stream WebSocket
This feature is not available in the Cloud API.
The live stream WebSocket is the Local API Gateway connection for continuous robot data. It can:
- receive output such as pose, images, point clouds, and laser scans;
- send low-latency
TwistorAckermannDriveStampedteleoperation commands.
For live Custom KPI values from local-fleet member robots, use the separate Fleet Custom KPI WebSocket. That route always uses JSON and has a different payload.
Basic Flow
- Log in with
POST /apigateway/v2/auth/login. - List robots with
GET /apigateway/v2/robots. - List stream descriptors with
GET /apigateway/v2/robots/{robotId}/streams. - Select an
outputdescriptor to receive data, or a supportedinputdescriptor to send commands. - Open one WebSocket for that descriptor, direction, and format.
To receive feedback while sending commands, open two WebSockets: one input connection and one output connection.
Endpoint
GET /apigateway/v2/robots/{robotId}/streams/connect
This route must be a WebSocket upgrade request.
Choose the Frame Format
Add these optional query parameters:
direction=output|inputformat=protobuf|json
If they are omitted, the connection uses direction=output&format=protobuf. This keeps older output
clients working without URL or decoder changes.
The four combinations are:
output+protobuf: the Agent sends binaryMessageStreamenvelopes. Decode the envelope, then decode its typeddatafield.output+json: the Agent sends the selected typed message directly as a JSON text frame. For example, a pose frame is the pose object, with no extraMessageStreamwrapper.input+protobuf: send the raw serialized command message as a binary frame. Do not wrap it inMessageStream.input+json: send the command object directly as a JSON text frame.
JSON is convenient for browsers, scripts, pose, status, and other readable values. Protobuf is more efficient and is recommended for images, scans, point clouds, and other large or frequent output.
Query Parameters
access_token: Local API Gateway token. Browser clients normally remove the leadingbearerbefore placing the token in the URL.componentName: descriptorcomponentName; required for component streams.streamName: descriptorstreamName; required for every source.source: descriptorstreamSourceType.streamType: descriptorstreamType.direction:outputorinput; defaultoutput.format:protobuforjson; defaultprotobuf.url: descriptorsourceUrlfor ONVIF, or a user-entered ONVIF/RTSP URL.endpoint: descriptorsourceEndpointfor a device source.usernameandpassword: optional ONVIF/RTSP credentials. Discovery does not return credentials.maxRate: optional output rate limit. Omit it or use0to let Agent defaults apply.withoutDownSampling: optional output point-cloud control; defaultfalse.
maxRate and withoutDownSampling apply to output connections. Input command rate is limited by the
Agent as described under Teleoperation Safety.
Source Support
Output accepts:
1: Nimbus component stream;2: ROS1 topic;3: ROS2 topic;4: ONVIF URL;5: RTSP URL;6: device endpoint, such as/dev/video0.
Input accepts component, ROS1, and ROS2 sources (1-3). Use the fields returned by stream listing;
do not guess generated gateway component names.
JSON Output Example
This browser example prints a readable typed JSON frame, such as PoseStamped:
const query = new URLSearchParams({
access_token: token.replace(/^bearer\s+/i, ''),
componentName: stream.componentName || '',
streamName: stream.streamName,
source: String(stream.streamSourceType),
streamType: stream.streamType,
direction: 'output',
format: 'json'
});
const socket = new WebSocket(
`ws://agent-host:19992/apigateway/v2/robots/${encodeURIComponent(robotId)}/streams/connect?${query}`
);
socket.onmessage = event => {
const value = JSON.parse(event.data);
console.log('typed stream value', value);
};
Use wss:// only when the client trusts the Agent's local HTTPS certificate.
Protobuf Output
Every output WebSocket message is one serialized MessageStream envelope when
format=protobuf. The envelope contains:
- stream identity;
dataType, which names theNimbus.Messages.*payload type;data, which contains the typed protobuf payload;- compression information.
Decode it in this order:
- Decode the WebSocket frame as
MessageStream. - If it is gzip-compressed, inflate
data. - Select the typed protobuf parser using
dataType. - Parse
datawith that parser.
Common output types include CompressedImage, Image, LaserScan, PoseStamped, and
PointCloud2.
Teleoperation Input
Input WebSockets initially accept these exact descriptor types:
Nimbus.Messages.geometry_msgs.TwistNimbus.Messages.ackermann_msgs.AckermannDriveStamped
JSON Twist example:
{
"linear": { "x": 0.2, "y": 0, "z": 0 },
"angular": { "x": 0, "y": 0, "z": 0.1 }
}
JSON Ackermann example:
{
"drive": {
"steeringAngle": 0.1,
"steeringAngleVelocity": 0,
"speed": 0.2,
"acceleration": 0,
"jerk": 0
}
}
For format=protobuf, serialize the selected Twist or AckermannDriveStamped object and send
those raw bytes. A binary input frame is not a MessageStream envelope.
Teleoperation Safety
- Only one input WebSocket can control the same resolved stream target. A second connection receives
HTTP
409. - The Agent publishes at most 20 commands per second. When frames arrive faster, the newest valid command replaces the older pending command.
- If no valid command arrives for 500 ms, the Agent publishes a type-correct zero command.
- Invalid data, disconnect, cancellation, host stop, and Agent shutdown also attempt a zero command.
- Each complete input message is limited to 64 KiB. Fragmented WebSocket frames are supported.
- Speed and steering values must be finite. The robot controller is still responsible for its own physical speed and steering limits.
- A cooperative client should send zero before disconnecting. The Agent deadman remains the final safety mechanism when client cleanup cannot run.
Use the joystick at /debug/local-api-gateway/streams to exercise discovered Twist and Ackermann
inputs locally. It supports JSON and protobuf and sends zero on normal joystick release paths.
Python Examples
The Agent repository includes two relevant console samples:
# Read output as direct JSON, for example a pose stream.
nimbus-apigateway-streams <local-user-token> <base-url> pose --format json
# Send a finite one-second command after selecting an input descriptor.
nimbus-apigateway-teleoperation <local-user-token> <base-url> cmd_vel \
--format json --speed 0.2 --turn 0.1 --rate 10 --duration 1
The teleoperation sample defaults speed and turn to zero, caps its rate at 20 Hz, caps duration at five minutes, and sends zero before and after the requested command interval.
Output Delivery and Size Limits
- Output is live, best-effort delivery, not guaranteed replay.
- Each output client has a bounded queue of one frame. Slow clients can lose older queued frames.
- JSON conversion allows at most 12 MiB of decoded protobuf and a 16 MiB UTF-8 JSON frame. The Agent
closes oversized JSON output with code
1009; use protobuf for that stream. - Input messages allow at most 64 KiB.
Errors
Before the WebSocket upgrade:
400: missing/invalid query, unsupported input type/source, or not a WebSocket request;401: token missing, expired, or invalid;404: local robot, stream, or message type not found;409: another input WebSocket already controls the target;501: the selected robot is a local-fleet member; live WebSocket tunneling is not implemented.
After the upgrade, the close code explains the failure:
1000: normal close;1003: text/binary frame does not matchformat;1007: malformed protobuf, JSON, or UTF-8;1008: command violates input policy, such as a non-finite value;1009: input frame or JSON output is too large;1011: target publication or output conversion failed.
Browser code 1006 means the browser did not receive a close frame. With local HTTPS, a common
cause is an untrusted local certificate.