# The platform

Instrumented robot cells, a strict data contract, the embodiment matrix, and the sim-verify loop — behind one API.

The facility is an API. Your policy arrives as a container, a checkpoint, or
an endpoint; what comes back from `POST /v1/runs` is `n`, `success_rate`, and
`ci95` — with every episode's artifacts attached.

*Example: the core primitive*

**Python**

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

run = roborama.run(
    robot="g1-edu-pro@fw2.3",            # embodiment @ pinned firmware
    environment="kitchen-std@v1.2",      # versioned catalogue scene
    policy=roborama.Policy.container(    # see policy packaging
        image="ghcr.io/acme/skill:v4",
        action_space="joint_delta_50hz",
        observation_contract="droid-3cam",
    ),
    task="load_dishwasher@v2",
    episodes="auto(ci=0.95, moe=0.03)",  # size n for ±3% at 95% — or an int
    perturbation={                       # the schedule IS the product spec
        "layout_jitter_mm": 25,
        "lighting": ["3000K", "5600K"],
        "distractors": "set-B",
        "seed": 42,
    },
    data={"retention": "30d", "train_on_failures": False},  # IP posture
    max_budget_usd=4_000,                # hard stop, metered live
)

print(run.result())
#  n=612  success_rate=0.874  ci95=(0.846, 0.898)
#  failure_clusters: [grasp_slip: 41, perception_miss: 22, collision: 9]
#  robot_hours=20.4  environment_hours=20.4  cost_usd=3_812
#  artifacts: mcap[], video[], ground_truth[], report_pdf
```

**TypeScript**

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

const roborama = new Roborama();

const run = await roborama.runs.create({
  robot: "g1-edu-pro@fw2.3", // embodiment @ pinned firmware
  environment: "kitchen-std@v1.2", // versioned catalogue scene
  policy: {
    type: "container",
    image: "ghcr.io/acme/skill:v4",
    action_space: "joint_delta_50hz",
    observation_contract: "droid-3cam",
  },
  task: "load_dishwasher@v2",
  episodes: "auto(ci=0.95, moe=0.03)", // size n for ±3% at 95%
  perturbation: {
    layout_jitter_mm: 25,
    lighting: ["3000K", "5600K"],
    distractors: "set-B",
    seed: 42,
  },
  data: { retention: "30d", train_on_failures: false },
  max_budget_usd: 4000, // hard stop, metered live
});

const result = await run.result();
console.log(result);
// { n: 612, success_rate: 0.874, ci95: [0.846, 0.898], cost_usd: 3812 }
```

**cURL**

```bash
curl https://api.roborama.com/v1/runs \
  -H "Authorization: Bearer $ROBORAMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "run",
    "robot": "g1-edu-pro@fw2.3",
    "environment": "kitchen-std@v1.2",
    "policy": {
      "type": "container",
      "image": "ghcr.io/acme/skill:v4",
      "action_space": "joint_delta_50hz",
      "observation_contract": "droid-3cam"
    },
    "task": "load_dishwasher@v2",
    "episodes": "auto(ci=0.95, moe=0.03)",
    "perturbation": {
      "layout_jitter_mm": 25,
      "lighting": ["3000K", "5600K"],
      "distractors": "set-B",
      "seed": 42
    },
    "data": { "retention": "30d", "train_on_failures": false },
    "max_budget_usd": 4000
  }'
```

**Agent (tool-use payload)**

```json
{
  "type": "tool_use",
  "name": "roborama_run",
  "input": {
    "robot": "g1-edu-pro@fw2.3",
    "environment": "kitchen-std@v1.2",
    "policy": {
      "type": "container",
      "image": "ghcr.io/acme/skill:v4",
      "action_space": "joint_delta_50hz",
      "observation_contract": "droid-3cam"
    },
    "task": "load_dishwasher@v2",
    "episodes": "auto(ci=0.95, moe=0.03)",
    "perturbation": {
      "layout_jitter_mm": 25,
      "lighting": ["3000K", "5600K"],
      "distractors": "set-B",
      "seed": 42
    },
    "data": { "retention": "30d", "train_on_failures": false },
    "max_budget_usd": 4000
  }
}
```

