This document describes the HTTP call from the robot to Task-router-x402 (informal name teleop_fetch: script, ROS node, systemd, etc.) and what the robot sees after an operator accepts and the ROSBridge proxy is active.
See also: README.md (TELEOP_* table, Docker, health), ROBOT_SIDE_AI_AGENT.md (checklist for robot-side code), source src/routes/teleopHelp.js, src/ws/teleopServer.js.
Usually one action: tell Task-router-x402 the robot needs help — POST /api/robots/{robotId}/teleop/help.
This call does not need teleoperator JWT, cookies, or WebSocket; the operator and VR use those after accept.
Without these the help route is not mounted (requests to /api/robots/.../teleop/help are not handled as teleop):
DATABASE_URLandTELEOPERATOR_JWT_SECRETare set — tables exist,/api/teleoperator/*,/api/robots/.../teleop/help, UI/teleoperatorare wired.- The robot is registered in the registry: admin
/ui(/api/admin/robots), orPOST /api/robots/enrollwithROBOT_FLEET_ENROLLMENT_SECRET(headerX-Robot-Fleet-SecretorAuthorization: Bearer), orPOST /api/robotswith the same secret or admin session. - The robot row has
teleopSecret(from enroll/admin API; publicGET /api/robotsdoes not return it).
Check: GET /health — teleoperatorEnabled: true when DB is connected; teleopWs: true when teleop WebSocket is enabled (TELEOP_WS_ENABLED not false/0).
Recommended path: call POST /api/robots/enroll once (and when IP/host changes) with the same enrollmentKey (stable device id in your config).
| Parameter | Value |
|---|---|
| URL | http(s)://<TASK_ROUTER_HOST>:<PORT>/api/robots/enroll |
| Fleet auth | X-Robot-Fleet-Secret: <ROBOT_FLEET_ENROLLMENT_SECRET> or Authorization: Bearer <same secret> |
| Body (JSON) | Required enrollmentKey, host, port; optional name, rosbridgeHost, rosbridgePort, teleopSecret (server generates if omitted), operatorRegistryUrl (allowlist push, see ROBOT_OPERATOR_SYNC.md) |
Response: full robot object including id (store as robotId) and teleopSecret. Repeat with the same enrollmentKey updates the row (same id).
Discovering TASK_ROUTER_HOST: with default mDNS, http://raid-app.local:3000 (default MDNS_HOSTNAME=raid-app). If the operator sets MDNS_HOSTNAME, use http://<that-name>.local:<PORT> instead. See README and ROBOT_INTEGRATION_STABILITY.md.
| Parameter | Value |
|---|---|
| Method | POST |
| URL | http(s)://<HOST>:<PORT>/api/robots/<robotId>/teleop/help |
robotId |
UUID from POST /api/robots/enroll or admin POST /api/admin/robots (not the robot’s host:port). |
| Robot secret | Header X-Robot-Teleop-Secret: <secret> — same as in registry. Or Authorization: Bearer <secret> (same value). |
| Body | JSON: required string message. metadata recommended: strings task_id, error_context (may be empty), optional situation_report — free UTF-8 state text (up to ~64 KiB UTF-8 bytes, server truncates longer). If metadata is missing, the server fills those fields with empty strings. Extra keys under metadata are preserved. |
| Content-Type | With body: application/json. |
| Code | Meaning |
|---|---|
| 201 | New request; body has helpRequest, duplicate: false. |
| 200 | Open request already exists; same shape, duplicate: true. |
| 401 | Missing/wrong secret or robot has no teleopSecret. |
| 404 | No such robotId in registry. |
| 400 | Missing or non-string message. |
| 500 | Server/DB error. |
After 201/200 a help_request event is sent on /ws/teleoperator?token=…: if the robot has active teleoperator_robot_grants rows, only those operators; otherwise all connected clients with valid JWT. The robot does not need to open anything extra for this.
curl -sS -X POST \
"http://TASK_ROUTER_HOST:3000/api/robots/ROBOT_UUID/teleop/help" \
-H "Content-Type: application/json" \
-H "X-Robot-Teleop-Secret: your-shared-secret" \
-d '{"message":"Need assistance","metadata":{"task_id":"run-1","error_context":"","situation_report":"Near door; navigation stalled.","battery":12}}'There is no minimum length in code: empty string means “teleop disabled” for that robot. In production use a long random secret like an API key.
This is not part of teleop_fetch: it applies after the operator calls POST /api/teleoperator/help-requests/{id}/accept and connects to /ws/teleop/session/{sessionId}?token=….
Then Task-router-x402 opens its own client WebSocket to ws://rosbridgeHost:rosbridgePort (robot card fields; default rosbridgeHost = host, port 9090).
The JWT is not sent to the robot. Only stable profile fields:
| Channel | Name | Value |
|---|---|---|
| HTTP header | X-Teleoperator-Id |
Teleoperator user UUID in PostgreSQL (= JWT sub). |
| HTTP header | X-Teleoperator-Login |
Login from JWT only if present when the token was issued. |
| Query | teleoperator_id |
Same as X-Teleoperator-Id. |
| Query | teleoperator_login |
Same as login; omitted if no login. |
Example URL (paths to rosbridge aside; often ws://IP:9090?teleoperator_id=…&teleoperator_login=…):
ws://192.168.1.10:9090?teleoperator_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890&teleoperator_login=operator1
Stock rosbridge may ignore these headers and query. They are usually read by nginx / another proxy in front of rosbridge or a custom wrapper.
| Variable | Default | If false / 0 / no / off |
|---|---|---|
TELEOP_FORWARD_OPERATOR_HEADERS |
on | Do not send X-Teleoperator-*. |
TELEOP_FORWARD_OPERATOR_QUERY |
on | Do not append teleoperator_* to the URL. |
Empty env values keep defaults (enabled). Implementation: buildRosbridgeWebSocketTarget in src/ws/teleopServer.js, flags in src/config.js (forwardOperatorHeaders / forwardOperatorQuery).
Reconnects and WS session lifetime (same teleopServer.js + README env): TELEOP_ROSBRIDGE_CONNECT_ATTEMPTS, TELEOP_ROSBRIDGE_RECONNECT_DELAY_MS, TELEOP_ROSBRIDGE_DROP_RECONNECT_ATTEMPTS, TELEOP_SESSION_END_GRACE_MS. Operator JWT still TELEOPERATOR_JWT_EXPIRES_IN.
- Robot and Task-router-x402 must reach each other (often LAN for HTTP
teleop/helpand outbound WS to rosbridge). - Do not log full
teleopSecret. - CORS allows
X-Robot-Teleop-SecretandX-Robot-Fleet-Secretfor browsers; typicalteleop_fetchon the robot is server-to-server, no CORS.
Change only if the POST …/teleop/help contract is wrong (URL, method, secret header, robot UUID). Forwarding teleoperator_* is configured on Task-router-x402 and on the robot proxy/rosbridge stack; teleop_fetch usually needs no extra logic for that.
Teleop tag, POST /api/robots/{robotId}/teleop/help — interactive at /docs.