API reference
Fleet
One tag: method, path, summary, auth, and scopes. Request and response fields ship in this page — expand a row to read the contract.
Endpoints
5
GET
/api/v1/robots/me
Agent Token
Get authenticated robot
fleet:read
/api/v1/robots/me
Agent Token
Get authenticated robot
Description
Returns the robot linked to the authenticated service-agent identity. The robot ID is derived from the credential and cannot be supplied by the caller.
Auth
Agent TokenRequired Scopes
Responses
200
Authenticated robot
Returns: Robot
Authenticated robot
Returns: Robot
Response Body
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Robot UUID |
| name | string | Yes | Robot name |
| status | string (pending, online, offline, charging, error, maintenance, decommissioned) | Yes | Current robot status. `pending` is a pre-registered unit with no identity yet; `decommissioned` is a retired unit, kept for history but excluded from fleet listings and from the plan's robot quota. |
| metadata | object | Yes | Arbitrary metadata payload |
| location | object | Yes | Robot location payload |
| inserted_at | datetime (ISO 8601) | Yes | Record insertion timestamp |
| updated_at | datetime (ISO 8601) | Yes | Record update timestamp |
| tags | array<string> | Yes | Free-form tags, lowercased and trimmed |
| service_agent_id | string | No | Service agent UUID |
| workspace_id | string | Yes | Workspace UUID |
| capabilities | array<string> | Yes | Declared robot capabilities |
| last_seen_at | datetime (ISO 8601) | No | Last heartbeat timestamp |
| firmware_version | string | No | Current firmware version |
| serial_number | string | Yes | Unique serial number |
| battery_level | integer | Yes | Battery percentage (0-100) |
| manufacturer | string | No | Manufacturer name |
| model | string | No | Model name |
| robot_type | string (amr, drone, manipulator, humanoid) | Yes | Robot type |
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"
}
403
fleet:read scope required
fleet:read scope required
404
Authenticated service agent is not linked to a robot
Authenticated service agent is not linked to a robot
POST
/api/v1/robots/me/heartbeat
Agent Token
Record authenticated robot heartbeat
fleet:write
/api/v1/robots/me/heartbeat
Agent Token
Record authenticated robot heartbeat
Description
Records a heartbeat for the robot owned by the authenticated service-agent identity. Only `battery_level` (0-100), `location`, `firmware_version`, and `metadata` are cast. `status` is forced to `online` and `last_seen_at` is set by the server — a client-supplied status is ignored. The body may be wrapped in `heartbeat` or sent flat.
Auth
Agent TokenRequired Scopes
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 | Current location |
| heartbeat.firmware_version | string | No | Current firmware version |
| heartbeat.battery_level | integer | No | Current battery level (0-100). Values outside that range return 422. |
Responses
200
Robot heartbeat recorded
Returns: Robot
Robot heartbeat recorded
Returns: Robot
Response Body
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Robot UUID |
| name | string | Yes | Robot name |
| status | string (pending, online, offline, charging, error, maintenance, decommissioned) | Yes | Current robot status. `pending` is a pre-registered unit with no identity yet; `decommissioned` is a retired unit, kept for history but excluded from fleet listings and from the plan's robot quota. |
| metadata | object | Yes | Arbitrary metadata payload |
| location | object | Yes | Robot location payload |
| inserted_at | datetime (ISO 8601) | Yes | Record insertion timestamp |
| updated_at | datetime (ISO 8601) | Yes | Record update timestamp |
| tags | array<string> | Yes | Free-form tags, lowercased and trimmed |
| service_agent_id | string | No | Service agent UUID |
| workspace_id | string | Yes | Workspace UUID |
| capabilities | array<string> | Yes | Declared robot capabilities |
| last_seen_at | datetime (ISO 8601) | No | Last heartbeat timestamp |
| firmware_version | string | No | Current firmware version |
| serial_number | string | Yes | Unique serial number |
| battery_level | integer | Yes | Battery percentage (0-100) |
| manufacturer | string | No | Manufacturer name |
| model | string | No | Model name |
| robot_type | string (amr, drone, manipulator, humanoid) | Yes | Robot type |
403
fleet:write scope required
fleet:write scope required
404
Linked robot not found
Linked robot not found
410
The robot row is `decommissioned` but this credential is still valid (belt-and-braces). A normal decommission revokes the identity first, so the same write returns 401 instead. Type is https://robotscenter.net/problems/robot-decommissioned.
The robot row is `decommissioned` but this credential is still valid (belt-and-braces). A normal decommission revokes the identity first, so the same write returns 401 instead. Type is https://robotscenter.net/problems/robot-decommissioned.
422
Validation error (invalid battery_level or other changeset errors)
Validation error (invalid battery_level or other changeset errors)
POST
/api/v1/robots/me/diagnostics
Agent Token
Record authenticated robot diagnostic
fleet:write
/api/v1/robots/me/diagnostics
Agent Token
Record authenticated robot diagnostic
Description
Records a single diagnostic metric for the robot owned by the authenticated service-agent identity. This write is not counted against `telemetry_points_per_month`; it is bounded only by per-credential rate limiting. The body may be wrapped in `diagnostic` or sent flat. `recorded_at` defaults to now and is not clamped to the batch telemetry 7-day / 5-minute window.
Auth
Agent TokenRequired Scopes
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, defaults to normal |
| diagnostic.unit | string | No | Metric unit |
| diagnostic.metadata | object | No | Sensor metadata |
| diagnostic.metric_name | string | Yes | Metric name (free-form, max 120 characters) |
| diagnostic.metric_value | number | Yes | Numeric metric value |
| diagnostic.recorded_at | datetime (ISO 8601) | No | When the metric was recorded (defaults to now). Unlike batch telemetry, this path does not reject timestamps outside a 7-day / 5-minute window. |
Responses
201
Diagnostic recorded
Returns: Diagnostic
Diagnostic recorded
Returns: Diagnostic
Response Body
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Diagnostic UUID |
| status | string (normal, warning, critical) | Yes | Diagnostic status |
| unit | string | No | Metric unit |
| metadata | object | Yes | Diagnostic metadata |
| inserted_at | datetime (ISO 8601) | Yes | Record insertion timestamp |
| updated_at | datetime (ISO 8601) | Yes | Record update timestamp |
| workspace_id | string | Yes | Workspace UUID |
| robot_id | string | Yes | Robot UUID |
| metric_name | string | Yes | Metric name |
| metric_value | number | Yes | Metric numeric value |
| recorded_at | datetime (ISO 8601) | Yes | When the metric was recorded |
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"
}
403
fleet:write scope required
fleet:write scope required
404
Linked robot not found
Linked robot not found
410
The robot row is `decommissioned` but this credential is still valid (belt-and-braces). A normal decommission revokes the identity first, so the same write returns 401 instead. Type is https://robotscenter.net/problems/robot-decommissioned.
The robot row is `decommissioned` but this credential is still valid (belt-and-braces). A normal decommission revokes the identity first, so the same write returns 401 instead. Type is https://robotscenter.net/problems/robot-decommissioned.
422
Validation error
Validation error
POST
/api/v1/robots/me/telemetry
Agent Token
Ingest a telemetry batch
fleet:write
/api/v1/robots/me/telemetry
Agent Token
Ingest a telemetry batch
Description
Records up to 500 metric readings for the robot owned by the authenticated service-agent identity, in one transaction. Readings become ordinary diagnostics, so they show up everywhere single readings do. Idempotency: supply a `batch_id` that is unique per payload. Replaying a `batch_id` already accepted for this robot returns 200 with `accepted: 0` and `duplicate: true` and writes nothing — this holds even if the payload differs, so never reuse a batch id for new readings. Omitting `batch_id` is allowed (the server generates one) but then the request carries no idempotency guarantee. Backfill: `recorded_at` may be up to 7 days in the past, so an offline robot can drain its backlog on reconnect, and at most 5 minutes in the future to absorb clock skew. Readings outside that window are rejected individually. Partial validity: malformed readings are reported per index in `errors` while the valid ones are accepted, and the batch is still consumed for idempotency — a retry of a partially bad batch will not re-insert its good half. Metering: accepted readings count against the workspace's `telemetry_points_per_month` quota, and the request is also subject to per-credential rate limiting.
Auth
Agent TokenRequired Scopes
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| batch_id | string | No | Client-supplied id, unique per payload, at most 200 characters. Replays of an accepted id are acknowledged without writing. A non-string or over-long value is 400 `invalid_batch_id`. |
| readings | array<object> | Yes | Up to 500 readings. An empty list or a larger batch is rejected outright. |
Responses
200
Batch acknowledged
Batch acknowledged
Response Body
| Field | Type | Required | Description |
|---|---|---|---|
| duplicate | boolean | No | True when this batch id was already accepted; nothing was written |
| accepted | integer | No | Readings written |
| errors | array<object> | No | Per-index validation failures for rejected readings |
| batch_id | string | No | Batch id, generated when not supplied |
| robot_id | string | No | Robot the batch was recorded for |
Example
{
"duplicate": false,
"accepted": 2,
"errors": [],
"batch_id": "b-2026-08-03T11:00:00Z-001",
"robot_id": "0f9d1c2e-3b4a-4c5d-8e6f-7a8b9c0d1e2f"
}
400
The batch itself is unusable: empty (`empty_batch`), larger than 500 readings (`batch_too_large`), `readings` is not a list (`invalid_readings`), or `batch_id` is not a string / exceeds 200 characters (`invalid_batch_id`)
The batch itself is unusable: empty (`empty_batch`), larger than 500 readings (`batch_too_large`), `readings` is not a list (`invalid_readings`), or `batch_id` is not a string / exceeds 200 characters (`invalid_batch_id`)
403
fleet:write scope required
fleet:write scope required
404
Linked robot not found
Linked robot not found
410
The robot row is `decommissioned` but this credential is still valid (belt-and-braces). A normal decommission revokes the identity first, so the same write returns 401 instead. Type is https://robotscenter.net/problems/robot-decommissioned.
The robot row is `decommissioned` but this credential is still valid (belt-and-braces). A normal decommission revokes the identity first, so the same write returns 401 instead. Type is https://robotscenter.net/problems/robot-decommissioned.
429
The workspace exceeded its monthly telemetry-points quota, or the credential exceeded its rate limit
The workspace exceeded its monthly telemetry-points quota, or the credential exceeded its rate limit
POST
/api/v1/robots/me/ota-updates/:id/acknowledge
Agent Token
Acknowledge an OTA result
fleet:write
/api/v1/robots/me/ota-updates/:id/acknowledge
Agent Token
Acknowledge an OTA result
Description
Records `completed` or `failed` for an OTA update in `installing`. The robot is derived from the authenticated service-agent credential, and an update assigned to another robot is returned as not found. The body may be wrapped in `acknowledgement` or sent flat.
Auth
Agent TokenRequired Scopes
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| acknowledgement | object | No | Optional wrapper. The same fields are accepted at the top level when omitted. |
| acknowledgement.status | string (completed, failed) | Yes | Terminal device-reported result |
| acknowledgement.error_message | string | No | Failure detail; persisted only when status is failed |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | Yes | OTA update UUID |
Responses
200
OTA result recorded
Returns: OTAUpdate
OTA result recorded
Returns: OTAUpdate
Response Body
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | OTA update UUID |
| status | string (pending, downloading, installing, completed, failed, rolled_back) | Yes | OTA lifecycle status |
| metadata | object | Yes | OTA metadata payload |
| update_type | string (firmware, software, config, security_patch) | Yes | OTA update type |
| error_message | string | No | Failure detail |
| inserted_at | datetime (ISO 8601) | Yes | Record insertion timestamp |
| updated_at | datetime (ISO 8601) | Yes | Record update timestamp |
| completed_at | datetime (ISO 8601) | No | Completion timestamp |
| scheduled_at | datetime (ISO 8601) | No | Scheduled timestamp |
| workspace_id | string | Yes | Workspace UUID |
| robot_id | string | No | Robot UUID |
| firmware_version | string | Yes | Target version |
| rollback_version | string | No | Version rolled back from |
| rollout_percentage | integer | Yes | Rollout percentage |
Example
{
"completed_at": null,
"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_percentage": 100,
"scheduled_at": "2026-03-27T09:25:00Z",
"status": "downloading",
"update_type": "firmware",
"updated_at": "2026-03-27T09:14:00Z",
"workspace_id": "4ce4d170-e6d5-4118-8f4d-fac69dc099ea"
}
400
Status is not completed/failed or the update is not installing
Status is not completed/failed or the update is not installing
403
fleet:write scope required
fleet:write scope required
404
Update not found for the authenticated robot
Update not found for the authenticated robot