On the floor today, ten embodiments across six classes: `nori-a3` (arm-pod,
24 cells), `so-101` (arm-pod, 16 cells), `stretch3` (mobile-manipulator,
4 cells), `fr3-bench` (fixed-arm, 8 cells), `aloha-bimanual` (bimanual,
3 cells), `tiago-pro` (mobile-manipulator, 2 cells), `aloha2-pro` (bimanual,
2 cells), `spot-arm` (quadruped-manip, 2 cells), `g1-edu-pro` (humanoid,
6 cells), and `g1-edu-plus` (humanoid, 2 cells) — every cell with calibrated
cameras, force-torque sensing, and scripted resets.

## One screen, the whole system

```text
task (draft→pilot→frozen) → quote → run|eval|verify|matrix|threshold
                                → episodes (MCAP + video + GT)
                                        → result (n, rate, CI, clusters)
                                        → report (verification, citable)
                                        → webhook (gate your release)
```

The front of the pipeline is the claim itself: a customer's task arrives as
a draft Task Spec, gets piloted live, and freezes into an immutable, mutually
signed revision that every report cites. Everything downstream runs against
that frozen protocol.

You quote a job, you run a primitive, and everything that comes back is either
an artifact (MCAP, video, ground truth), a statistic (n, rate, confidence
interval, failure clusters), or a hook (a webhook that gates your release).
There is no dashboard-only state: anything you can see, an agent can `curl`.

## Cells, not labs

A Roborama cell is a robot with a pinned firmware, a versioned scene, calibrated
cameras and force-torque sensing, and a scripted reset. Your policy arrives as a
container, a checkpoint, or an endpoint — its declared `action_space` and
`observation_contract` are validated before any motor moves. Then the cell does
the one thing labs are bad at: it runs the same experiment hundreds of times
without a graduate student in the loop.

Everything is versioned. Robots pin firmware (`g1-edu-pro@fw2.3`), environments
and suites pin revisions (`kitchen-std@v1.2`, `rbr-manip-core@v3`), and
perturbation schedules are seeded and replayable. `@latest` is allowed, but the
resolved pin is recorded — every result is reproducible and citable.

*Example: policies in, three ways*

**Python**

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

# 1. Container — runs in-facility, GPU-adjacent
container = roborama.Policy.container(
    image="ghcr.io/acme/skill:v4",
    action_space="joint_delta_50hz",
    observation_contract="droid-3cam",
)

# 2. Checkpoint — known runtimes: openpi, lerobot
checkpoint = roborama.Policy.checkpoint(
    hf="acme/skill-v4", runtime="openpi")

# 3. Endpoint — customer-hosted inference; measured RTT logged per step
endpoint = roborama.Policy.endpoint(
    url="https://inference.acme.ai/act",
    latency_budget_ms=80,
    fallback="halt",
)

# Declared contracts are validated before any motor moves.
run = roborama.run(
    robot="g1-edu-pro@fw2.3",
    environment="kitchen-std@v1.2",
    policy=container,
    task="pick_place@v1",
    episodes="auto(ci=0.95, moe=0.03)",
)
print(run.id)
```

**TypeScript**

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

const roborama = new Roborama();

// 1. Container — runs in-facility, GPU-adjacent
const container = {
  type: "container",
  image: "ghcr.io/acme/skill:v4",
  action_space: "joint_delta_50hz",
  observation_contract: "droid-3cam",
} as const;

// 2. Checkpoint — known runtimes: openpi, lerobot
const checkpoint = {
  type: "checkpoint",
  hf: "acme/skill-v4",
  runtime: "openpi",
} as const;

// 3. Endpoint — customer-hosted inference; measured RTT logged per step
const endpoint = {
  type: "endpoint",
  url: "https://inference.acme.ai/act",
  latency_budget_ms: 80,
  fallback: "halt",
} as const;

// Declared contracts are validated before any motor moves.
const run = await roborama.runs.create({
  robot: "g1-edu-pro@fw2.3",
  environment: "kitchen-std@v1.2",
  policy: container,
  task: "pick_place@v1",
  episodes: "auto(ci=0.95, moe=0.03)",
});
console.log(run.id, checkpoint.type, endpoint.type);
```

