Platform / Fleet
Telemetry ingest, rollups, and retention
Robots report metric readings in batches rather than one request per sample. Batches are idempotent on a client-supplied batch_id, accept a backlog up to seven days old, and are aggregated into minute and hour rollups that outlive the raw rows behind them.
01 Request
referencePOST /api/v1/robots/me/telemetry
Authenticated with the robot's own credential and requiring the fleet:write scope. The robot is resolved from the authenticated identity; there is no robot id in the request. The body may be flat or wrapped in a telemetry object.
| Field | Required | Rules |
|---|---|---|
| batch_id | No | Client-supplied, at most 200 characters. Omitted or blank means the server generates one and no dedupe is possible. |
| readings | Yes | 1 to 500 readings. An empty or larger list is rejected outright. |
| readings[].metric_name | Yes | Free-form, at most 120 characters |
| readings[].metric_value | Yes | Number |
| readings[].unit | No | At most 40 characters |
| readings[].status | No | normal, warning, or critical; defaults to normal |
| readings[].recorded_at | No | ISO 8601. Defaults to receipt time. Accepted from 7 days in the past to 5 minutes in the future. |
| readings[].metadata | No | Object |
02 Batch example
examplePOST /api/v1/robots/me/telemetry
Authorization: Bearer {robot_credential}
{
"batch_id": "b-2026-08-03T11:00:00Z-001",
"readings": [
{"metric_name": "battery_level", "metric_value": 82.5, "unit": "percent"},
{"metric_name": "motor_temperature", "metric_value": 61.2, "unit": "celsius",
"status": "warning", "recorded_at": "2026-08-03T10:59:30Z"}
]
}
Response 200:
{
"robot_id": "0f9d1c2e-3b4a-4c5d-8e6f-7a8b9c0d1e2f",
"batch_id": "b-2026-08-03T11:00:00Z-001",
"accepted": 2,
"duplicate": false,
"errors": []
}
03 Idempotency and partial batches
detailsReplaying a batch_id is acknowledged, not re-ingested
A batch_id already accepted for that robot returns 200 with accepted 0, duplicate true, and an empty errors list. Nothing is written a second time. This holds even if the replayed payload differs, so a store-and-forward client can retry a batch it is unsure about.
Malformed readings are reported per index
Valid readings in a mixed batch are accepted; invalid ones come back in errors as {"index": 0, "errors": ["metric_name can't be blank"]}. The batch is still recorded as consumed, so a retry cannot re-insert the half that succeeded.
04 Error responses
reference| Status | Response field and value | When |
|---|---|---|
| 400 | detail: "batch_too_large" | More than 500 readings |
| 400 | detail: "empty_batch" | readings is empty |
| 400 | detail: "invalid_readings" | readings is not a list |
| 400 | detail: "invalid_batch_id" | batch_id is not a string, or exceeds 200 characters |
| 403 | detail: "The token does not have sufficient scope" | Missing fleet:write; extra.required_scopes names it |
| 403 | error: "workspace_frozen" / "workspace_paused" / "workspace_archived" | The workspace is not accepting machine traffic. detail is the sentence form of the same state. |
| 404 | detail: "Resource not found" | The credential has no linked robot |
| 410 | title: "Robot decommissioned", type: problems/robot-decommissioned | The robot is retired. detail explains that the record is kept for history but can no longer report. |
| 429 | title: "Plan quota exceeded", type: problems/quota-exceeded | telemetry_points_per_month is exhausted. detail names your plan and limit; extra carries resource, plan, limit, usage, and upgrade_url. The batch is not ingested. |
| 429 | detail: "Rate limit exceeded. Please retry later." | Per-credential rate limiting; extra.retry_after_seconds and a retry-after header |
05 Rollups and chart granularity
detailsTwo aggregate resolutions
A rollup worker maintains 1-minute and 1-hour aggregates per robot per metric, each carrying min, max, avg, count, and last_value. Charts read rollups, never raw rows.
Which resolution a range uses
The 1h range reads minute buckets. The 24h, 7d, and 30d ranges read hour buckets. A series is capped at 800 points, and at most 100 distinct metric names are offered per selector.
Backfilled readings are rolled up too
A batch whose oldest accepted reading is more than 10 minutes old schedules a backfill rollup, so a robot draining an offline backlog still lands in the right buckets.
06 Retention
reference| Data | Window | Notes |
|---|---|---|
| Raw readings | 30 days (default) | Configurable, but held at 90 days until the rollup backfill migration has run so charts are never left without a source |
| Minute rollups | 90 days | Fixed |
| Hour rollups | 400 days | Fixed -- year-over-year trends survive raw pruning |
| Batch id ledger | 90 days | The idempotency window; older batch ids can be reused |
Related docs
see also