# Data contract

What every episode records, channel by channel — plus retention, export formats, the IP boundary, and receipted deletion.

Every episode produces the same record: an MCAP file with a fixed channel
set, a video, and a replay spec. The contract is deliberately boring — the
value of episode 17 is that it's shaped exactly like episode 16 — and it's
the substrate everything else stands on: failure clusters link into it,
[verify()](/docs/primitives/verify/) returns it as ground truth, and the
verification report cites it.

## The episode record

*Example: episodes, exports, reports*

**Python**

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

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

episode = run.episodes[17]
print(episode.video_url)
print(episode.mcap_url)     # channels: /joint_states_measured,
                            # /joint_targets_commanded, /camera/*,
                            # /ft_wrist, /gt/object_poses, /events, /meta
print(episode.replay_spec)  # initial conditions, re-runnable

run.export(format="lerobot")   # also: "rlds", "mcap-bundle"
run.report(format="pdf")       # the deployment verification report
```

**TypeScript**

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

const roborama = new Roborama();

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

const episodes = await run.episodes();
const episode = episodes[17];
console.log(episode.video_url);
// channels: /joint_states_measured, /joint_targets_commanded,
console.log(episode.mcap_url);
//   /camera/*, /ft_wrist, /gt/object_poses, /events, /meta
console.log(episode.replay_spec); // initial conditions, re-runnable

await run.export({ format: "lerobot" }); // also: "rlds", "mcap-bundle"
await run.report({ format: "pdf" }); // the deployment verification report
```

**cURL**

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

curl "https://api.roborama.com/v1/runs/run_8842/export?format=lerobot" \
  -X POST \
  -H "Authorization: Bearer $ROBORAMA_API_KEY"

curl "https://api.roborama.com/v1/runs/run_8842/report?format=pdf" \
  -H "Authorization: Bearer $ROBORAMA_API_KEY" \
  -o verification-report.pdf
```

**Agent (tool-use payload)**

```json
[
  {
    "type": "tool_use",
    "name": "roborama_get_episodes",
    "input": { "run_id": "run_8842" }
  },
  {
    "type": "tool_use",
    "name": "roborama_export_run",
    "input": { "run_id": "run_8842", "format": "lerobot" }
  },
  {
    "type": "tool_use",
    "name": "roborama_get_report",
    "input": { "run_id": "run_8842", "format": "pdf" }
  }
]
```

| MCAP channel | Contents |
| --- | --- |
| `/joint_states_measured` | encoder-measured joint positions, velocities, efforts |
| `/joint_targets_commanded` | what the policy asked for — diff against measured to see tracking error |
| `/camera/*` | every feed named by the observation contract, timestamped |
| `/ft_wrist` | wrist force-torque, where the environment is instrumented for it |
| `/gt/object_poses` | mocap 6-DoF ground-truth object poses — the channel sim calibration wants |
| `/events` | episode lifecycle: start, success/failure determination, cluster label, endpoint RTTs, estops |
| `/meta` | reproducibility record — see below |

`/meta` is the channel that makes a result defensible later: resolved
firmware pin, calibration age, actuator cycle counts, thermal state, the
seed, and the *realized* perturbations — the layout jitter that actually
happened, not just what the schedule requested.

Each episode also carries `replay_spec`: its initial conditions in a form you
can re-run on the same scene revision or load into a simulator.

## Staged and instruction-level statistics

Runs against a [Task Spec](/docs/concepts/tasks-and-method-transfer/) that
declares stages or an instruction distribution carry two extra result
accessors, both under the same statistics discipline as the headline rate:

- `run.stage_rates` — per-stage success, each stage with its own n and
  Wilson ci95, so a long-horizon task shows *where* it degrades.
- `run.instruction_breakdown` — rate per instruction phrasing, held-out
  paraphrases reported separately, each row with n and ci95.

The result also records the realized `interventions` block (declared
policy, count, timestamps); intervened episodes are flagged in the
statistics. The canonical espresso run in
[/fixtures/runs.json](/fixtures/runs.json) carries all three.

## Exports

`run.export(format=...)` repackages the episode set: `"lerobot"` and
`"rlds"` for training pipelines, `"mcap-bundle"` for the raw record.
`run.report(format="pdf")` renders the deployment verification report — in
our terminology this is *verification* producing *conformance evidence*, and
the report says so.

## Retention, ownership, deletion

`data.retention` on the run sets how long artifacts live (the canonical run
uses `"30d"`). Fetching after expiry returns `retention_expired`.
`roborama.data.purge(run_id)` deletes early — customer-initiated, and it
returns a receipt with an id you can point an auditor at.

`data.train_on_failures` defaults to `False`, contractually: your episodes —
failures included — are your IP, not our training set, unless you flip the
bit. Endpoint-packaged [policies](/docs/concepts/policies/) extend the same
posture to weights, which never enter the facility at all.

> **The default is the contract:** The `False` default on `train_on_failures` is a term of service, not a
> configuration suggestion. No run trains on your failures because you forgot
> a flag.

## Calibration exports

There is one deliberately scoped exception to "your episodes are yours
alone": aggregate calibration data. Simulator vendors can license
ground-truth channels — `gt/object_poses`, commanded-vs-executed,
contact events — under `sim-calibration-v1`, which permits validation use
and forbids policy training.

*Example: roborama.calibration.export()*

**Python**

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

ds = roborama.calibration.export(
    embodiment="g1-edu-pro",
    scenes=["kitchen-std@v1.2"],
    channels=["gt/object_poses", "commanded_vs_executed", "contact_events"],
    license="sim-calibration-v1",  # scoped: validation only, never training
)

print(ds.id, ds.download_url)
```

**TypeScript**

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

const roborama = new Roborama();

const ds = await roborama.calibration.export({
  embodiment: "g1-edu-pro",
  scenes: ["kitchen-std@v1.2"],
  channels: ["gt/object_poses", "commanded_vs_executed", "contact_events"],
  license: "sim-calibration-v1", // scoped: validation only, never training
});

console.log(ds.id, ds.download_url);
```

**cURL**

```bash
curl https://api.roborama.com/v1/calibration/exports \
  -H "Authorization: Bearer $ROBORAMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "embodiment": "g1-edu-pro",
    "scenes": ["kitchen-std@v1.2"],
    "channels": ["gt/object_poses", "commanded_vs_executed",
                 "contact_events"],
    "license": "sim-calibration-v1"
  }'
```

**Agent (tool-use payload)**

```json
{
  "type": "tool_use",
  "name": "roborama_calibration_export",
  "input": {
    "embodiment": "g1-edu-pro",
    "scenes": ["kitchen-std@v1.2"],
    "channels": ["gt/object_poses", "commanded_vs_executed",
                 "contact_events"],
    "license": "sim-calibration-v1"
  }
}
```

## Where next

- [Sim triage](/docs/guides/sim-triage/) — feeding disagreement ground truth back into a simulator.
- [run()](/docs/primitives/run/) — where `data.retention` and `train_on_failures` are set.
- [Billing & budgets](/docs/guides/billing-and-budgets/) — purge receipts, key scopes, governance.