**cURL**

```bash
# Declared contracts are validated before any motor moves.
# policy.type: "container" | "checkpoint" | "endpoint"
curl https://api.roborama.com/v1/runs \
  -H "Authorization: Bearer $ROBORAMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "run",
    "robot": "g1-edu-pro@fw2.3",
    "environment": "kitchen-std@v1.2",
    "policy": {
      "type": "endpoint",
      "url": "https://inference.acme.ai/act",
      "latency_budget_ms": 80,
      "fallback": "halt"
    },
    "task": "pick_place@v1",
    "episodes": "auto(ci=0.95, moe=0.03)"
  }'
```

**Agent (tool-use payload)**

```json
{
  "type": "tool_use",
  "name": "roborama_run",
  "input": {
    "robot": "g1-edu-pro@fw2.3",
    "environment": "kitchen-std@v1.2",
    "policy": {
      "type": "endpoint",
      "url": "https://inference.acme.ai/act",
      "latency_budget_ms": 80,
      "fallback": "halt"
    },
    "task": "pick_place@v1",
    "episodes": "auto(ci=0.95, moe=0.03)"
  }
}
```

## Two meters, visible everywhere

Pricing is two numbers: **robot-hours** (occupancy of an embodiment) and
**environment-hours** (occupancy of a scene). Both appear in every quote and
every result — the canonical run below cost `robot_hours=20.4`,
`environment_hours=20.4`, `cost_usd=3,812`. No opaque credits. The
[rate card](/pricing/) is a JSON file.

```text
n=612  success_rate=0.874  ci95=(0.846, 0.898)
failure_clusters: [grasp_slip: 41, perception_miss: 22, collision: 9]
robot_hours=20.4  environment_hours=20.4  cost_usd=3,812
artifacts: mcap[], video[], ground_truth[], report_pdf
```

Three numbers worth knowing:

- **n=612** — the canonical run: `episodes="auto(ci=0.95, moe=0.03)"` sized
  the job for ±3 points at 95% confidence.
- **7 of 8 minutes** — of a real-world RL run went to resets and overhead in
  Physical Intelligence's published result ([research §5](/research/));
  Roborama resets are scripted.
- **caught** — fw2.4 dropped −10.5 pts (0.787 vs 0.892, n=240 per cell),
  flagged by the matrix before release.

## The matrix

One policy against ten embodiments and three environments, every cell sized by
`episodes_per_cell="auto(ci=0.95, moe=0.05)"`. Pass and fail are declared
gates — here, a Wilson 95% lower bound of at least 0.80 — not vibes. This grid
caught a real regression: `g1-edu-pro@fw2.4` dropped 10.5 points in the kitchen
replica against fw2.3 (0.787 vs 0.892, n=240 per cell) after a wrist-controller
change opened a `grasp_slip` cluster.

Task pick_place_class3, episodes_per_cell=auto(ci=0.95, moe=0.05), gate: ci95 lower bound ≥ 0.8.

