> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroruntime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloud recording

> Upload episodes to your own cloud storage alongside the local copy, and what the follower does while the recorder comes up.

Turn it on in the constructor.

```python theme={null}
follower = MyArm(MEETING_ID, record="./sessions", cloud_recording=True)
```

The follower keeps writing locally and asks for a cloud recorder at the same
time. You end up with two copies of the same episodes, paired by episode id.

<ParamField path="cloud_recording" type="bool" default="False">
  Ask for a cloud recorder when recording starts. Needs a meeting id.
</ParamField>

<ParamField path="cloud_recording_timeout_s" type="float" default="90.0">
  How long to wait for the recorder to report in. Past this the state becomes
  `failed` and the session carries on recording locally.
</ParamField>

## Where it goes

Episodes are uploaded to the storage configured for your account. Set
that up on the [API keys page](https://app.videosdk.live/api-keys), and every
session after that follows it with no change to your code.

<Note>
  The local copy is written either way. Cloud recording adds a second copy, it
  does not replace the one on the robot.
</Note>

## States

`follower.cloud_recording_state` tracks the recorder.

| State | What it means |
| - | - |
| `idle` | not requested |
| `requested` | the follower asked the platform for a recorder |
| `starting` | the request was accepted and the recorder is coming up |
| `ready` | the recorder reported running, episodes may open |
| `failed` | it died, timed out, or ended on its own |
| `stopping` | released, still flushing |

Episodes cannot open while the state is `requested` or `starting`.
`start_episode()` returns `None` and logs why, rather than opening an episode
nobody is recording.

<Note>
  `failed` does not close the gate. A cloud failure drops back to local-only
  recording and never stops teleoperation, so episodes keep opening and the
  local copy stays complete.
</Note>

## Events

<ParamField path="on_recording_start(recording_id)" type="once, on ready">
  The recorder is live. Fires on the transition only, not on every heartbeat.
</ParamField>

<ParamField path="on_recording_state(state, detail)" type="on every change">
  The state above, with a reason string when there is one. This is the one to
  put on an operator screen.
</ParamField>

```python theme={null}
@follower.on_recording_state
def _(state, detail):
    print("cloud recorder:", state, detail or "")

@follower.on_recording_start
def _(recording_id):
    print("recording", recording_id)
```

## On the leader

`arm_when_aligned()` waits while the recorder is coming up, so arming looks like
it stalls for a few seconds on the first episode. That is deliberate: it holds
with the operator's hands already on the arm rather than arming into a refused
episode.

`leader.cloud_recording_state` mirrors the recorder as `None`, `"starting"`,
`"running"` or `"failed"`, so you can show progress while it waits.

```python theme={null}
def status(aligned, delta, seconds_left):
    if leader.cloud_recording_state == "starting":
        print("waiting for the cloud recorder")

leader.arm_when_aligned(hold_s=2.0, timeout=120.0, on_status=status)
```

<Warning>
  Give `timeout` a value large enough to cover the recorder coming up. Without
  one, `arm_when_aligned()` waits indefinitely. With one that is too short it
  raises `NotAligned` naming the recorder as the reason.
</Warning>

The leader can also ask for episode boundaries, which the follower stamps
because it is the machine with the capture clock:

```python theme={null}
leader.request_episode("start", task="pick up the cube")
leader.request_episode("end", success=True)
```

## Related

<CardGroup cols={2}>
  <Card title="Recording" icon="floppy-disk" href="/robotics/concepts/recording">
    What a session saves and how episodes are bounded.
  </Card>

  <Card title="Recording format" icon="folder-tree" href="/robotics/guides/recording-format">
    The files a session writes, and the drop counters to check.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.