Log in Create workspace
Skip to content

API reference

Operator - Fleet

Manage robots, missions, cohorts, diagnostics, alerts, OTA releases, and bulk work.

Jump to 63 endpoints
On this page

Overview

Fleet APIs let operators manage the selected workspace's physical or hosted robots. They combine registration and grouping with mission state, health evidence, maintenance predictions, OTA status, and auditable batch progress.

Inventory is not presence
A robot record describes identity and configuration. last_seen_at, heartbeat data, diagnostics, battery, fleet alerts, and maintenance predictions provide operational health; do not infer readiness from status alone.
Bulk work is asynchronous evidence
Batch operations save per-target command links and progress. For dispatch_command, queued or accepted delivery is not device success: targets remain running until an exact terminal command result. A cancellation request stops known-unsent work but cannot guarantee a command already sent to a robot has physically stopped. Progress PubSub is emitted after commit on a best-effort basis; poll persisted batch/target state after reconnect.
Current location and historical evidence differ
Robot current position and geofence boundary state are separate from append-only location samples. A trusted heartbeat location must be no more than five minutes old or 30 seconds ahead. Historical pages use a signed workspace/robot-bound cursor (25 default, 100 max); old samples expire after 90 days by default without clearing current position, active alerts or transition_version.
Work-order history survives detail retention
Only in_progress work orders reserve a robot. Completed/cancelled order summaries keep outcome, parts, completion actor and alert/prediction links. Recent /events rows age out after 400 days by default, but the response's archived_audit_url leads to paginated immutable audit counterparts keyed by original work_order_event_id. Audit history has no automatic time purge.

Before you begin

  • A signed-in browser session with a selected workspace backed by an active membership is required; an archived workspace remains readable but rejects mutations. Every /api/v1/operator endpoint rejects API credentials and bearer tokens, regardless of their scopes.
  • Any role on an active membership can read fleet state. Owners, admins, and members can operate the fleet; auditors cannot register, dispatch, acknowledge, update, or decommission.
  • Use stable serial numbers and verify the robot, cohort, mission, or update belongs to the selected workspace before acting.

How it works

  1. Build a safe target set

    Filter robots by site, tag, status, or attention signal and inspect health before dispatch. Cohorts give reusable membership; batch operations give a durable snapshot of the chosen targets.

  2. Observe after action

    After a mission, OTA, alert action, or batch starts, follow its detail endpoint and per-target outcome. An accepted mission or OTA command is still outcome unknown until matching robot ACK. For geofences, hydrate current state first, then accept only strictly newer transition_version for the same fence/revision. Compare fresh heartbeat, diagnostics, and fleet-alert summaries before declaring the operation complete.

Examples

These examples are illustrative. Replace the sample values with your own. Keep credentials in environment variables or a secret manager, never in client-side code.

List robots needing attention

The list stays workspace-scoped; returned robot summaries include identity, lifecycle, battery, location, tags, and last-seen data.