| robot | cell-a | kitchen-std | warehouse-std |
| --- | --- | --- | --- |
| g1-edu-pro@fw2.3 | 0.937 (n=190, ci95 0.893–0.964) pass | 0.892 (n=240, ci95 0.846–0.925) pass | 0.862 (n=210, ci95 0.809–0.902) pass |
| g1-edu-pro@fw2.4 | 0.921 (n=190, ci95 0.874–0.952) pass | 0.787 (n=240, ci95 0.731–0.835) fail | 0.838 (n=210, ci95 0.782–0.882) fail |
| g1-edu-plus@fw2.3 | 0.953 (n=190, ci95 0.912–0.975) pass | 0.921 (n=240, ci95 0.880–0.949) pass | 0.890 (n=210, ci95 0.841–0.926) pass |
| aloha2-pro@fw1.1 | 0.937 (n=190, ci95 0.893–0.964) pass | 0.871 (n=240, ci95 0.823–0.908) pass | 0.843 (n=210, ci95 0.788–0.886) fail |
| spot-arm@fw4.1 | 0.884 (n=190, ci95 0.831–0.922) pass | 0.746 (n=240, ci95 0.687–0.797) fail | 0.790 (n=210, ci95 0.730–0.840) fail |
| tiago-pro@fw2.0 | 0.905 (n=190, ci95 0.855–0.939) pass | 0.808 (n=240, ci95 0.753–0.853) fail | 0.724 (n=210, ci95 0.660–0.780) fail |
| fr3-bench@fw5.2 | 0.947 (n=190, ci95 0.905–0.971) pass | 0.821 (n=240, ci95 0.768–0.864) fail | 0.790 (n=210, ci95 0.730–0.840) fail |
| stretch3@fw1.9 | 0.879 (n=190, ci95 0.825–0.918) pass | 0.729 (n=240, ci95 0.670–0.781) fail | 0.629 (n=210, ci95 0.561–0.691) fail |
| nori-a3@fw1.0 | 0.800 (n=190, ci95 0.737–0.851) fail | 0.537 (n=240, ci95 0.474–0.599) fail | 0.467 (n=210, ci95 0.400–0.534) fail |
| so-101@fw1.2 | 0.742 (n=190, ci95 0.675–0.799) fail | 0.463 (n=240, ci95 0.401–0.526) fail | 0.410 (n=210, ci95 0.346–0.478) fail |

## The verify loop

Simulation is a first-class *input*. `verify()` consumes the initial conditions
your simulator flagged as uncertain — PolaRiS, Isaac, and world-model formats,
or Roborama layout-replay specs — runs them on physical hardware, and returns
ground truth formatted to recalibrate your simulator: mocap 6-DoF object poses
and commanded-vs-executed trajectories. Disagreement episodes are the payload.
An `audit_sample` of random episodes guards against the sim's blind spots.

*Example: the sim interlock*

**Python**

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

policy = roborama.Policy.checkpoint(hf="acme/skill-v4", runtime="openpi")

# Initial conditions your simulator marked uncertain: sampled states from
# PolaRiS / Isaac / world-model exports, or Roborama layout-replay specs.
scenarios = roborama.scenarios.from_file("sim_flagged_p1.json")

report = roborama.verify(
    policy=policy,
    scenarios=scenarios,
    robots=["g1-edu-pro@fw2.3", "g1-edu-plus@fw2.3"],
    audit_sample=0.10,          # random episodes vs. the sim's blind spots
    return_ground_truth=True,   # mocap 6-DoF poses + commanded-vs-executed,
                                # formatted to recalibrate your simulator
)

print(report.sim_agreement)  # where reality matched sim predictions
print(report.disagreements)  # scenario ids where reality diverged
```

**TypeScript**

```typescript
import { readFileSync } from "node:fs";
import Roborama from "@roborama/sdk"; // reads ROBORAMA_API_KEY

const roborama = new Roborama();

// Initial conditions your simulator marked uncertain: sampled states from
// PolaRiS / Isaac / world-model exports, or Roborama layout-replay specs.
const scenarios = JSON.parse(readFileSync("sim_flagged_p1.json", "utf8"));

const report = await roborama.runs.create({
  kind: "verify",
  policy: { type: "checkpoint", hf: "acme/skill-v4", runtime: "openpi" },
  scenarios,
  robots: ["g1-edu-pro@fw2.3", "g1-edu-plus@fw2.3"],
  audit_sample: 0.1, // random episodes vs. the sim's blind spots
  return_ground_truth: true, // mocap 6-DoF poses + commanded-vs-executed
});

