Robots Center Agents Network
Log in Create workspace

Platform / Fleet

Zero-touch provisioning and the claim flow

Pre-register robots in bulk, hand the factory one reusable claim token, and let each unit register itself on first boot. A claim token is never itself an API credential: every claim mints a credential unique to the robot that presented it.

API docs

01 Three ways to register a robot

details

In the browser

/app/fleet

The Register robot action creates a single robot from the fleet board. No API call required.

CSV pre-registration

up to 1,000 rows

Upload a CSV on /app/fleet, or POST parsed rows to the batch endpoint. Each row becomes a pending robot with no service agent linked.

Self-registration

claim token

The unit calls POST /api/v1/enrollments/claim on first boot with its serial number and receives its own credential.

02 CSV columns

details

serial_number is the only required column

Optional columns: name (defaults to the serial number), model, manufacturer, robot_type, cohort (an existing cohort name), and tags (comma or semicolon separated). Headers are matched case- and separator-insensitively, so "Serial Number" works; unknown columns are ignored. Files are capped at 1,000 rows and 2 MB.

Per-row results, not an all-or-nothing batch

The response reports a summary of total, created, and skipped, plus one result row per input row carrying row, serial_number, status, and -- when skipped -- a reason. Skip reasons are invalid, duplicate_in_batch, duplicate_serial, unknown_cohort, and quota_exceeded. A per-row quota_exceeded still returns 201 for the batch as a whole.

03 Bulk pre-registration response

example
json
POST /api/v1/operator/robots/batch

{"robots": [
  {"serial_number": "RC-0001", "model": "MR-2", "cohort": "Rotterdam DC"},
  {"serial_number": "RC-0002"},
  {"serial_number": "RC-0001"}
]}

Response 201:
{
  "summary": {"total": 3, "created": 2, "skipped": 1},
  "robots": [{"id": "...", "serial_number": "RC-0001", "status": "pending"}],
  "results": [
    {"row": 1, "serial_number": "RC-0001", "status": "created"},
    {"row": 2, "serial_number": "RC-0002", "status": "created"},
    {"row": 3, "serial_number": "RC-0001", "status": "skipped",
     "reason": "duplicate_in_batch", "errors": {}}
  ]
}

04 Fleet claim tokens

reference
Field Meaning
max_claims How many times the token may be claimed. Clamped to 1-1000; above 1 the token is a fleet claim token and no service agent is created up front.
expires_in_minutes Lifetime from creation, clamped to 5-1440, default 30. The response carries the resolved expires_at.
serial_allowlist Serial numbers permitted to claim. Up to 1,000 entries of at most 120 characters. Empty means any serial is accepted.
cohort_id Cohort every claimed unit joins. Cleared if that cohort is later deleted.
default_tags Tags applied to every claimed unit, lowercased and trimmed.
kind / scope_preset / scopes agent or robot; read_only, worker, or robot preset, or an explicit scope list.

05 What a claim does

details

Serial binding

If the presented serial matches a pending pre-registered robot, the claim binds to that row instead of creating a duplicate, links a newly created service agent, moves the robot out of pending, and applies the token's cohort and default tags.

Unique credentials per claim

Each claim returns an api_key and a 30-day access_token minted for that robot alone. The claim token itself never authenticates an API request, and the claim response deliberately omits serial_allowlist so one unit cannot enumerate its siblings.

Token status

Reported status resolves in order: revoked, then expired, then claimed (a single-use token that has been used), then exhausted, then active. A revoked token that was previously claimed reports revoked.

06 Claim request and response

example
json
POST /api/v1/enrollments/claim

{
  "token": "age_xxxx.yyyy",
  "serial_number": "RC-0001"
}

Response 201:
{
  "enrollment": {"id": "...", "kind": "robot", "status": "active", "claims_remaining": 299},
  "service_agent": {"id": "...", "name": "RC-0001", "status": "active"},
  "robot": {"id": "...", "serial_number": "RC-0001", "status": "offline"},
  "api_key": {"key": "agk_xxx.yyy", "credential_id": "...", "type": "api_key"},
  "access_token": {"token": "SFMyNTY...", "type": "bearer", "expires_in": 2592000}
}

07 Claim error responses

reference
Status detail / error When
400 serial_not_allowed Serial is not on the token's allowlist
400 serial_already_claimed That serial's robot is already bound to another identity
400 serial_number_required A fleet claim token was presented without a serial number
401 Invalid enrollment token Bad format, unknown prefix, or digest mismatch
409 enrollment revoked The token was revoked
409 enrollment already claimed A single-use token was already used
409 enrollment exhausted A fleet token has no claims left
410 This enrollment token has expired Past expires_at
429 quota_exceeded The workspace is at its robots plan limit
403 workspace_frozen / workspace_paused / workspace_archived The workspace is not accepting machine traffic; nothing is created and the denial is audited

08 Robot lifecycle

details

Statuses

pending, online, offline, charging, error, maintenance, decommissioned. maintenance is still in service. pending means the row exists and is cohortable and claimable, but no machine has presented an identity yet.

decommissioned is terminal

Decommissioning revokes the robot's credentials in the same transaction that sets the status, so its API key and any outstanding access token stop authenticating. The row and its history are kept, it is hidden from listings unless asked for explicitly, it stops consuming the robots quota, and bulk operations skip it with skip_reason robot_decommissioned.

What decommissioning does not do

It is a filter on the surfaces that act on robots, not a cascade. Static cohort membership rows survive and are still returned by the cohort show endpoint; only roster and rule-match reads exclude the robot. Automatic mission assignment picks from online robots only and will never choose it, but an explicit assign call still succeeds -- it checks that the robot exists in the workspace, not that it is in service. Treat the 410 on device writes as the hard boundary.

Device writes after decommission

Heartbeat, diagnostics, and telemetry writes return 410 Gone with type https://robotscenter.net/problems/robot-decommissioned, so a still-powered unit cannot put itself back on the roster.

Related docs

see also