javascript
const response = await fetch("/api/v1/operator/robots?filter%5Bstatus%5D=offline", {
  credentials: "same-origin",
  headers: {"Accept": "application/json"}
});
const body = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${body.detail}`);
console.log(body);
What to expect

200 OK with {"robots": [], "next_cursor": null} when no offline robots match. A populated robot row includes serial_number, status, battery_level, location, tags, and last_seen_at. Pass next_cursor unchanged for the next bounded page.

Troubleshooting

A valid robot ID returns 404
Fleet lookups never cross workspace boundaries. Check the selected workspace; a foreign identifier is deliberately reported as not found.
An operation partly failed
Open the batch or OTA status and inspect target-level reasons. Retry only failed targets when supported, rather than issuing a second broad operation that repeats successful work.

Endpoint reference

Ready to integrate? Open an endpoint for its required permissions, request fields, response format, and status codes. The examples above explain the workflow; these details define each individual request.

GET
/api/v1/operator/robots Web Session

List robots

Description

Returns a name/id-ordered page of robots in the current workspace (default 25, maximum 100). Follow next_cursor with the same filters for continuation. A mutable robot name can move between pages; this is not a frozen roster snapshot.

Auth

Web Session
Parameters
Name In Type Required Description
query query string No Case-insensitive search in name, serial, manufacturer, or model
status query string No Filter by robot status. Decommissioned robots are hidden unless `status=decommissioned` is requested explicitly.
robot_type query string No Filter by robot type
cohort_id query string No Restrict to entities whose robot belongs to this cohort, statically or by rule
tag query string No Restrict to entities whose robot carries this tag (lowercased and trimmed). Exactly one tag; a value that does not resolve to a single tag matches nothing rather than being ignored.
limit query integer No Page size (default 25, maximum 100)
cursor query string No Opaque continuation token, bound to workspace and effective filters
Responses
200

List of robots

Returns: RobotList

Example
{
  "next_cursor": null,
  "robots": [
    {
      "battery_level": 82,
      "capabilities": [
        "navigate",
        "pickup",
        "delivery"
      ],
      "firmware_version": "3.2.1",
      "id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "inserted_at": "2026-03-25T08:00:00Z",
      "last_seen_at": "2026-03-27T09:12:10Z",
      "location": {
        "lat": 41.717,
        "lng": 44.793,
        "zone": "A3"
      },
      "manufacturer": "RoboWorks",
      "metadata": {
        "shift": "day"
      },
      "model": "AX-7",
      "name": "Aisle Runner 7",
      "robot_type": "amr",
      "serial_number": "AMR-AX7-0042",
      "service_agent_id": "f4d49601-3477-4c9d-b896-ac08a26a3147",
      "status": "online",
      "tags": [
        "night-shift",
        "warehouse-b"
      ],
      "updated_at": "2026-03-27T09:12:10Z",
      "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
    }
  ]
}
POST
/api/v1/operator/robots Web Session

Register robot

Description

Registers a new robot in the current workspace.

Auth

Web Session
Request Body
Field Type Required Description
robot object Yes Robot attributes
robot.name string Yes Robot name (2-120 characters)
robot.metadata object No Arbitrary metadata key-value pairs
robot.location object No Current location (lat, lng, floor, zone)
robot.tags array<object> No Free-form tags (lowercased and trimmed, max 32 tags of 64 characters)
robot.service_agent_id string Yes Canonical service-agent UUID linked to this robot
robot.capabilities array<object> No List of robot capabilities
robot.firmware_version string No Current firmware version
robot.serial_number string Yes Globally unique serial number
robot.battery_level integer No Battery level (0-100)
robot.manufacturer string No Manufacturer name
robot.model string No Model name
robot.robot_type string (amr, drone, manipulator, humanoid) No Robot type
Responses
201

Robot registered

Returns: Robot

Example
{
  "battery_level": 82,
  "capabilities": [
    "navigate",
    "pickup",
    "delivery"
  ],
  "firmware_version": "3.2.1",
  "id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "inserted_at": "2026-03-25T08:00:00Z",
  "last_seen_at": "2026-03-27T09:12:10Z",
  "location": {
    "lat": 41.717,
    "lng": 44.793,
    "zone": "A3"
  },
  "manufacturer": "RoboWorks",
  "metadata": {
    "shift": "day"
  },
  "model": "AX-7",
  "name": "Aisle Runner 7",
  "robot_type": "amr",
  "serial_number": "AMR-AX7-0042",
  "service_agent_id": "f4d49601-3477-4c9d-b896-ac08a26a3147",
  "status": "online",
  "tags": [
    "night-shift",
    "warehouse-b"
  ],
  "updated_at": "2026-03-27T09:12:10Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
422

Validation error

POST
/api/v1/operator/robots/batch Web Session

Pre-register robots in bulk

Description

Creates one `pending` robot per serial number, with no service agent linked. Rows are independent: a duplicate serial, an invalid row, an unknown cohort name or a row past the plan's robot quota is reported against its own row number while the rest of the batch still lands. Maximum 1000 rows per request.

Auth

Web Session
Request Body
Field Type Required Description
robots array<object> Yes Rows to pre-register. Each row needs `serial_number`; `name`, `model`, `manufacturer`, `robot_type`, `cohort` (an existing cohort name) and `tags` are optional. The name defaults to the serial number.
Responses
201

Batch processed; per-row results returned

Returns: RobotBatchResult

Example
{
  "results": [
    {
      "row": 1,
      "serial_number": "AMR-AX7-0042",
      "status": "created"
    },
    {
      "row": 2,
      "serial_number": "AMR-AX7-0043",
      "status": "created"
    },
    {
      "errors": {},
      "reason": "duplicate_in_batch",
      "row": 3,
      "serial_number": "AMR-AX7-0042",
      "status": "skipped"
    }
  ],
  "robots": [
    {
      "battery_level": 82,
      "capabilities": [
        "navigate",
        "pickup",
        "delivery"
      ],
      "firmware_version": "3.2.1",
      "id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "inserted_at": "2026-03-25T08:00:00Z",
      "last_seen_at": "2026-03-27T09:12:10Z",
      "location": {
        "lat": 41.717,
        "lng": 44.793,
        "zone": "A3"
      },
      "manufacturer": "RoboWorks",
      "metadata": {
        "shift": "day"
      },
      "model": "AX-7",
      "name": "Aisle Runner 7",
      "robot_type": "amr",
      "serial_number": "AMR-AX7-0042",
      "service_agent_id": "f4d49601-3477-4c9d-b896-ac08a26a3147",
      "status": "online",
      "tags": [
        "night-shift",
        "warehouse-b"
      ],
      "updated_at": "2026-03-27T09:12:10Z",
      "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
    }
  ],
  "summary": {
    "created": 2,
    "skipped": 1,
    "total": 3
  }
}
400

Empty batch, more than 1000 rows, or a malformed payload

GET
/api/v1/operator/robots/:id Web Session

Get robot details

Description

Returns details of a specific robot.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Robot UUID
Responses
200

Robot details

Returns: Robot

Example
{
  "battery_level": 82,
  "capabilities": [
    "navigate",
    "pickup",
    "delivery"
  ],
  "firmware_version": "3.2.1",
  "id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "inserted_at": "2026-03-25T08:00:00Z",
  "last_seen_at": "2026-03-27T09:12:10Z",
  "location": {
    "lat": 41.717,
    "lng": 44.793,
    "zone": "A3"
  },
  "manufacturer": "RoboWorks",
  "metadata": {
    "shift": "day"
  },
  "model": "AX-7",
  "name": "Aisle Runner 7",
  "robot_type": "amr",
  "serial_number": "AMR-AX7-0042",
  "service_agent_id": "f4d49601-3477-4c9d-b896-ac08a26a3147",
  "status": "online",
  "tags": [
    "night-shift",
    "warehouse-b"
  ],
  "updated_at": "2026-03-27T09:12:10Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Robot not found

PUT
/api/v1/operator/robots/:id Web Session

Update robot

Description

Updates an existing robot's attributes.

Auth

Web Session
Request Body
Field Type Required Description
robot object Yes Robot attributes to update
robot.name string No Robot name
robot.metadata object No Metadata
robot.location object No Location
robot.tags array<object> No Free-form tags (lowercased and trimmed, max 32 tags of 64 characters)
robot.capabilities array<object> No Capabilities
robot.firmware_version string No Firmware version
robot.battery_level integer No Battery level (0-100)
robot.manufacturer string No Manufacturer name
robot.model string No Model name
Parameters
Name In Type Required Description
id path string Yes Robot UUID
Responses
200

Robot updated

Returns: Robot

Example
{
  "battery_level": 82,
  "capabilities": [
    "navigate",
    "pickup",
    "delivery"
  ],
  "firmware_version": "3.2.1",
  "id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "inserted_at": "2026-03-25T08:00:00Z",
  "last_seen_at": "2026-03-27T09:12:10Z",
  "location": {
    "lat": 41.717,
    "lng": 44.793,
    "zone": "A3"
  },
  "manufacturer": "RoboWorks",
  "metadata": {
    "shift": "day"
  },
  "model": "AX-7",
  "name": "Aisle Runner 7",
  "robot_type": "amr",
  "serial_number": "AMR-AX7-0042",
  "service_agent_id": "f4d49601-3477-4c9d-b896-ac08a26a3147",
  "status": "online",
  "tags": [
    "night-shift",
    "warehouse-b"
  ],
  "updated_at": "2026-03-27T09:12:10Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Robot not found

422

Validation error

DELETE
/api/v1/operator/robots/:id Web Session

Decommission robot

Description

Retires a robot permanently by setting its status to `decommissioned` and revoking the robot identity's credentials (the `agk_` key and any outstanding bearer token stop authenticating immediately, so a still-powered unit cannot report its way back onto the roster). The row is never deleted — mission, diagnostic and alert history stay intact and the robot is still readable by id — but it disappears from robot listings (unless `status=decommissioned` is requested), from mission auto-assignment and alert evaluation, and it stops counting against the plan's robot quota. This is distinct from `maintenance`, which is an in-service state.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Robot UUID
Responses
204

Robot decommissioned

404

Robot not found

POST
/api/v1/operator/robots/:id/heartbeat Web Session

Record robot heartbeat

Description

Records a robot heartbeat. Numeric lat/lng location is validated, appended to bounded history, and evaluated against active geofences atomically; optional observed_at is ISO8601, at most five minutes old or 30 seconds future, and strictly newer than the previous trusted fix. Omitted location preserves the trusted fix. `last_seen_at` is server time. Claimed robots move to `online`; unclaimed `pending` pre-registrations stay `pending`. The body may be wrapped in `heartbeat` or sent flat.

Auth

Web Session
Request Body
Field Type Required Description
heartbeat object No Optional wrapper. The same fields are accepted at the top level when the wrapper is omitted.
heartbeat.metadata object No Arbitrary metadata
heartbeat.location object No Trusted numeric lat/lng, optional observed_at and bounded scalar extras
heartbeat.firmware_version string No Current firmware version
heartbeat.battery_level integer No Current battery level (0-100). Values outside that range return 422.
Parameters
Name In Type Required Description
id path string Yes Robot UUID
Responses
200

Heartbeat recorded

Returns: Robot

Example
{
  "battery_level": 82,
  "capabilities": [
    "navigate",
    "pickup",
    "delivery"
  ],
  "firmware_version": "3.2.1",
  "id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "inserted_at": "2026-03-25T08:00:00Z",
  "last_seen_at": "2026-03-27T09:12:10Z",
  "location": {
    "lat": 41.717,
    "lng": 44.793,
    "zone": "A3"
  },
  "manufacturer": "RoboWorks",
  "metadata": {
    "shift": "day"
  },
  "model": "AX-7",
  "name": "Aisle Runner 7",
  "robot_type": "amr",
  "serial_number": "AMR-AX7-0042",
  "service_agent_id": "f4d49601-3477-4c9d-b896-ac08a26a3147",
  "status": "online",
  "tags": [
    "night-shift",
    "warehouse-b"
  ],
  "updated_at": "2026-03-27T09:12:10Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Robot not found

410

The robot has been decommissioned and can no longer report

422

Validation error (invalid battery_level or other changeset errors)

GET
/api/v1/operator/missions Web Session

List missions

Description

Returns missions in the current workspace newest first, with optional status and robot filters. Always bounded: `limit` defaults to 100 and is clamped to 500.

Auth

Web Session
Parameters
Name In Type Required Description
status query string No Filter by mission status
robot_id query string No Filter by robot UUID
cohort_id query string No Restrict to entities whose robot belongs to this cohort, statically or by rule
tag query string No Restrict to entities whose robot carries this tag (lowercased and trimmed). Exactly one tag; a value that does not resolve to a single tag matches nothing rather than being ignored.
limit query integer No Maximum missions to return, newest first (default 100, server maximum 500)
Responses
200

List of missions

Returns: MissionList

Example
{
  "missions": [
    {
      "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
      "completed_at": null,
      "control_action": null,
      "control_command_id": null,
      "control_result": {},
      "control_status": null,
      "description": "Collect 3 bins from aisle A and deliver to packing.",
      "execution_result": {},
      "execution_status": "queued",
      "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
      "inserted_at": "2026-03-27T09:10:00Z",
      "name": "Pick order #4821",
      "payload": {
        "order_id": "4821"
      },
      "priority": "high",
      "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
      "result": {},
      "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "scheduled_at": "2026-03-27T09:20:00Z",
      "started_at": null,
      "status": "assigned",
      "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
      "updated_at": "2026-03-27T09:12:00Z",
      "waypoints": {
        "waypoints": [
          {
            "lat": 41.717,
            "lng": 44.793,
            "order": 0
          }
        ]
      },
      "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
    }
  ]
}
POST
/api/v1/operator/missions Web Session

Create mission

Description

Creates a new mission in the current workspace. A new mission is always `pending`: lifecycle fields (`status`, `trace_id`, `result`, `started_at`, `completed_at`) are ignored and only change through the assign/start/pause/resume/complete/fail/cancel endpoints.

Auth

Web Session
Request Body
Field Type Required Description
mission object Yes Mission attributes
mission.name string Yes Mission name (2-200 characters)
mission.priority string (low, normal, high, critical) No Priority level
mission.description string No Mission description
mission.payload object No Mission-specific payload. Auto-assign reads `payload.required_capabilities` (an array of strings) and prefers online robots whose capabilities overlap.
mission.scheduled_at datetime (ISO 8601) No Scheduled start time. ScheduledMissionWorker queues eligible pending or assigned missions after this time; robot acknowledgment confirms actual start.
mission.robot_id string No Robot UUID to assign
mission.waypoints object No Mission waypoints as `{"waypoints": [...]}`. Each waypoint is an object with a numeric `lat` (-90 to 90) and `lng` (-180 to 180), an optional `action` string and an optional `params` object; anything else is refused with 422.
Responses
201

Mission created

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
422

Validation error

GET
/api/v1/operator/missions/:id Web Session

Get mission details

Description

Returns details of a specific mission.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Mission details

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Mission not found

PUT
/api/v1/operator/missions/:id Web Session

Update mission

Description

Updates an existing mission's attributes.

Auth

Web Session
Request Body
Field Type Required Description
mission object Yes Mission attributes to update
mission.name string No Mission name
mission.priority string No Priority level
mission.description string No Description
mission.payload object No Payload data
mission.waypoints object No Waypoints as `{"waypoints": [...]}`, validated like create: each waypoint needs a numeric `lat` (-90 to 90) and `lng` (-180 to 180).
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Mission updated

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Mission not found

422

Validation error

POST
/api/v1/operator/missions/:id/assign Web Session

Assign robot to mission

Description

Assigns a fresh online robot with a linked active service identity and no conflicting active mission to a pending or assigned mission.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Mission UUID
robot_id query string Yes Robot UUID to assign
Responses
200

Robot assigned

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

Invalid mission status for assignment

404

Mission or robot not found

POST
/api/v1/operator/missions/:id/start Web Session

Start mission

Description

Queues fleet.mission.execute for a fresh online, linked robot. Returns assigned/queued with command_id and a trace; only a robot ACK moves it to in_progress/running. Queued commands wait for reconnect and are not duplicated on repeat start. An accepted command is never automatically re-executed after transport loss.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Execute command queued; awaiting robot acceptance

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

Invalid mission status for starting

404

Mission not found

422

The mission's robot already has an active mission

POST
/api/v1/operator/missions/:id/cancel Web Session

Cancel mission

Description

Queues fleet.mission.cancel for active robot work; status stays unchanged until robot success ACK. A queued execute is cancelled locally only when delivery_state is known_unsent. Legacy unknown or possibly_delivered queued executes instead receive a pending device cancel; original execute redelivery is fenced and a late bound ACK remains valid. An uncertain earlier control returns control_pending until its ACK, after which the operator may retry. The control payload is {"mission_id": "mission UUID", "execute_command_id": "current execute command UUID"}; execute_command_id binds the request to that execute attempt.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Cancel requested, or queued execute cancelled locally

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

Invalid mission status or control_pending while an uncertain prior control resolves

404

Mission not found

POST
/api/v1/operator/missions/:id/pause Web Session

Pause mission

Description

Queues fleet.mission.pause with payload {"mission_id": "mission UUID", "execute_command_id": "current execute command UUID"}. execute_command_id binds the control to the current execute attempt. Returns current in_progress status with control_action/status/command_id; paused only after robot success ACK.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Pause requested; awaiting robot acknowledgment

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

Invalid mission status for pausing

404

Mission not found

POST
/api/v1/operator/missions/:id/resume Web Session

Resume mission

Description

Queues fleet.mission.resume with payload {"mission_id": "mission UUID", "execute_command_id": "current execute command UUID"}. execute_command_id binds the control to the current execute attempt. Returns current paused status until robot success ACK.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Resume requested; awaiting robot acknowledgment

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

Invalid mission status for resuming

404

Mission not found

POST
/api/v1/operator/missions/:id/complete Web Session

Complete mission

Description

Manual override only: closes an in-progress mission without proving device success. Requires JSON {"mission":{"reason":"operator-entered explanation"}}; records actor/reason in audit and execution_result, sets execution_status=overridden, and fences late device outcomes.

Auth

Web Session
Request Body
Field Type Required Description
mission object Yes Manual override
mission.reason string Yes Nonblank operator-entered reason
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Manual completion override recorded

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

Invalid mission status or missing, blank, or non-string override reason (override_reason_required)

404

Mission not found

POST
/api/v1/operator/missions/:id/fail Web Session

Fail mission

Description

Manual override only: closes an in-progress or paused mission without proving device failure. Requires JSON {"mission":{"reason":"operator-entered explanation"}}; records actor/reason in audit and execution_result and sets execution_status=overridden.

Auth

Web Session
Request Body
Field Type Required Description
mission object Yes Manual override
mission.reason string Yes Nonblank operator-entered reason
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Manual failure override recorded

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

Invalid mission status or missing, blank, or non-string override reason (override_reason_required)

404

Mission not found

GET
/api/v1/operator/robots/:id/diagnostics Web Session

List robot diagnostics

Description

Returns diagnostic metrics for a robot, with optional metric name, status, and time range filters. Newest first. Filters are accepted flat or as filter[...]; filter[limit] defaults to 100 and is clamped to 500 (a top-level limit is ignored).

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Robot UUID
metric_name query string No Filter by metric name (e.g. battery_health, motor_temp)
status query string No Filter by diagnostic status
since query datetime (ISO 8601) No ISO 8601 datetime lower bound for recorded_at
cohort_id query string No Restrict to entities whose robot belongs to this cohort, statically or by rule
tag query string No Restrict to entities whose robot carries this tag (lowercased and trimmed). Exactly one tag; a value that does not resolve to a single tag matches nothing rather than being ignored.
Responses
200

List of diagnostics

Returns: DiagnosticList

Example
{
  "diagnostics": [
    {
      "id": "b3ec0b4e-64be-44f3-a5f6-e6f54c8bf190",
      "inserted_at": "2026-03-27T09:11:30Z",
      "metadata": {},
      "metric_name": "battery_health",
      "metric_value": 76.5,
      "recorded_at": "2026-03-27T09:11:30Z",
      "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "status": "warning",
      "unit": "percent",
      "updated_at": "2026-03-27T09:11:30Z",
      "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
    }
  ]
}
POST
/api/v1/operator/robots/:id/diagnostics Web Session

Record robot diagnostic

Description

Records a new diagnostic metric reading for a robot.

Auth

Web Session
Request Body
Field Type Required Description
diagnostic object No Optional wrapper. The same fields are accepted at the top level when the wrapper is omitted.
diagnostic.status string (normal, warning, critical) No Diagnostic status
diagnostic.unit string No Unit of measurement (e.g. %, C, mm)
diagnostic.metadata object No Arbitrary metadata key-value pairs
diagnostic.metric_name string Yes Metric name (e.g. battery_health, motor_temp)
diagnostic.metric_value number Yes Numeric metric value
diagnostic.recorded_at datetime (ISO 8601) No When the metric was recorded (defaults to now). At most 7 days in the past and 5 minutes in the future; otherwise 422.
Parameters
Name In Type Required Description
id path string Yes Robot UUID
Responses
201

Diagnostic recorded

Returns: Diagnostic

Example
{
  "id": "b3ec0b4e-64be-44f3-a5f6-e6f54c8bf190",
  "inserted_at": "2026-03-27T09:11:30Z",
  "metadata": {},
  "metric_name": "battery_health",
  "metric_value": 76.5,
  "recorded_at": "2026-03-27T09:11:30Z",
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "status": "warning",
  "unit": "percent",
  "updated_at": "2026-03-27T09:11:30Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Robot not found

410

The robot has been decommissioned and can no longer report

422

Validation error

GET
/api/v1/operator/robots/:id/health Web Session

Robot health summary

Description

Returns the latest metric reading for each diagnostic category and an overall health status.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Robot UUID
Responses
200

Health summary with per-metric latest readings

Returns: RobotHealthSummary

Example
{
  "metrics": [
    {
      "metric_name": "battery_health",
      "metric_value": 76.5,
      "recorded_at": "2026-03-27T09:11:30Z",
      "status": "warning",
      "unit": "percent"
    }
  ],
  "overall_status": "warning",
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22"
}
404

Robot not found

GET
/api/v1/operator/fleet-alerts Web Session

List fleet alerts

Description

Returns fleet alerts for the workspace newest first, with optional filters for severity, status, robot, and alert type. Always bounded: `limit` defaults to 100 and is clamped to 500.

Auth

Web Session
Parameters
Name In Type Required Description
severity query string No Filter by severity
status query string No Filter by alert status
robot_id query string No Filter by robot UUID
alert_type query string No Filter by alert type
cohort_id query string No Restrict to entities whose robot belongs to this cohort, statically or by rule
tag query string No Restrict to entities whose robot carries this tag (lowercased and trimmed). Exactly one tag; a value that does not resolve to a single tag matches nothing rather than being ignored.
limit query integer No Maximum fleet alerts to return, newest first (default 100, server maximum 500)
Responses
200

List of fleet alerts

Returns: FleetAlertList

Example
{
  "alerts": [
    {
      "acknowledged_at": null,
      "acknowledged_by": null,
      "alert_type": "battery_low",
      "id": "804db680-c8f5-4280-b147-35fca5f754a6",
      "inserted_at": "2026-03-27T09:12:00Z",
      "message": "Battery dropped below 15% during active shift.",
      "metadata": {},
      "resolved_at": null,
      "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "severity": "critical",
      "status": "active",
      "title": "Battery below critical threshold",
      "updated_at": "2026-03-27T09:12:00Z",
      "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
    }
  ]
}
GET
/api/v1/operator/fleet-alerts/:id Web Session

Get fleet alert

Description

Returns details of a specific fleet alert.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Fleet alert UUID
Responses
200

Fleet alert details

Returns: FleetAlert

Example
{
  "acknowledged_at": null,
  "acknowledged_by": null,
  "alert_type": "battery_low",
  "id": "804db680-c8f5-4280-b147-35fca5f754a6",
  "inserted_at": "2026-03-27T09:12:00Z",
  "message": "Battery dropped below 15% during active shift.",
  "metadata": {},
  "resolved_at": null,
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "severity": "critical",
  "status": "active",
  "title": "Battery below critical threshold",
  "updated_at": "2026-03-27T09:12:00Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Alert not found

POST
/api/v1/operator/fleet-alerts/:id/acknowledge Web Session

Acknowledge fleet alert

Description

Acknowledges an active fleet alert, marking it as seen by an operator.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Fleet alert UUID
Responses
200

Alert acknowledged

Returns: FleetAlert

Example
{
  "acknowledged_at": null,
  "acknowledged_by": null,
  "alert_type": "battery_low",
  "id": "804db680-c8f5-4280-b147-35fca5f754a6",
  "inserted_at": "2026-03-27T09:12:00Z",
  "message": "Battery dropped below 15% during active shift.",
  "metadata": {},
  "resolved_at": null,
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "severity": "critical",
  "status": "active",
  "title": "Battery below critical threshold",
  "updated_at": "2026-03-27T09:12:00Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

Invalid alert status

404

Alert not found

POST
/api/v1/operator/fleet-alerts/:id/resolve Web Session

Resolve fleet alert

Description

Resolves an active or acknowledged fleet alert.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Fleet alert UUID
Responses
200

Alert resolved

Returns: FleetAlert

Example
{
  "acknowledged_at": null,
  "acknowledged_by": null,
  "alert_type": "battery_low",
  "id": "804db680-c8f5-4280-b147-35fca5f754a6",
  "inserted_at": "2026-03-27T09:12:00Z",
  "message": "Battery dropped below 15% during active shift.",
  "metadata": {},
  "resolved_at": null,
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "severity": "critical",
  "status": "active",
  "title": "Battery below critical threshold",
  "updated_at": "2026-03-27T09:12:00Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

Invalid alert status

404

Alert not found

GET
/api/v1/operator/fleet/alerts/summary Web Session

Fleet alert summary

Description

Returns counts of fleet alerts grouped by status and severity.

Auth

Web Session
Responses
200

Alert counts by status and severity

Returns: FleetAlertSummary

Example
{
  "acknowledged": 4,
  "active": 3,
  "by_severity": {
    "critical": 3,
    "error": 4,
    "info": 1,
    "warning": 6
  },
  "resolved": 7,
  "total": 14
}
GET
/api/v1/operator/ota-updates Web Session

List OTA updates

Description

Returns OTA updates in the current workspace newest first. Filters are read only from the nested `filter` map — `?status=pending` is ignored. Use `filter[status]`, `filter[update_type]`, `filter[robot_id]`, and `filter[limit]`. Always bounded: `filter[limit]` defaults to 100 and is clamped to 500.

Auth

Web Session
Parameters
Name In Type Required Description
filter[status] query string No Filter by OTA update status. Top-level `?status=` is ignored.
filter[update_type] query string No Filter by update type. Top-level `?update_type=` is ignored.
filter[robot_id] query string No Filter by robot UUID. Top-level `?robot_id=` is ignored.
filter[limit] query integer No Maximum OTA updates to return (default 100, server maximum 500). Top-level `?limit=` is ignored.
Responses
200

List of OTA updates

Returns: OTAUpdateList

Example
{
  "ota_updates": [
    {
      "completed_at": null,
      "delivery_state": "artifact_required",
      "error_message": null,
      "firmware_version": "3.3.0",
      "id": "5ed7cc57-e9a0-44b9-b345-8d8cc6ddf1aa",
      "inserted_at": "2026-03-27T09:12:00Z",
      "metadata": {},
      "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "rollback_version": null,
      "rollout": null,
      "rollout_percentage": 100,
      "scheduled_at": "2026-03-27T09:25:00Z",
      "status": "pending",
      "update_type": "firmware",
      "updated_at": "2026-03-27T09:14:00Z",
      "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
    }
  ]
}
POST
/api/v1/operator/ota-updates Web Session

Schedule OTA update

Description

Compatibility endpoint. With a signed `artifact` object, creates a one-target device-confirmed rollout; without it creates an explicit `artifact_required` non-dispatchable legacy draft. The server never advances device phases itself. Use POST /ota-updates/rollouts for multi-target or cohort selection.

Auth

Web Session
Request Body
Field Type Required Description
ota_update object Yes OTA update attributes
ota_update.update_type string (firmware, software, config, security_patch) Yes Type of update
ota_update.scheduled_at datetime (ISO 8601) No When to apply the update
ota_update.robot_id string Yes Target robot UUID
ota_update.firmware_version string Yes Target firmware/software version
ota_update.rollout_percentage integer No First-wave percentage 0-100 (default 100) when an artifact is provided; 0 stages without dispatch.
Responses
201

OTA update scheduled

Returns: OTAUpdate

Example
{
  "completed_at": null,
  "delivery_state": "artifact_required",
  "error_message": null,
  "firmware_version": "3.3.0",
  "id": "5ed7cc57-e9a0-44b9-b345-8d8cc6ddf1aa",
  "inserted_at": "2026-03-27T09:12:00Z",
  "metadata": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "rollback_version": null,
  "rollout": null,
  "rollout_percentage": 100,
  "scheduled_at": "2026-03-27T09:25:00Z",
  "status": "pending",
  "update_type": "firmware",
  "updated_at": "2026-03-27T09:14:00Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
422

Validation error

POST
/api/v1/operator/ota-updates/rollouts Web Session

Create signed OTA rollout

Description

Snapshots 1-100 eligible robots from robot_ids or cohort_id in stable UUID order. Each attempt is unique; first-wave size is ceil(target_count * rollout_percentage / 100), and later waves require every current target's device-confirmed success. 0% remains staged without dispatch. Only deployment-allowlisted HTTPS hosts are accepted. Devices, not this server, verify bytes, SHA-256 and Ed25519 signature before install.

Auth

Web Session
Request Body
Field Type Required Description
rollout object Yes Rollout attributes; flat body also accepted
rollout.cohort_id string No Workspace cohort UUID; at most 100 resolved robots
rollout.firmware_version string Yes Desired version
rollout.rollout_percentage integer No First-wave percentage 0-100, default 100
rollout.robot_ids array<object> No Explicit robot UUIDs (1-100); exclusive with cohort_id
rollout.artifact object Yes uri (allowlisted HTTPS, no query/userinfo/fragment), sha256 (64 hex), size_bytes (1-2147483648), signature {algorithm: ed25519, key_id, value: base64 64-byte signature}
Responses
201

Rollout created; OTAUpdate compatibility fields plus rollout summary

Returns: OTAUpdate

400

Invalid artifact, targets, percentage, workspace or overlapping OTA

422

Invalid update type or scheduled timestamp

GET
/api/v1/operator/ota-updates/:id/attempts Web Session

Get OTA rollout attempts and progress

Description

Workspace-scoped attempt snapshot with total/completed/failed/staged counts, device phases, wave number, known prior version, command ID, bounded result and error. Artifact URI is never returned.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes OTA update UUID
Responses
200

Rollout attempt progress

404

No rollout for this update

GET
/api/v1/operator/ota-updates/:id Web Session

Get OTA update details

Description

Returns details of a specific OTA update.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes OTA update UUID
Responses
200

OTA update details

Returns: OTAUpdate

Example
{
  "completed_at": null,
  "delivery_state": "artifact_required",
  "error_message": null,
  "firmware_version": "3.3.0",
  "id": "5ed7cc57-e9a0-44b9-b345-8d8cc6ddf1aa",
  "inserted_at": "2026-03-27T09:12:00Z",
  "metadata": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "rollback_version": null,
  "rollout": null,
  "rollout_percentage": 100,
  "scheduled_at": "2026-03-27T09:25:00Z",
  "status": "pending",
  "update_type": "firmware",
  "updated_at": "2026-03-27T09:14:00Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

OTA update not found

POST
/api/v1/operator/ota-updates/:id/rollback Web Session

Rollback OTA update

Description

Requests device-local rollback to the previous version captured at rollout creation. Optional attempt_id selects a workspace-owned install target of this update; omitted selects the legacy representative robot_id. Conflicting robot_id selectors are rejected. Returns 202 rollback_requested; rolled_back is recorded only after matching device ACK. Legacy rows without a known prior version are refused.

Auth

Web Session
Request Body
Field Type Required Description
attempt_id string No Install attempt UUID of a target in this OTA rollout; omit for legacy representative target
Parameters
Name In Type Required Description
id path string Yes OTA update UUID
Responses
202

Rollback requested, awaiting device confirmation

400

Invalid status, missing previous version, or active newer OTA

404

OTA update not found

GET
/api/v1/operator/ota-updates/:id/status Web Session

Get OTA update status

Description

Returns the current status of an OTA update.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes OTA update UUID
Responses
200

OTA update status

Returns: OTAUpdate

Example
{
  "completed_at": null,
  "delivery_state": "artifact_required",
  "error_message": null,
  "firmware_version": "3.3.0",
  "id": "5ed7cc57-e9a0-44b9-b345-8d8cc6ddf1aa",
  "inserted_at": "2026-03-27T09:12:00Z",
  "metadata": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "rollback_version": null,
  "rollout": null,
  "rollout_percentage": 100,
  "scheduled_at": "2026-03-27T09:25:00Z",
  "status": "pending",
  "update_type": "firmware",
  "updated_at": "2026-03-27T09:14:00Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

OTA update not found

PUT
/api/v1/operator/missions/:id/waypoints Web Session

Update mission waypoints

Description

Replaces the waypoints for a mission with the provided list. Each waypoint must be an object with a numeric lat (-90 to 90) and lng (-180 to 180), plus an optional action string and params object. A body that is not a list, or holds any other entry, is refused with 422 and the stored waypoints are left unchanged.

Auth

Web Session
Request Body
Field Type Required Description
waypoints array<object> Yes List of waypoints
waypoints[].params object No Additional parameters for the action
waypoints[].action string No Action at waypoint (pickup, deliver, etc.)
waypoints[].lat number Yes Latitude
waypoints[].lng number Yes Longitude
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Waypoints updated

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Mission not found

422

Validation error

POST
/api/v1/operator/missions/:id/auto-assign Web Session

Auto-assign mission to robot

Description

Automatically assigns the best available online robot to a pending mission. Selection prefers higher battery and, when `payload.required_capabilities` is a non-empty array, robots whose capabilities overlap that list.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Mission UUID
Responses
200

Mission auto-assigned

Returns: Mission

Example
{
  "command_id": "a7c54e2a-c050-4e38-b7d1-1a1e7a35f631",
  "completed_at": null,
  "control_action": null,
  "control_command_id": null,
  "control_result": {},
  "control_status": null,
  "description": "Collect 3 bins from aisle A and deliver to packing.",
  "execution_result": {},
  "execution_status": "queued",
  "id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
  "inserted_at": "2026-03-27T09:10:00Z",
  "name": "Pick order #4821",
  "payload": {
    "order_id": "4821"
  },
  "priority": "high",
  "requested_by_user_id": "9cbce85e-c65e-4b0b-8cf3-951606331c6e",
  "result": {},
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "scheduled_at": "2026-03-27T09:20:00Z",
  "started_at": null,
  "status": "assigned",
  "trace_id": "6646e3ff-8f2f-4204-ad5c-f4ebc1830f9e",
  "updated_at": "2026-03-27T09:12:00Z",
  "waypoints": {
    "waypoints": [
      {
        "lat": 41.717,
        "lng": 44.793,
        "order": 0
      }
    ]
  },
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400

No available robots or invalid status

POST
/api/v1/operator/missions/auto-assign-all Web Session

Auto-assign all pending missions

Description

Automatically assigns pending missions to available online robots, most urgent first, and returns one result object per mission. Up to 500 pending missions are handled per call; call again to work through a longer backlog. Each result is `{status: "ok", mission_id, robot_id}` or `{status: "error", mission_id, reason}` — `reason` is `no_available_robot` once the free robots (or those with the required capabilities) have been handed out.

Auth

Web Session
Responses
200

Assignment results, one object per pending mission handled.

Returns: MissionAutoAssignResults

Example
{
  "results": [
    {
      "mission_id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b1",
      "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "status": "ok"
    },
    {
      "mission_id": "d11b00f4-74fd-4fe5-a2d2-0f0b8ef8d6b2",
      "reason": "no_available_robot",
      "status": "error"
    }
  ]
}
GET
/api/v1/operator/robots/:id/maintenance-check Web Session

Check robot maintenance needs

Description

Analyzes recent diagnostics for a robot and predicts maintenance needs. Creates fleet alerts for detected issues.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Robot UUID
Responses
200

Maintenance analysis results

Returns: MaintenanceCheckResult

Example
{
  "alerts_created": 1,
  "predictions": {
    "battery_health": "warning",
    "critical_metrics": "normal",
    "motor_temperature": "normal"
  },
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22"
}
404

Robot not found

GET
/api/v1/operator/fleet/maintenance-predictions Web Session

Fleet maintenance predictions

Description

Returns all robots predicted to need maintenance soon based on recent diagnostic trends.

Auth

Web Session
Responses
200

List of robots needing maintenance

Returns: FleetMaintenancePredictions

Example
{
  "predictions": [
    {
      "predictions": {
        "battery_health": "warning",
        "critical_metrics": "normal",
        "motor_temperature": "normal"
      },
      "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22"
    }
  ]
}
GET
/api/v1/operator/cohorts Web Session

List cohorts

Description

Returns the groups and sites defined in the current workspace, each with its static member count.

Auth

Web Session
Parameters
Name In Type Required Description
kind query string No Filter by cohort kind
query query string No Case-insensitive match on name or description
limit query integer No Maximum cohorts to return (default 100, server maximum 500)
Responses
200

List of cohorts

Returns: CohortList

Example
{
  "cohorts": [
    {
      "description": "Cold storage aisles",
      "id": "0b1f4a52-9de7-4a2f-93de-4a94f38f4a11",
      "inserted_at": "2026-08-03T08:00:00Z",
      "kind": "site",
      "member_count": 2,
      "name": "Warehouse B",
      "rules": {
        "robot_type": "amr",
        "tags_any": [
          "night-shift"
        ]
      },
      "site_meta": {
        "building": "B",
        "floor": "1",
        "zone": "cold-storage"
      },
      "updated_at": "2026-08-03T08:00:00Z",
      "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
    }
  ]
}
POST
/api/v1/operator/cohorts Web Session

Create cohort

Description

Creates a group or site cohort. Names are unique per workspace. Supplying `rules` makes membership dynamic: robots matching the rule appear as members at read time without membership rows.

Auth

Web Session
Request Body
Field Type Required Description
name string Yes Cohort name (2-120 characters)
description string No Free-form description
kind string (group, site) No Cohort kind (defaults to group)
rules object No Dynamic membership rule. Supported keys: robot_type, manufacturer, model, firmware_version, capabilities (array), tags_any (array). Keys combine with AND.
site_meta object No Site metadata: building, floor, zone. Other keys are ignored.
Responses
201

Cohort created

Returns: Cohort

Example
{
  "description": "Cold storage aisles",
  "id": "0b1f4a52-9de7-4a2f-93de-4a94f38f4a11",
  "inserted_at": "2026-08-03T08:00:00Z",
  "kind": "site",
  "member_count": 2,
  "name": "Warehouse B",
  "rules": {
    "robot_type": "amr",
    "tags_any": [
      "night-shift"
    ]
  },
  "site_meta": {
    "building": "B",
    "floor": "1",
    "zone": "cold-storage"
  },
  "updated_at": "2026-08-03T08:00:00Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
422

Validation error

GET
/api/v1/operator/cohorts/:id Web Session

Get cohort details

Description

Returns a cohort with a bounded page of its static membership rows. Rule-matched members are not membership rows and are not listed here — query the robots endpoint with `cohort_id` for the full membership.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Cohort UUID
limit query integer No Maximum membership rows to return (default 100, server maximum 500)
Responses
200

Cohort details

Returns: Cohort

Example
{
  "description": "Cold storage aisles",
  "id": "0b1f4a52-9de7-4a2f-93de-4a94f38f4a11",
  "inserted_at": "2026-08-03T08:00:00Z",
  "kind": "site",
  "member_count": 2,
  "name": "Warehouse B",
  "rules": {
    "robot_type": "amr",
    "tags_any": [
      "night-shift"
    ]
  },
  "site_meta": {
    "building": "B",
    "floor": "1",
    "zone": "cold-storage"
  },
  "updated_at": "2026-08-03T08:00:00Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Cohort not found

PATCH
/api/v1/operator/cohorts/:id Web Session

Update cohort

Description

Updates a cohort's name, kind, description, site metadata or rules.

Auth

Web Session
Request Body
Field Type Required Description
cohort object Yes Cohort attributes to update
cohort.name string No Cohort name
cohort.description string No Description
cohort.kind string (group, site) No Cohort kind
cohort.rules object No Dynamic membership rule, or null to make membership static only
cohort.site_meta object No building / floor / zone
Parameters
Name In Type Required Description
id path string Yes Cohort UUID
Responses
200

Cohort updated

Returns: Cohort

Example
{
  "description": "Cold storage aisles",
  "id": "0b1f4a52-9de7-4a2f-93de-4a94f38f4a11",
  "inserted_at": "2026-08-03T08:00:00Z",
  "kind": "site",
  "member_count": 2,
  "name": "Warehouse B",
  "rules": {
    "robot_type": "amr",
    "tags_any": [
      "night-shift"
    ]
  },
  "site_meta": {
    "building": "B",
    "floor": "1",
    "zone": "cold-storage"
  },
  "updated_at": "2026-08-03T08:00:00Z",
  "workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
404

Cohort not found

422

Validation error

DELETE
/api/v1/operator/cohorts/:id Web Session

Delete cohort

Description

Deletes a cohort and its membership rows only when no geofence definition, including archived history, references it. Member robots and service agents are untouched; a referenced cohort returns a normal 422 instead of silently widening a safety rule.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Cohort UUID
Responses
204

Cohort deleted

404

Cohort not found

422

Cohort has geofences

POST
/api/v1/operator/cohorts/:cohort_id/members Web Session

Add cohort member

Description

Adds a robot or a service agent to a cohort. Supply exactly one of `robot_id` / `service_agent_id`. Robots that have not been claimed yet can be added — a linked service agent is not required.

Auth

Web Session
Request Body
Field Type Required Description
service_agent_id string No Service agent UUID to enroll
robot_id string No Robot UUID to enroll
Parameters
Name In Type Required Description
cohort_id path string Yes Cohort UUID
Responses
201

Member added

Returns: CohortMembership

Example
{
  "cohort_id": "0b1f4a52-9de7-4a2f-93de-4a94f38f4a11",
  "id": "6d1b1a5e-0f27-4f2b-9f4b-4f2f0a5f9c31",
  "inserted_at": "2026-08-03T08:00:00Z",
  "robot_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
  "service_agent_id": null
}
400

Member does not belong to this workspace

404

Cohort not found

422

Validation error

GET
/api/v1/operator/batch_operations Web Session

List bulk operations

Description

Returns bulk fleet operations newest first, with their live counts. Always bounded: `limit` defaults to 25 and is clamped to 100.

Auth

Web Session
Parameters
Name In Type Required Description
status query string No Filter by batch status
kind query string No Filter by action kind
limit query integer No Maximum batches to return (default 25, server maximum 100)
Responses
200

List of bulk operations

Returns: BatchOperationList

Example
{
  "batch_operations": [
    {
      "failed_count": 1,
      "filter_snapshot": {
        "cohort_id": "6f1c0f2a-1b2c-4d5e-8f90-1a2b3c4d5e6f"
      },
      "id": "0a0f1f5c-4f4c-4a2e-8f2b-6c0a2c1f0d11",
      "kind": "apply_tags",
      "params": {
        "tags": [
          "audit-2026q3"
        ]
      },
      "parent_id": null,
      "progress": {
        "failed": 1,
        "queued": 0,
        "running": 0,
        "skipped": 1,
        "succeeded": 40
      },
      "reason": "Q3 physical audit",
      "selection_mode": "filter",
      "skipped_count": 1,
      "status": "completed_with_errors",
      "succeeded_count": 40,
      "targets": [
        {
          "attempts": 1,
          "error": null,
          "id": "d5a1c7f0-1e2d-4c3b-9a8f-7e6d5c4b3a21",
          "skip_reason": "robot_decommissioned",
          "status": "skipped",
          "target_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
          "target_type": "robot"
        }
      ],
      "total_count": 42,
      "workspace_id": "9a2f0d3e-2b7b-4a1f-9c2a-9a0b1c2d3e4f"
    }
  ]
}
POST
/api/v1/operator/batch_operations Web Session

Start a bulk operation

Description

Applies one action to many robots and returns immediately; the work runs on the `fleet` queue and progress is readable from the show endpoint or the `fleet.batch_operation.*` realtime events. Supply exactly one selection: `robot_ids` (an explicit list) or `filter` (the same filter the fleet board uses). A `filter` selection is **snapshotted at creation** — robots that start matching afterwards are not swept in. Selections are capped at 5000 targets. Decommissioned robots are never targeted, and a robot retired between selection and execution is recorded as `skipped` rather than mutated. `set_status` accepts only operator-settable statuses (online, offline, charging, error, maintenance); `pending` and `decommissioned` belong to the claim and decommission flows and are rejected.

Auth

Web Session
Request Body
Field Type Required Description
reason string No Why this was run. Recorded on the audit event (max 500 characters).
filter object No Fleet-board filter to expand into targets: `query`, `status`, `robot_type`, `cohort_id`, `tag`. An empty map means every in-service robot. Any other key, or a value the fleet board would reject, returns 400 `invalid_filter`.
params object Yes Action parameters: `status` for set_status; `tags` (array) for apply_tags/remove_tags; `cohort_id` for add_to_cohort/remove_from_cohort; `command_type` and optional `payload` for dispatch_command; none for acknowledge_alerts.
kind string (set_status, apply_tags, remove_tags, add_to_cohort, remove_from_cohort, dispatch_command, acknowledge_alerts) Yes The action to apply to every target
robot_ids array<object> No Explicit robot UUIDs. Ids outside this workspace and retired units are dropped.
Responses
201

Batch created and queued

Returns: BatchOperation

Example
{
  "failed_count": 1,
  "filter_snapshot": {
    "cohort_id": "6f1c0f2a-1b2c-4d5e-8f90-1a2b3c4d5e6f"
  },
  "id": "0a0f1f5c-4f4c-4a2e-8f2b-6c0a2c1f0d11",
  "kind": "apply_tags",
  "params": {
    "tags": [
      "audit-2026q3"
    ]
  },
  "parent_id": null,
  "progress": {
    "failed": 1,
    "queued": 0,
    "running": 0,
    "skipped": 1,
    "succeeded": 40
  },
  "reason": "Q3 physical audit",
  "selection_mode": "filter",
  "skipped_count": 1,
  "status": "completed_with_errors",
  "succeeded_count": 40,
  "targets": [
    {
      "attempts": 1,
      "error": null,
      "id": "d5a1c7f0-1e2d-4c3b-9a8f-7e6d5c4b3a21",
      "skip_reason": "robot_decommissioned",
      "status": "skipped",
      "target_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "target_type": "robot"
    }
  ],
  "total_count": 42,
  "workspace_id": "9a2f0d3e-2b7b-4a1f-9c2a-9a0b1c2d3e4f"
}
400

Unknown kind, invalid params for the kind, an invalid filter, an empty selection, or more than 5000 targets

GET
/api/v1/operator/batch_operations/:id Web Session

Get bulk operation progress

Description

Returns a batch with counts derived from its target rows and a bounded page of per-target outcomes, each carrying its error or skip reason.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Batch operation UUID
target_status query string No Only return targets in this state
target_limit query integer No Maximum target rows to return (default 100, server maximum 500)
Responses
200

Batch with progress and targets

Returns: BatchOperation

Example
{
  "failed_count": 1,
  "filter_snapshot": {
    "cohort_id": "6f1c0f2a-1b2c-4d5e-8f90-1a2b3c4d5e6f"
  },
  "id": "0a0f1f5c-4f4c-4a2e-8f2b-6c0a2c1f0d11",
  "kind": "apply_tags",
  "params": {
    "tags": [
      "audit-2026q3"
    ]
  },
  "parent_id": null,
  "progress": {
    "failed": 1,
    "queued": 0,
    "running": 0,
    "skipped": 1,
    "succeeded": 40
  },
  "reason": "Q3 physical audit",
  "selection_mode": "filter",
  "skipped_count": 1,
  "status": "completed_with_errors",
  "succeeded_count": 40,
  "targets": [
    {
      "attempts": 1,
      "error": null,
      "id": "d5a1c7f0-1e2d-4c3b-9a8f-7e6d5c4b3a21",
      "skip_reason": "robot_decommissioned",
      "status": "skipped",
      "target_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "target_type": "robot"
    }
  ],
  "total_count": 42,
  "workspace_id": "9a2f0d3e-2b7b-4a1f-9c2a-9a0b1c2d3e4f"
}
404

Batch not found

POST
/api/v1/operator/batch_operations/:id/cancel Web Session

Cancel a bulk operation

Description

Stops a queued or running batch. Cancellation is cooperative: targets already claimed by the running chunk finish, every target still queued becomes `skipped`, and the batch records who cancelled it. A batch that already finished returns 400.

Auth

Web Session
Request Body
Field Type Required Description
reason string No Optional cancellation reason, stored trimmed with credentials redacted, at most 500 characters
Parameters
Name In Type Required Description
id path string Yes Batch operation UUID
Responses
200

Batch cancelled

Returns: BatchOperation

Example
{
  "failed_count": 1,
  "filter_snapshot": {
    "cohort_id": "6f1c0f2a-1b2c-4d5e-8f90-1a2b3c4d5e6f"
  },
  "id": "0a0f1f5c-4f4c-4a2e-8f2b-6c0a2c1f0d11",
  "kind": "apply_tags",
  "params": {
    "tags": [
      "audit-2026q3"
    ]
  },
  "parent_id": null,
  "progress": {
    "failed": 1,
    "queued": 0,
    "running": 0,
    "skipped": 1,
    "succeeded": 40
  },
  "reason": "Q3 physical audit",
  "selection_mode": "filter",
  "skipped_count": 1,
  "status": "completed_with_errors",
  "succeeded_count": 40,
  "targets": [
    {
      "attempts": 1,
      "error": null,
      "id": "d5a1c7f0-1e2d-4c3b-9a8f-7e6d5c4b3a21",
      "skip_reason": "robot_decommissioned",
      "status": "skipped",
      "target_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "target_type": "robot"
    }
  ],
  "total_count": 42,
  "workspace_id": "9a2f0d3e-2b7b-4a1f-9c2a-9a0b1c2d3e4f"
}
400

The batch already finished

404

Batch not found

POST
/api/v1/operator/batch_operations/:id/retry_failed Web Session

Retry a bulk operation's failures

Description

Creates a child batch — same kind, same params, `parent_id` pointing at the original — carrying only the targets that failed. The original batch is left exactly as it was, so the record of the first attempt survives. Each failure is retried once from its batch: targets an earlier child already carries are left out, so repeating the call never re-dispatches to the same robots, and a failure that fails again is retried from that child. Returns 400 `no_failed_targets` when nothing failed and 400 `already_retried` when every failure already has a retry.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Batch operation UUID
Responses
201

Child batch created

Returns: BatchOperation

Example
{
  "failed_count": 1,
  "filter_snapshot": {
    "cohort_id": "6f1c0f2a-1b2c-4d5e-8f90-1a2b3c4d5e6f"
  },
  "id": "0a0f1f5c-4f4c-4a2e-8f2b-6c0a2c1f0d11",
  "kind": "apply_tags",
  "params": {
    "tags": [
      "audit-2026q3"
    ]
  },
  "parent_id": null,
  "progress": {
    "failed": 1,
    "queued": 0,
    "running": 0,
    "skipped": 1,
    "succeeded": 40
  },
  "reason": "Q3 physical audit",
  "selection_mode": "filter",
  "skipped_count": 1,
  "status": "completed_with_errors",
  "succeeded_count": 40,
  "targets": [
    {
      "attempts": 1,
      "error": null,
      "id": "d5a1c7f0-1e2d-4c3b-9a8f-7e6d5c4b3a21",
      "skip_reason": "robot_decommissioned",
      "status": "skipped",
      "target_id": "d2c9a4df-c6ad-4016-9ba4-30c1c6d53a22",
      "target_type": "robot"
    }
  ],
  "total_count": 42,
  "workspace_id": "9a2f0d3e-2b7b-4a1f-9c2a-9a0b1c2d3e4f"
}
400

The batch has no failed targets, or every failure was already retried

404

Batch not found

DELETE
/api/v1/operator/cohorts/:cohort_id/members/:membership_id Web Session

Remove cohort member

Description

Removes a membership row. The member entity itself is untouched.

Auth

Web Session
Parameters
Name In Type Required Description
cohort_id path string Yes Cohort UUID
membership_id path string Yes Membership UUID
Responses
204

Member removed

404

Membership not found

GET
/api/v1/operator/geofences Web Session

List workspace geofences

Description

Bounded definitions (25 by default, at most 100 per page); filter by active or cohort_id.

Auth

Web Session
Parameters
Name In Type Required Description
limit query string No Maximum definitions (1..100)
offset query string No Offset (0..10000)
active query string No true or false
cohort_id query string No Same-workspace cohort UUID
Responses
200

Workspace geofences

Example
{
  "geofences": [
    {
      "active": true,
      "breach_on": "exit",
      "cohort_id": null,
      "geometry": {
        "center": [
          44.8,
          41.7
        ],
        "radius_m": 500,
        "type": "Circle"
      },
      "id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
      "name": "Depot"
    }
  ]
}
POST
/api/v1/operator/geofences Web Session

Create geofence

Description

Creates a workspace-wide or cohort-scoped safety rule. At most 100 definitions may be active per workspace; inactive definitions do not count. GeoJSON-like Circle {type, center: [lng, lat], radius_m: 1..100000} or Polygon {type, coordinates: [[[lng, lat], ...]]}; one simple ring of 3..100 vertices, no holes or antimeridian crossing. Boundaries count as inside.

Auth

Web Session
Request Body
Field Type Required Description
geofence object No Definition; wrapper optional
geofence.active boolean No Whether this rule evaluates, default true
geofence.name string Yes 2..120 character name
geofence.cohort_id string No Optional same-workspace site or group UUID
geofence.breach_on string (enter, exit) No Exit keeps robots inside (default); enter keeps them out
geofence.geometry object Yes GeoJSON-like Circle {type, center: [lng, lat], radius_m: 1..100000} or Polygon {type, coordinates: [[[lng, lat], ...]]}; one simple ring of 3..100 vertices, no holes or antimeridian crossing. Boundaries count as inside.
Responses
201

Geofence created

Example
{
  "active": true,
  "breach_on": "exit",
  "cohort_id": null,
  "geometry": {
    "center": [
      44.8,
      41.7
    ],
    "radius_m": 500,
    "type": "Circle"
  },
  "id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
  "name": "Depot"
}
422

Invalid geometry, cohort, or active cap

GET
/api/v1/operator/geofences/:id Web Session

Get geofence

Description

GeoJSON-like Circle {type, center: [lng, lat], radius_m: 1..100000} or Polygon {type, coordinates: [[[lng, lat], ...]]}; one simple ring of 3..100 vertices, no holes or antimeridian crossing. Boundaries count as inside.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Geofence UUID
Responses
200

Geofence

Example
{
  "active": true,
  "breach_on": "exit",
  "cohort_id": null,
  "geometry": {
    "center": [
      44.8,
      41.7
    ],
    "radius_m": 500,
    "type": "Circle"
  },
  "id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
  "name": "Depot"
}
404

Not found

PATCH
/api/v1/operator/geofences/:id Web Session

Update geofence

Description

Edits bump the evaluation revision so stale robot boundary state cannot suppress a new breach. GeoJSON-like Circle {type, center: [lng, lat], radius_m: 1..100000} or Polygon {type, coordinates: [[[lng, lat], ...]]}; one simple ring of 3..100 vertices, no holes or antimeridian crossing. Boundaries count as inside.

Auth

Web Session
Request Body
Field Type Required Description
geofence object No Changed fields; wrapper optional
geofence.active boolean No Whether this rule evaluates, default true
geofence.name string Yes 2..120 character name
geofence.cohort_id string No Optional same-workspace site or group UUID
geofence.breach_on string (enter, exit) No Exit keeps robots inside (default); enter keeps them out
geofence.geometry object Yes GeoJSON-like Circle {type, center: [lng, lat], radius_m: 1..100000} or Polygon {type, coordinates: [[[lng, lat], ...]]}; one simple ring of 3..100 vertices, no holes or antimeridian crossing. Boundaries count as inside.
Parameters
Name In Type Required Description
id path string Yes Geofence UUID
Responses
200

Updated geofence

Example
{
  "active": true,
  "breach_on": "exit",
  "cohort_id": null,
  "geometry": {
    "center": [
      44.8,
      41.7
    ],
    "radius_m": 500,
    "type": "Circle"
  },
  "id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
  "name": "Depot"
}
422

Validation error

DELETE
/api/v1/operator/geofences/:id Web Session

Delete geofence

Description

Archives the definition and preserves its boundary state, historical fleet alerts, location samples and committed event evidence. Open incidents are not automatically resolved: deletion does not certify a robot has returned to safety.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Geofence UUID
Responses
204

Deleted

404

Not found

GET
/api/v1/operator/robots/:id/locations Web Session

Robot location history

Description

Trusted append-only fixes in observed_at DESC, id DESC order. Default limit 25, maximum 100; next_cursor is signed, versioned, workspace/robot-bound, and invalid/tampered cursors return 400. Coordinates use [longitude, latitude]. The response includes current trusted position and at most 100 active per-fence states with numeric transition_version (Unix microseconds), safety_state safe|breached|unknown and geometry/state revisions. Missing state has not been evaluated; unknown means an edited fence awaits a trusted fix. On reconnect hydrate this snapshot first. Key state by workspace+robot+fence; treat its transition_version as an inclusive floor and ignore any event version less than or equal to the snapshot version, even with an unseen event id. Thereafter accept only strictly larger versions and reject older geometry_revision values. Stable envelope ids permit duplicate detection. Realtime/webhook delivery order is not guaranteed.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Robot UUID
limit query string No Page size, 1..100
cursor query string No Opaque signed next_cursor from prior page
Responses
200

Location page

Example
{
  "current": {
    "coordinates": [
      44.8,
      41.7
    ],
    "location": {
      "lat": 41.7,
      "lng": 44.8
    },
    "observed_at": "2026-09-25T12:00:00.000000Z"
  },
  "geofence_states": [
    {
      "alert_id": null,
      "geofence_id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
      "geometry_revision": 1,
      "observed_at": "2026-09-25T12:00:00.000000Z",
      "safety_state": "safe",
      "state_revision": 1,
      "transition_version": 1790337600000000
    }
  ],
  "next_cursor": null,
  "samples": [
    {
      "coordinates": [
        44.8,
        41.7
      ],
      "id": "e8f77d1f-19af-4ded-91ab-b346058e7c31",
      "location": {
        "lat": 41.7,
        "lng": 44.8
      },
      "observed_at": "2026-09-25T12:00:00.000000Z",
      "received_at": "2026-09-25T12:00:01.000000Z",
      "robot_id": "6a05ee70-faa4-40f6-8f14-b49465c2d398"
    }
  ]
}
400

Invalid filter, limit or cursor

404

Robot not found

GET
/api/v1/operator/maintenance-work-orders Web Session

List maintenance work orders

Description

Workspace-scoped newest-first keyset page, 25 by default and at most 100. Return next_cursor to continue; no records are silently hidden. Filter by status, priority, robot_id, alert_id, assignee_user_id, due_before, or due_after.

Auth

Web Session
Parameters
Name In Type Required Description
status query string No Optional status filter
priority query string No Optional priority filter
robot_id query string No Optional robot_id filter
alert_id query string No Optional alert_id filter
assignee_user_id query string No Optional assignee_user_id filter
due_before query string No Optional due_before filter
due_after query string No Optional due_after filter
limit query string No Optional limit filter
cursor query string No Optional cursor filter
Responses
200

Bounded page

Example
{
  "next_cursor": null,
  "work_orders": [
    {
      "id": "e8f77d1f-19af-4ded-91ab-b346058e7c31",
      "priority": "normal",
      "status": "open",
      "robot_id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
      "outcome": null,
      "alert_id": null,
      "assignee_user_id": null,
      "checklist": [
        "Check bearing"
      ],
      "due_at": null,
      "notes": "Inspect motor",
      "prediction_snapshot": {}
    }
  ]
}
POST
/api/v1/operator/maintenance-work-orders Web Session

Create maintenance work order

Description

Operator-only. Link an optional same-robot fleet alert and/or a current warning/critical prediction_category; the latter is snapshotted at creation. Assignee must have an active workspace membership. due_at is ISO8601 UTC and may be in the past for overdue backlog. Only in_progress reserves a robot. Beginning work atomically refuses active missions and reserved OTA attempts; open/scheduled are planning. Completion/cancellation releases the work-order hold but preserves a separate manual hold. Heartbeats cannot clear either hold. A linked robot is restored online only after a heartbeat within 300 seconds; otherwise it remains offline (or pending if unlinked). No prediction or page read creates an order.

Auth

Web Session
Request Body
Field Type Required Description
work_order object No Work-order fields; wrapper optional
work_order.priority string (low, normal, high, critical) No Default normal
work_order.status string (open, scheduled, in_progress, completed, cancelled) No Transition target; completed uses the completion endpoint
work_order.robot_id string Yes Same-workspace robot UUID
work_order.alert_id string No Optional same-robot fleet alert UUID
work_order.assignee_user_id string No Active workspace member UUID
work_order.checklist array<object> No At most 50 items, each at most 200 characters
work_order.due_at datetime (ISO 8601) No ISO8601 due time, including past overdue dates
work_order.notes string No At most 4000 characters
work_order.prediction_category string No Current warning/critical category, snapshotted at creation
Responses
201

Created

Example
{
  "id": "e8f77d1f-19af-4ded-91ab-b346058e7c31",
  "priority": "normal",
  "status": "open",
  "robot_id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
  "outcome": null,
  "alert_id": null,
  "assignee_user_id": null,
  "checklist": [
    "Check bearing"
  ],
  "due_at": null,
  "notes": "Inspect motor",
  "prediction_snapshot": {}
}
422

Invalid evidence, assignee, or fields

GET
/api/v1/operator/maintenance-work-orders/:id Web Session

Get maintenance work order

Description

Read-only for auditors; durable summary and robot/alert link remain after completion or retirement.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Work-order UUID
Responses
200

Work order

Example
{
  "id": "e8f77d1f-19af-4ded-91ab-b346058e7c31",
  "priority": "normal",
  "status": "open",
  "robot_id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
  "outcome": null,
  "alert_id": null,
  "assignee_user_id": null,
  "checklist": [
    "Check bearing"
  ],
  "due_at": null,
  "notes": "Inspect motor",
  "prediction_snapshot": {}
}
404

Not found

PATCH
/api/v1/operator/maintenance-work-orders/:id Web Session

Assign, schedule, begin, or cancel work

Description

Operator-only compare-and-set transition. Allowed: open→scheduled/in_progress/cancelled, scheduled→open/in_progress/cancelled, in_progress→cancelled. Terminal records are immutable; duplicate terminal same-result action is idempotent. Only in_progress reserves a robot. Beginning work atomically refuses active missions and reserved OTA attempts; open/scheduled are planning. Completion/cancellation releases the work-order hold but preserves a separate manual hold. Heartbeats cannot clear either hold. A linked robot is restored online only after a heartbeat within 300 seconds; otherwise it remains offline (or pending if unlinked). No prediction or page read creates an order.

Auth

Web Session
Request Body
Field Type Required Description
work_order object No Changed fields; wrapper optional
work_order.priority string (low, normal, high, critical) No Default normal
work_order.status string (open, scheduled, in_progress, completed, cancelled) No Transition target; completed uses the completion endpoint
work_order.robot_id string Yes Same-workspace robot UUID
work_order.alert_id string No Optional same-robot fleet alert UUID
work_order.assignee_user_id string No Active workspace member UUID
work_order.checklist array<object> No At most 50 items, each at most 200 characters
work_order.due_at datetime (ISO 8601) No ISO8601 due time, including past overdue dates
work_order.notes string No At most 4000 characters
work_order.prediction_category string No Current warning/critical category, snapshotted at creation
Parameters
Name In Type Required Description
id path string Yes Work-order UUID
Responses
200

Updated

Example
{
  "id": "e8f77d1f-19af-4ded-91ab-b346058e7c31",
  "priority": "normal",
  "status": "open",
  "robot_id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
  "outcome": null,
  "alert_id": null,
  "assignee_user_id": null,
  "checklist": [
    "Check bearing"
  ],
  "due_at": null,
  "notes": "Inspect motor",
  "prediction_snapshot": {}
}
409

Robot reserved or order terminal

POST
/api/v1/operator/maintenance-work-orders/:id/complete Web Session

Complete maintenance work order

Description

Operator-only; in_progress required. Outcome is mandatory, completion time and actor are server recorded. Parts/notes/time are durable; an identical retry is idempotent. Only in_progress reserves a robot. Beginning work atomically refuses active missions and reserved OTA attempts; open/scheduled are planning. Completion/cancellation releases the work-order hold but preserves a separate manual hold. Heartbeats cannot clear either hold. A linked robot is restored online only after a heartbeat within 300 seconds; otherwise it remains offline (or pending if unlinked). No prediction or page read creates an order.

Auth

Web Session
Request Body
Field Type Required Description
completion object No Completion fields; wrapper optional
completion.parts array<object> No At most 50 part labels
completion.completion_notes string No At most 4000 characters
completion.outcome string (repaired, inspected, no_fault_found, deferred, other) Yes Required resolution
completion.time_spent_minutes integer No 0..100000
Parameters
Name In Type Required Description
id path string Yes Work-order UUID
Responses
200

Completed

Example
{
  "id": "e8f77d1f-19af-4ded-91ab-b346058e7c31",
  "priority": "normal",
  "status": "open",
  "robot_id": "6a05ee70-faa4-40f6-8f14-b49465c2d398",
  "outcome": null,
  "alert_id": null,
  "assignee_user_id": null,
  "checklist": [
    "Check bearing"
  ],
  "due_at": null,
  "notes": "Inspect motor",
  "prediction_snapshot": {}
}
422

Invalid completion

GET
/api/v1/operator/maintenance-work-orders/:id/events Web Session

Read work-order lifecycle evidence

Description

Recent detailed lifecycle events, newest first and bounded to 25 by default or at most 100. Detail rows become eligible only when both the terminal order's updated_at and the event's inserted_at are older than the workspace's work_order_evidence_retention_days (400 days by default); active-order detail never expires. The response includes archived_audit_url, a workspace-scoped paginated audit link filtered to this work order. The immutable audit counterpart preserves each original event ID, actor, action, statuses, reason, priority, assignee and event time. The work-order outcome, parts and alert/prediction links remain on the summary. Do not infer that an empty recent-events list means no historical transitions.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Work-order UUID
limit query string No Optional limit filter
Responses
200

Recent lifecycle events and archived audit link

Example
{
  "events": [],
  "archived_audit_url": "/api/v1/operator/maintenance-work-orders/WORK_ORDER_UUID/audit-events"
}
GET
/api/v1/operator/maintenance-work-orders/:id/audit-events Web Session

Page immutable work-order lifecycle audit

Description

Workspace/order-bound keyset history ordered by original event time and original work_order_event_id, not the coarse audit insertion time. Default 25, maximum 100. Pass next_cursor unchanged; the signed cursor binds workspace and work order, and a missing/deleted anchor or foreign/tampered cursor returns 400. The response retains actor, action, from/to status, reason, priority/assignee, available robot/alert references, exact event_occurred_at and archival provenance after terminal detail rows expire. reference_provenance maps robot_id and alert_id independently to event_evidence or not_recorded; an explicit historical null still counts as event evidence. Missing refs are never inferred from today's mutable order. Unrecognized legacy evidence keys/types fail closed and leave the detail row for review rather than copying arbitrary data into audit. Audit history has no automatic time purge; explicit data deletion can remove it.

Auth

Web Session
Parameters
Name In Type Required Description
id path string Yes Work-order UUID
limit query string No Optional limit filter
cursor query string No Optional cursor filter
Responses
200

Immutable lifecycle audit page

Example
{
  "next_cursor": null,
  "audit_events": []
}
400

Invalid limit or bound cursor

404

Work order not found in this workspace

Where to go next

see also