# Robots & firmware

Every embodiment is pinned as model@firmware, because a firmware bump is a different robot as far as your statistics are concerned.

A success rate is a property of a policy *on a specific machine running
specific firmware*. Change either and the number is a different claim. So
every robot in the API is addressed as `model@firmware` —
`g1-edu-pro@fw2.3` — and the pin travels with every result, every artifact,
and every citation.

## The fleet

*Example: roborama.robots.list()*

**Python**

```python
import roborama  # reads ROBORAMA_API_KEY from the environment

robots = roborama.robots.list()
print(robots)
# [{id:"g1-edu-pro", class:"humanoid", dof:37, hands:"dex3-1",
#   firmwares:["2.3","2.4"], cells:6, duty_cycle_pct:71, tier:"verify"}]

environments = roborama.environments.list(cls="kitchen")
print(environments)
# [{id:"kitchen-std", rev:"v1.2", instrumentation:["mocap","ft"],
#   objects:214, reset:"scripted", adder_tier:"replica"}]
```

**TypeScript**

```typescript
import Roborama from "@roborama/sdk"; // reads ROBORAMA_API_KEY

const roborama = new Roborama();

const robots = await roborama.robots.list();
console.log(robots);
// [{ id: "g1-edu-pro", class: "humanoid", dof: 37, hands: "dex3-1",
// firmwares: ["2.3", "2.4"], cells: 6,
//   duty_cycle_pct: 71, tier: "verify" }]

const environments = await roborama.environments.list({ cls: "kitchen" });
console.log(environments);
// [{ id: "kitchen-std", rev: "v1.2", instrumentation: ["mocap", "ft"],
//    objects: 214, reset: "scripted", adder_tier: "replica" }]
```

**cURL**

```bash
curl https://api.roborama.com/v1/robots \
  -H "Authorization: Bearer $ROBORAMA_API_KEY"

curl "https://api.roborama.com/v1/environments?cls=kitchen" \
  -H "Authorization: Bearer $ROBORAMA_API_KEY"
```

**Agent (tool-use payload)**

```json
[
  {
    "type": "tool_use",
    "name": "roborama_list_robots",
    "input": {}
  },
  {
    "type": "tool_use",
    "name": "roborama_list_environments",
    "input": { "cls": "kitchen" }
  }
]
```

Each entry describes capacity, not just capability: `cells` is how many
physical stations run that embodiment, and `duty_cycle_pct` is measured
utilization — how much of the wall-clock those cells spend running episodes
rather than resetting or idling. A `duty_cycle_pct` of 71 on six cells is
the honest answer to "how fast will my 600 episodes finish", and it feeds
the `queue_eta` in every [quote](/docs/api-reference/quote/).

| Field | Type | Notes |
| --- | --- | --- |
| `id` | `str` | model id, e.g. `g1-edu-pro`; pin firmware with `@`, e.g. `g1-edu-pro@fw2.3` |
| `class` | `str` | `humanoid`, `bimanual`, `mobile-manipulator`, `arm-pod` |
| `dof` | `int` | actuated degrees of freedom, hands included |
| `hands` | `str` | end effector fitted, e.g. `dex3-1`, `gripper-2f` |
| `firmwares` | `str[]` | pinnable firmware revisions currently installed on at least one cell |
| `cells` | `int` | physical stations running this embodiment |
| `duty_cycle_pct` | `int` | measured utilization; a throughput number, not a success rate |
| `tier` | `"verify" \| "soak"` | verification-tier hardware vs. commodity iteration pods |

## Why firmware is part of the address

Firmware changes controllers, and controllers change outcomes. The canonical
example is on the [matrix page](/docs/primitives/matrix/): the same policy on
`g1-edu-pro` in `kitchen-std@v1.2` scored 0.892 (n=240, ci95 0.846–0.925) on
`fw2.3` and 0.787 (n=240, ci95 0.731–0.835) on `fw2.4` — a 10.5-point drop
traced to a wrist controller change, surfacing as a `grasp_slip` cluster. If
the API let you say just "g1-edu-pro", those two results would be
indistinguishable, and one of them would be wrong.

`@latest` is accepted as a convenience, but the resolved pin is recorded in
the run and in every episode's `/meta` channel. There is no way to produce a
result that doesn't know what firmware it ran on.

> **Pins are recorded, not just requested:** `/meta` records the *realized* state per episode: resolved firmware,
> calibration age, actuator cycle counts, and thermal state. Two runs with the
> same pin are still distinguishable if the hardware drifted between them. See
> the [data contract](/docs/concepts/data-contract/).

## Tiers: verify and soak

The fleet splits into two tiers, and the split is the basis of the
[threshold contract](/docs/primitives/threshold/). Verification-tier hardware
(`g1-edu-pro`, `g1-edu-plus`, `spot-arm`, `aloha2-pro`, `tiago-pro`,
`aloha-bimanual`, `fr3-bench`, `stretch3`) is the machine your deployment
claim is about — instrumented, calibrated, and priced accordingly. Soak-tier
pods (`nori-a3` at 24 cells, `so-101` at 16, single-digit dollars per
robot-hour) exist for patient bulk iteration: cheap episodes while you climb,
escalating to verification tier when a threshold is crossed. Rates for both
are on [/pricing/](/pricing/).

## Where next

- [Environments](/docs/concepts/environments/) — the other half of the address.
- [run()](/docs/primitives/run/) — where the pin gets used.
- [API reference: Robots](/docs/api-reference/robots/) — the REST shape.