console.log(report.sim_agreement); // where reality matched sim predictions
console.log(report.disagreements); // scenario ids where reality diverged
```

**cURL**

```bash
# sim_flagged_p1.json: initial conditions your simulator marked uncertain
# (PolaRiS / Isaac / world-model exports, or Roborama layout-replay specs)
curl https://api.roborama.com/v1/runs \
  -H "Authorization: Bearer $ROBORAMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --slurpfile s sim_flagged_p1.json '{
    kind: "verify",
    policy: { type: "checkpoint", hf: "acme/skill-v4", runtime: "openpi" },
    scenarios: $s[0],
    robots: ["g1-edu-pro@fw2.3", "g1-edu-plus@fw2.3"],
    audit_sample: 0.10,
    return_ground_truth: true
  }')"
```

**Agent (tool-use payload)**

```json
{
  "type": "tool_use",
  "name": "roborama_verify",
  "input": {
    "policy": { "type": "checkpoint", "hf": "acme/skill-v4",
                "runtime": "openpi" },
    "scenarios": {
      "format": "layout-replay",
      "source": "sim_flagged_p1.json",
      "count": 318
    },
    "robots": ["g1-edu-pro@fw2.3", "g1-edu-plus@fw2.3"],
    "audit_sample": 0.1,
    "return_ground_truth": true
  }
}
```

Simulator vendors can license the same ground truth directly as
[calibration exports](/docs/concepts/data-contract/) under `sim-calibration-v1`
(validation use, no policy training).

## The data contract

Every episode ships MCAP with a fixed channel set — `/joint_states_measured`,
`/joint_targets_commanded`, `/camera/*`, `/ft_wrist`, `/gt/object_poses`,
`/events`, `/meta` — plus video and a re-runnable replay spec. `/meta` carries
what reproducibility actually requires: firmware, calibration age, actuator
cycle counts, thermal state, seed, and the *realized* (not just requested)
perturbations. Exports: `lerobot`, `rlds`, `mcap-bundle`. Retention is explicit
per run, and `data.train_on_failures` defaults to `False` — your failures are
your IP. Details in the [data contract](/docs/concepts/data-contract/).

## Watch it run

Every run exposes a live episode ticker and WebRTC streams for its cell, and
five webhook events — `run.completed`, `episode.failed`, `threshold.crossed`,
`regression.detected`, `estop.triggered` — so your CI, your training loop, or
your agent can react without polling. See [CI gates](/docs/guides/ci-gates/).

*Example: the live surface*

**Python**

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

run = roborama.runs.get("run_8842")

for event in run.watch():   # live: episode ticker + WebRTC stream URLs
    print(event.episode, event.status)

stream = roborama.streams.get("cell-g1-04")
print(stream)
# {webrtc: "wss://streams.roborama.com/cells/cell-g1-04/webrtc",
#  mjpeg: "https://streams.roborama.com/cells/cell-g1-04/mjpeg",
#  viewer_token: "vt_7Kq2mHentXw4"}
```

**TypeScript**

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

const roborama = new Roborama();

const run = await roborama.runs.get("run_8842");

for await (const event of run.watch()) {
  // live: episode ticker + WebRTC stream URLs
  console.log(event.episode, event.status);
}

const stream = await roborama.streams.get("cell-g1-04");
console.log(stream);
// { webrtc: "wss://streams.roborama.com/cells/cell-g1-04/webrtc",
//   mjpeg: "https://streams.roborama.com/cells/cell-g1-04/mjpeg",
//   viewer_token: "vt_7Kq2mHentXw4" }
```

**cURL**

```bash
# live event stream (server-sent events): episode ticker + status
curl -N https://api.roborama.com/v1/runs/run_8842/events \
  -H "Authorization: Bearer $ROBORAMA_API_KEY"

# cell video stream descriptor
curl https://api.roborama.com/v1/streams/cell-g1-04 \
  -H "Authorization: Bearer $ROBORAMA_API_KEY"
```

**Agent (tool-use payload)**

```json
[
  {
    "type": "tool_use",
    "name": "roborama_watch_run",
    "input": { "run_id": "run_8842" }
  },
  {
    "type": "tool_use",
    "name": "roborama_get_stream",
    "input": { "cell": "cell-g1-04" }
  }
]
```
