---
sidebar:
  hidden: true
title: roboto.experimental.sessions.session
---
## Module Contents

### Session

```python
class roboto.experimental.sessions.session.Session(
    record: roboto.experimental.sessions.record.SessionRecord,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
)
```

`from roboto import Session`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L62-L1423)

An operational time window of a Device.

A Session is a drone flight, a vehicle drive, a robot arm test run: some contiguous activity in the real world. It groups the recordings, logs, and other data produced during that window. Because a Session is bounded by the activity rather than by the recordings, it can span many files or cover just a slice of one. Each file it includes can be narrowed to a sub-window of that file.

The Session's aggregate bounds, `min_timestamp_ns` and `max_timestamp_ns` in Unix-epoch nanoseconds, span every file the Session includes. Roboto recomputes them whenever the Session's files or the anchors of their data change, and each method of this class that makes such a change returns with the updated bounds.

A Session can reference one or many devices: a single drone for a solo mission, or all of the drones in a formation flight. Use [`attach_to_device()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.attach_to_device) and [`detach_from_device()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.detach_from_device) to change which devices it references.

How to create a Session:

- [`Session.create()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.create) accepts zero, one, or many devices, and does not require a name.
- [`create_session()`](/reference/python-sdk/roboto/domain/devices/device#roboto.domain.devices.device.Device.create_session) creates one named Session on a device, optionally declaring its files and topics in the same call.
- [`create_sessions()`](/reference/python-sdk/roboto/domain/devices/device#roboto.domain.devices.device.Device.create_sessions) creates many such Sessions on a device in one call.
- [`create_session()`](/reference/python-sdk/roboto/domain/datasets/dataset#roboto.domain.datasets.dataset.Dataset.create_session) creates a Session for an existing Dataset, inferring the devices involved and pre-populating files from the Dataset.

Once created, include files with [`add_file()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.add_file) or [`add_files()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.add_files).

**Usage**

Create a Session for a drone flight, include a recording, and list its topics:

```python
from roboto.experimental.sessions import Session
session = Session.create(name="flight-2026-04-23-001", device_ids=["robot-abc"])
session.add_file("fl_0123456789abcdef")
for topic in session.list_topics():
    print(topic.name)
```

**Parameters**

- **record** (`roboto.experimental.sessions.record.SessionRecord`)
- **roboto_client** (`Optional[roboto.http.RobotoClient]`)

**Properties**

- **Session.completes_at** (`datetime.datetime | None`): When Roboto will mark this Session complete unless another file is added first, per its [`completion_policy`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.completion_policy).

  `None` while the Session is complete, has no completion policy, or has no files.

  Return type: `Optional[datetime.datetime]`

- **Session.completion_policy** (`roboto.experimental.sessions.record.CompletionPolicy | None`): When Roboto marks this Session complete on its own, or `None` if it is marked complete only by [`complete()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.complete).

  Return type: `Optional[roboto.experimental.sessions.record.CompletionPolicy]`

- **Session.created** (`datetime.datetime | None`): UTC timestamp when this Session was created.

  Return type: `Optional[datetime.datetime]`

- **Session.created_by** (`str`): Identifier of the user or service which created this Session.

- **Session.custom_fields** (`dict[str, Any]`): Custom-field values defined on Sessions in this org.

  Every `Ready` [`CustomField`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField) for the org appears as a key. Values that have not been set on this session surface as `None` rather than being absent. Empty when no custom fields are defined for the org.

  A [`Timestamp`](/reference/python-sdk/roboto/domain/custom_fields/record#roboto.domain.custom_fields.record.CustomFieldType.Timestamp) value is returned as an ISO 8601 string.

- **Session.description** (`str | None`): Optional description of this Session.

  Return type: `Optional[str]`

- **Session.ingestion_count** (`int`): How many times this Session has been announced ingested.

- **Session.max_timestamp_ns** (`int | None`): Latest time covered by this Session, in Unix-epoch nanoseconds.

  `None` while the Session includes no files, or only files added without a time window whose topic data has no time span registered yet.

  Return type: `Optional[int]`

- **Session.metadata** (`dict[str, Any]`): User-supplied metadata attached to this Session.

  Sessions are not filterable or sortable by `metadata` keys. For queryable structured attributes on a Session, define a custom field on the `Session` entity type.

- **Session.min_timestamp_ns** (`int | None`): Earliest time covered by this Session, in Unix-epoch nanoseconds.

  `None` while the Session includes no files, or only files added without a time window whose topic data has no time span registered yet.

  Return type: `Optional[int]`

- **Session.modified** (`datetime.datetime | None`): UTC timestamp when this Session was last modified.

  Return type: `Optional[datetime.datetime]`

- **Session.modified_by** (`str`): Identifier of the user or service which last modified this Session.

- **Session.name** (`str | None`): Optional short name of this Session.

  Return type: `Optional[str]`

- **Session.org_id** (`str`): Identifier of the organization that owns this Session.

- **Session.record** (`roboto.experimental.sessions.record.SessionRecord`): Underlying data record for this Session.

- **Session.session_id** (`str`): Globally unique identifier assigned to this Session on creation.

- **Session.status** (`roboto.experimental.sessions.record.SessionStatus`): Whether this Session is in progress or complete (see [`complete()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.complete)).

- **Session.tags** (`list[str]`): User-supplied tags on this Session.

#### Session.add_file()

```python
def add_file(
    file: Union[roboto.domain.files.File, str],
    data_range: Optional[roboto.domain.topics.record.DataRange] = None,
    min_file_timestamp_ns: Optional[int] = None,
    max_file_timestamp_ns: Optional[int] = None,
    anchor: Optional[roboto.time.Time] = None,
    topics: Optional[collections.abc.Sequence[roboto.experimental.ingest.TopicDeclaration]] = None,
) -> roboto.experimental.sessions.record.SessionFileView
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L551-L626)

Include a single file in this Session, with whatever topic data it carries.

The singular form of [`add_files()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.add_files), taking the fields of one [`SessionFile`](/reference/python-sdk/roboto/experimental/sessions/operations#roboto.experimental.sessions.operations.SessionFile) as separate arguments. That class documents what each field means; [`add_files()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.add_files) documents what the platform does with them.

**Parameters**

- **file** (`Union[roboto.domain.files.File, str]`): A [`File`](/reference/python-sdk/roboto/domain/files/file#roboto.domain.files.file.File) or a file ID.
- **data_range** (`Optional[roboto.domain.topics.record.DataRange]`): Slice of the file this Session holds, or `None` for the whole file.
- **min_file_timestamp_ns** (`Optional[int]`): Optional lower bound of the part of the file to include, in the file's own timestamps. Must be paired with `max_file_timestamp_ns`.
- **max_file_timestamp_ns** (`Optional[int]`): Optional upper bound paired with `min_file_timestamp_ns`.
- **anchor** (`Optional[roboto.time.Time]`): Optional wall-clock instant at which time 0 of the data added here occurred: an `int` of nanoseconds since the Unix epoch, or any other [`Time`](/reference/python-sdk/roboto/time#roboto.time.Time), read as [`to_epoch_nanoseconds()`](/reference/python-sdk/roboto/time#roboto.time.to_epoch_nanoseconds) reads it (a `datetime` or ISO 8601 string is that instant; a `float`, `Decimal`, or numeric string is seconds since the epoch). Must fall after the Unix epoch.
- **topics** (`Optional[collections.abc.Sequence[roboto.experimental.ingest.TopicDeclaration]]`): Topics this file contributes data to, over the part of the file that carries them. Each lists the files a read of its data opens in [`representations`](/reference/python-sdk/roboto/experimental/ingest/operations#roboto.experimental.ingest.operations.TopicDeclaration.representations).

**Returns**

- `roboto.experimental.sessions.record.SessionFileView`: The file's place in this Session, as [`list_files()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.list_files) reports it.

**Raises**

- `TypeError`: `anchor` is not one of the [`Time`](/reference/python-sdk/roboto/time#roboto.time.Time) types.
- `ValueError`: `anchor` is a boolean, a string that is neither seconds nor ISO 8601, or a negative number (an `int`, `float`, `Decimal`, or numeric string); rejected client-side, before any request is made.
- `OverflowError`: `anchor` is infinite, such as `float("inf")`; rejected client-side, before any request is made.
- `pydantic.ValidationError`: The arguments break a rule [`SessionFile`](/reference/python-sdk/roboto/experimental/sessions/operations#roboto.experimental.sessions.operations.SessionFile) enforces, such as an `anchor` at or before the Unix epoch or a time window with only one of its two bounds, or the representations listed in `topics` name one file in two storage formats; rejected client-side, before any request is made. The rules for one topic's own representations are enforced earlier, when the caller builds its [`TopicDeclaration`](/reference/python-sdk/roboto/experimental/ingest/operations#roboto.experimental.ingest.operations.TopicDeclaration).
- [`roboto.exceptions.RobotoInvalidRequestException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoInvalidRequestException): With the anchor covering it added, the file's data or the window stated here would fall before the Unix epoch or past the largest storable Unix-epoch nanosecond value, or the anchor would move a window another Session declared over the same data there. Anchor the data at the instant it was recorded.
- [`roboto.exceptions.RobotoDomainException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoDomainException): Whatever else the platform refused this file with.

**Usage**

Include a whole file:

```python
session.add_file("fl_0123456789abcdef")
```

Include only a sub-window of a file:

```python
session.add_file(
    "fl_0123456789abcdef",
    min_file_timestamp_ns=0,
    max_file_timestamp_ns=60_000_000_000,
)
```

#### Session.add_files()

```python
def add_files(
    files: collections.abc.Sequence[roboto.experimental.sessions.operations.SessionFile],
) -> roboto.http.BatchResponse[roboto.experimental.sessions.record.SessionFileView]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L628-L686)

Include the given files in this Session, with whatever topic data they carry.

Each entry states one file's place in this Session, in the same terms a [`SessionDeclaration`](/reference/python-sdk/roboto/experimental/sessions/operations#roboto.experimental.sessions.operations.SessionDeclaration) states the files of a Session declared whole, so a Session composed file by file can say everything a declared one says.

The platform decides every refusal before adding anything, so an entry it refuses leaves the others added, while a failure it did not anticipate, such as a timeout, adds none of them. Resending converges on the same composition rather than duplicating it. The platform then recomputes this Session's aggregate bounds across every file it includes, and this instance reflects the new `min_timestamp_ns` / `max_timestamp_ns` on return.

**Parameters**

- **files** (`collections.abc.Sequence[roboto.experimental.sessions.operations.SessionFile]`): Files to include in the Session, each appearing exactly once and listing all of its topics; [`SessionFile`](/reference/python-sdk/roboto/experimental/sessions/operations#roboto.experimental.sessions.operations.SessionFile) documents what one entry states, including how the window it names survives re-anchoring the file. An empty sequence returns an empty response without contacting the platform.

**Returns**

- `roboto.http.BatchResponse[roboto.experimental.sessions.record.SessionFileView]`: One element per entry, in request order, holding either the file's place in this Session or why the platform refused it.

**Raises**

- `pydantic.ValidationError`: The sequence names a file more than once, declares more than [`MAX_FILES_AND_TOPICS_PER_REQUEST`](/reference/python-sdk/roboto/experimental/ingest/operations#roboto.experimental.ingest.operations.MAX_FILES_AND_TOPICS_PER_REQUEST) files and topics between them, or lists representations naming one file in two storage formats; rejected client-side, before any request is made.
- [`roboto.exceptions.RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): A file an entry names, or a file one of its topics' representations names, does not exist in this Session's org or has a status other than [`Available`](/reference/python-sdk/roboto/domain/files/record#roboto.domain.files.record.FileStatus.Available). Nothing is added.
- [`roboto.exceptions.RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller lacks permission to manage Sessions in the org that owns this Session, cannot edit a file an entry declares topics on, a file it anchors, or a file a listed representation names, or lacks topic edit access in that org while an entry states `is_default_for_reads` on a timeline source. Nothing is added.

**Usage**

```python
from roboto.experimental.sessions import SessionFile
added = session.add_files(
    [
        SessionFile(file_id="fl_aaa"),
        SessionFile(
            file_id="fl_bbb",
            min_file_timestamp_ns=0,
            max_file_timestamp_ns=60_000_000_000,
        ),
    ]
)
print([view.file_id for view in added.succeeded])
```

#### Session.attach_to_device()

```python
def attach_to_device(device_id: str) -> None
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L688-L709)

Attach a Device to this Session as a subject.

A Session may have many device attachments. For example, a formation flight where multiple drones operate within a single activity window.

**Parameters**

- **device_id** (`str`): ID of the Device to add as a subject of this Session.

**Raises**

- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): The Device does not exist in this Session's org, or the Session no longer exists.

**Returns**

- `None`

**Usage**

```python
session.attach_to_device("wingman")
list(session.list_devices())
# ['lead', 'wingman']
```

#### Session.clear_custom_field()

```python
def clear_custom_field(name: str) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L711-L713)

Clear a single custom-field value on this session to `None`.

**Parameters**

- **name** (`str`)

**Returns**

- `Session`

#### Session.clear_custom_fields()

```python
def clear_custom_fields(names: collections.abc.Sequence[str]) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L715-L717)

Clear multiple custom-field values on this session to `None`.

**Parameters**

- **names** (`collections.abc.Sequence[str]`)

**Returns**

- `Session`

#### Session.clear_unix_offset()

```python
def clear_unix_offset() -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L719-L757)

Return this Session's data to an offset of 0.

Removes the wall-clock anchor from all of this Session's topic data, so the Session's bounds return to their stored values, read as nanoseconds since the Unix epoch with nothing added.

Clearing reaches this Session's data and no more, exactly the data [`set_unix_offset()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.set_unix_offset) writes.

Any anchoring state can be cleared, and repeating the call changes nothing: clearing a Session that carries no anchor, or has no topic data at all, is a successful no-op, and a Session made of slices anchored at several different instants is still cleared, each slice moving back by its own offset.

**Returns**

- `Session`: This Session, refreshed with recomputed aggregate bounds.

**Raises**

- [`RobotoInvalidRequestException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoInvalidRequestException): Returning the data to an offset of 0 would start it, or a time range declared over it, before the Unix epoch. Data starts there when its own timestamps are negative; a range starts there when it begins earlier than its data's anchor.
- [`RobotoConflictException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoConflictException): A concurrent writer added files to the Session while the clear was being applied; retry the call.
- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): The Session no longer exists.
- [`RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller lacks permission to manage Sessions in the org that owns this Session.

**Usage**

The recomputed bounds return to the Session's stored values:

```python
session.min_timestamp_ns
# 1700000000250000000
session = session.clear_unix_offset()
session.min_timestamp_ns
# 250000000
```

#### Session.complete()

```python
def complete() -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L759-L785)

Mark this Session complete: declare that all of its files have been added.

Once it is complete, the platform announces the `session.ingested` platform event, which triggers can subscribe to, as soon as every ingestable file in the Session is ingested. A file is ingestable when its path matched one of the org's ingestion rules when its current version was created, or it has since been partly or fully ingested. Files that are not ingestable never delay the announcement.

Adding a file to a complete Session puts it back in progress; mark it complete again once the new files are added. If its ingestable files change while it stays complete (a new version, a file removed or deleted), it is announced again once they are all ingested. Marking a complete Session complete changes nothing.

**Returns**

- `Session`: This Session, refreshed from the server response.

**Usage**

```python
session.add_file("fl_0123456789abcdef")
session = session.complete()
session.status is SessionStatus.Complete
# True
```

#### Session.create()

```python
@classmethod
def create(
    name: Optional[str] = None,
    device_ids: collections.abc.Sequence[str] = (),
    description: Optional[str] = None,
    metadata: Optional[dict[str, Any]] = None,
    tags: Optional[collections.abc.Sequence[str]] = None,
    custom_fields: Optional[dict[str, Any]] = None,
    caller_org_id: Optional[str] = None,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
    completion_policy: Union[roboto.experimental.sessions.record.CompletionPolicy, None, roboto.sentinels.NotSetType] = NotSet,
) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L106-L178)

Create a new Session, optionally associating it with one or more devices.

Every call creates a new Session. For the common single-device case, prefer [`create_session()`](/reference/python-sdk/roboto/domain/devices/device#roboto.domain.devices.device.Device.create_session), which identifies the Session by name so a resend converges on the Session it already created. To add devices to an existing Session later, see [`attach_to_device()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.attach_to_device).

**Parameters**

- **name** (`Optional[str]`): Optional short name for the Session (max 120 characters).
- **device_ids** (`collections.abc.Sequence[str]`): Devices to associate with the Session at creation. Empty (the default) creates a Session with no associated devices.
- **description** (`Optional[str]`): Optional description of the Session.
- **metadata** (`Optional[dict[str, Any]]`): Optional initial metadata. Sessions are not filterable or sortable by `metadata` keys; for queryable structured attributes, define a custom field on the `Session` entity type.
- **tags** (`Optional[collections.abc.Sequence[str]]`): Optional initial tags. Sessions can be filtered by tag membership but are not sortable by tag.
- **custom_fields** (`Optional[dict[str, Any]]`): Optional initial values for Ready custom fields defined on Sessions in the caller's org. Keys must match Ready field names; values must satisfy each field's declared type.
- **caller_org_id** (`Optional[str]`): Caller's org scope. Required when the caller belongs to multiple orgs.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Optional RobotoClient; defaults to the ambient one.
- **completion_policy** (`Union[roboto.experimental.sessions.record.CompletionPolicy, None, roboto.sentinels.NotSetType]`): When Roboto marks the Session complete on its own. Left unset, the Session gets the org's default completion policy, if an org admin has set one, and [`completion_policy`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.completion_policy) on the returned Session shows the policy applied. `None` means the Session is marked complete only by [`complete()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.complete), whatever the org's default. Change it later with [`update()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.update).

**Returns**

- `Session`: The created Session.

**Raises**

- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): A device in `device_ids` does not exist in the caller's org. No Session is created.

**Usage**

```python
from roboto.experimental.sessions import Session
session = Session.create(
    name="flight-2026-04-23-001",
    device_ids=["robot-a", "robot-b"],
    description="formation flight #4",
    metadata={"pilot": "alice"},
    tags=["pre-flight-check"],
)
```

#### Session.create_if_not_exists()

```python
@classmethod
def create_if_not_exists(
    match_roboql_query: str,
    name: Optional[str] = None,
    device_ids: collections.abc.Sequence[str] = (),
    description: Optional[str] = None,
    metadata: Optional[dict[str, Any]] = None,
    tags: Optional[collections.abc.Sequence[str]] = None,
    custom_fields: Optional[dict[str, Any]] = None,
    completion_policy: Union[roboto.experimental.sessions.record.CompletionPolicy, None, roboto.sentinels.NotSetType] = NotSet,
    caller_org_id: Optional[str] = None,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L181-L257)

Return the first Session matching a RoboQL query, creating one when none matches.

Concurrent calls with the same query create one Session between them, so a process handling one file at a time (such as an S3 event handler) can use it to put each file into the Session it belongs to.

The Session created must match `match_roboql_query`. If `name`, `tags` and the other arguments describe a Session the query does not match, every later call creates another one.

When several Sessions match, which one is returned is not defined unless the query ends with a `SORT BY` clause, e.g. `name = 'flight-0042' SORT BY session_id`.

A matching Session is returned as it is: the arguments below apply only to a Session this call creates, so `completion_policy` and the rest are ignored on a match. Use [`update()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.update) to change a matched Session. The Session returned may be complete; adding a file to it puts it back in progress. To match only Sessions in progress, add `AND status = 'in_progress'` to the query.

**Parameters**

- **match_roboql_query** (`str`): RoboQL query over Sessions, e.g. `name = 'flight-0042'`.
- **name** (`Optional[str]`): Name of the Session to create when none matches.
- **device_ids** (`collections.abc.Sequence[str]`): Devices to associate with a created Session.
- **description** (`Optional[str]`): Description of a created Session.
- **metadata** (`Optional[dict[str, Any]]`): Metadata of a created Session.
- **tags** (`Optional[collections.abc.Sequence[str]]`): Tags of a created Session.
- **custom_fields** (`Optional[dict[str, Any]]`): Custom-field values of a created Session.
- **completion_policy** (`Union[roboto.experimental.sessions.record.CompletionPolicy, None, roboto.sentinels.NotSetType]`): Completion policy of a created Session; see [`create()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.create).
- **caller_org_id** (`Optional[str]`): Caller's org scope. Required when the caller belongs to multiple orgs.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Optional RobotoClient; defaults to the ambient one.

**Returns**

- `Session`: The matching or created Session.

**Raises**

- [`RobotoServiceUnavailableException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoServiceUnavailableException): Other calls with the same query kept this one waiting for more than 10 seconds, after the SDK's own retries. Calling again is safe.

**Usage**

```python
from roboto.experimental.sessions import CompletionPolicy, Session
session = Session.create_if_not_exists(
    "name = 'flight-0042'",
    name="flight-0042",
    completion_policy=CompletionPolicy(inactivity_minutes=15),
)
session.add_file("fl_0123456789abcdef")
```

#### Session.delete()

```python
def delete() -> None
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L787-L791)

Delete this Session. The files it included and the devices attached to it are not deleted.

**Returns**

- `None`

#### Session.detach_from_device()

```python
def detach_from_device(device_id: str) -> None
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L793-L802)

Remove a Device from this Session's subjects.

**Parameters**

- **device_id** (`str`): ID of the Device to remove as a subject of this Session.

**Returns**

- `None`

#### Session.for_dataset()

```python
@classmethod
def for_dataset(
    dataset_id: str,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
) -> collections.abc.Generator[Session, None, None]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L282-L319)

Iterate Sessions whose composition includes any file in the given dataset.

**Parameters**

- **dataset_id** (`str`): Dataset whose sessions to list.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Optional RobotoClient; defaults to the ambient one.

**Yields**

- Sessions, one at a time, following pagination automatically.

**Returns**

- `collections.abc.Generator[Session, None, None]`

**Usage**

```python
from roboto.experimental.sessions import Session
for session in Session.for_dataset("ds_abc"):
    print(session.session_id, session.name)
```

#### Session.for_org()

```python
@classmethod
def for_org(
    org_id: Optional[str] = None,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
) -> collections.abc.Generator[Session, None, None]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L322-L355)

Iterate all Sessions visible to the caller's org.

**Parameters**

- **org_id** (`Optional[str]`): Caller's org scope. Required when the caller belongs to multiple orgs.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Optional RobotoClient; defaults to the ambient one.

**Yields**

- Sessions, one at a time, following pagination automatically.

**Returns**

- `collections.abc.Generator[Session, None, None]`

#### Session.from_id()

```python
@classmethod
def from_id(
    session_id: str,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L395-L423)

Load a Session by ID.

**Parameters**

- **session_id** (`str`): Session primary key.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Optional RobotoClient; defaults to the ambient one.

**Returns**

- `Session`: The Session.

**Raises**

- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): No session with this ID exists.
- [`RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller lacks view access to the org that owns the session.

**Usage**

```python
from roboto.experimental.sessions import Session
session = Session.from_id("se_abc123")
session.name
# 'flight-2026-04-23-001'
```

#### Session.get_topic()

```python
def get_topic(topic_name: str) -> roboto.experimental.topics.Topic
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L804-L840)

Return the named Topic, scoped to this Session.

The returned Topic is scoped to this Session's associated files and defaults its read window to this Session's aggregate bounds, so `get_data*` reads just this Session's data without an explicit window.

**Parameters**

- **topic_name** (`str`): Exact name of the topic to retrieve (e.g. `"/camera/image"`).

**Returns**

- `roboto.experimental.topics.Topic`: The matching [`Topic`](/reference/python-sdk/roboto/experimental/topics/topic#roboto.experimental.topics.topic.Topic).

**Raises**

- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): No topic with `topic_name` is reachable from this Session (the topic is absent from the org, or this Session holds none of its data).
- [`RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller lacks permission to view Sessions in the org that owns this Session.

**Usage**

```python
topic = session.get_topic("/camera/image")
for timestamp, record in topic.get_data():
    print(timestamp, record)
```

#### Session.ingestion_status()

```python
def ingestion_status() -> roboto.experimental.sessions.record.SessionIngestionStatus
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L842-L857)

Return where this Session stands in ingestion, with the files it is still waiting on.

**Returns**

- `roboto.experimental.sessions.record.SessionIngestionStatus`: The current status. It does not refresh this Session instance.

**Usage**

```python
status = session.ingestion_status()
status.state
# <SessionIngestionState.Processing: 'processing'>
[f.relative_path for f in status.pending_files]
# ['flight_002.mcap']
```

#### Session.ingestion_summaries()

```python
@classmethod
def ingestion_summaries(
    session_ids: collections.abc.Sequence[str],
    org_id: Optional[str] = None,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
) -> list[roboto.experimental.sessions.record.SessionIngestionSummary]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L358-L392)

Where each of many Sessions stands in ingestion, in counts, a hundred Sessions per request.

Cheaper than [`ingestion_status()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.ingestion_status) on each Session for finding which of many are stuck: a summary has no file lists, only counts, including how many waiting files failed to ingest.

**Parameters**

- **session_ids** (`collections.abc.Sequence[str]`): Sessions in the caller's org. Others are left out of the result.
- **org_id** (`Optional[str]`): Caller's org scope. Required when the caller belongs to multiple orgs.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Optional RobotoClient; defaults to the ambient one.

**Returns**

- `list[roboto.experimental.sessions.record.SessionIngestionSummary]`: One summary per Session found, in request order.

**Usage**

```python
summaries = Session.ingestion_summaries(["se_abc123", "se_def456"])
[s.session_id for s in summaries if s.failed_file_count]
# ['se_def456']
```

#### Session.list_devices()

```python
def list_devices() -> collections.abc.Generator[str, None, None]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L886-L904)

Iterate the device IDs attached as subjects of this Session, paginated.

**Returns**

- `collections.abc.Generator[str, None, None]`

#### Session.list_files()

```python
def list_files() -> collections.abc.Generator[roboto.experimental.sessions.record.SessionFileView, None, None]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L906-L930)

Iterate the files this Session includes, following pagination automatically.

**Yields**

- [`SessionFileView`](/reference/python-sdk/roboto/experimental/sessions/record#roboto.experimental.sessions.record.SessionFileView) entries, each carrying the part of the file this Session holds (the optional `data_range` slice and `min_wall_clock_timestamp_ns` / `max_wall_clock_timestamp_ns` window), the `unix_epoch_offset_ns` the platform added to reach that window, and display fields of the file itself (name, dataset, tags, size, ...).

**Returns**

- `collections.abc.Generator[roboto.experimental.sessions.record.SessionFileView, None, None]`

#### Session.list_metrics()

```python
def list_metrics() -> list[roboto.domain.metrics.metric.Metric]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L932-L946)

Return all metrics published to this Session.

**Returns**

- `list[roboto.domain.metrics.metric.Metric]`: List of [`Metric`](/reference/python-sdk/roboto/domain/metrics/metric#roboto.domain.metrics.metric.Metric) instances for this Session.

**Usage**

```python
metrics = session.list_metrics()
for m in metrics:
    print(m.name, m.value)
```

#### Session.list_topics()

```python
def list_topics() -> collections.abc.Generator[roboto.experimental.topics.Topic, None, None]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L948-L992)

Iterate the topics reachable from this Session, following pagination.

A topic is yielded only when this Session holds some of its data: a time span of the topic ([`TimelineExtentRecord`](/reference/python-sdk/roboto/domain/topics/record#roboto.domain.topics.record.TimelineExtentRecord)) on one of the Session's files, inside the slice the Session holds of that file and overlapping the time window it holds. Each topic is yielded once however many files and partitions carry it, ordered by `name` with `topic_id` as a deterministic tiebreaker.

A yielded Topic is scoped to this Session's files and defaults its read window to this Session's aggregate bounds, so [`get_data()`](/reference/python-sdk/roboto/experimental/topics/topic#roboto.experimental.topics.topic.Topic.get_data) (and the other `get_data*` methods) read just this Session's data without an explicit window.

**Yields**

- [`Topic`](/reference/python-sdk/roboto/experimental/topics/topic#roboto.experimental.topics.topic.Topic) instances.

**Returns**

- `collections.abc.Generator[roboto.experimental.topics.Topic, None, None]`

**Usage**

```python
for topic in session.list_topics():
    for timestamp, record in topic.get_data():
        print(topic.name, timestamp, record)
```

#### Session.publish_metrics()

```python
def publish_metrics(
    metrics: list[roboto.domain.metrics.record.MetricEntry],
    device_id: Union[roboto.sentinels.NotSetType, Optional[str]] = NotSet,
) -> roboto.http.BatchResponse[roboto.domain.metrics.metric.Metric]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L994-L1052)

Record metric values for this Session in a single network call.

Convenience wrapper around [`publish()`](/reference/python-sdk/roboto/domain/metrics/metric#roboto.domain.metrics.metric.Metric.publish) that supplies this Session's `session_id` and `org_id`. Republishing a metric under the same name replaces its previous value for this Session.

If a metric definition does not already exist for a given name it is created automatically.

**Parameters**

- **metrics** (`list[roboto.domain.metrics.record.MetricEntry]`): Metric names and numeric values to record.
- **device_id** (`Union[roboto.sentinels.NotSetType, Optional[str]]`): Device to associate with each published value, or `None` to opt out. When omitted, the server infers a device from this Session's attached devices: the call succeeds only if exactly one device is associated and is rejected when zero or more than one are.

**Returns**

- `roboto.http.BatchResponse[roboto.domain.metrics.metric.Metric]`: One element per metric entry, in request order, holding either the recorded [`Metric`](/reference/python-sdk/roboto/domain/metrics/metric#roboto.domain.metrics.metric.Metric) or why the platform refused it.

**Raises**

- [`roboto.exceptions.RobotoInvalidRequestException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoInvalidRequestException): `device_id` was omitted and this Session has zero or more than one attached devices.

**Usage**

Let the server infer the device from this Session's single attached device:

```python
from roboto.domain.metrics import MetricEntry
published = session.publish_metrics(
    [
        MetricEntry(name="cpu.usage_max", value=87.2),
        MetricEntry(name="memory.peak_mb", value=2048.0),
    ]
)
len(published.succeeded)
# 2
```

Attach to an explicit device, overriding inference:

```python
session.publish_metrics(
    [MetricEntry(name="cpu.usage_max", value=87.2)],
    device_id="robot01",
)
```

#### Session.put_metadata()

```python
def put_metadata(metadata: dict[str, Any]) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1054-L1067)

Add or update metadata fields on this Session.

**Parameters**

- **metadata** (`dict[str, Any]`): Field-to-value map. Existing fields are overwritten; fields not in this map are left unchanged.

**Returns**

- `Session`: This Session, refreshed from the server response.

**Usage**

```python
session.put_metadata({"weather": "clear", "pilot": "alice"})
```

#### Session.put_tags()

```python
def put_tags(tags: roboto.updates.StrSequence) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1069-L1083)

Add tags to this Session.

Tags already present on the Session are not duplicated.

**Parameters**

- **tags** (`roboto.updates.StrSequence`): Tags to add.

**Returns**

- `Session`: This Session, refreshed from the server response.

**Usage**

```python
session.put_tags(["pre-flight-check", "training"])
```

#### Session.refresh()

```python
def refresh() -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1085-L1098)

Re-read this Session from the platform, replacing every property backed by its record.

Call it when something other than this instance changed the Session: the aggregate bounds `min_timestamp_ns` and `max_timestamp_ns` are recomputed whenever a Session's composition or anchoring changes, including by another caller.

**Returns**

- `Session`: This Session.

#### Session.remove_file()

```python
def remove_file(file: Union[roboto.domain.files.File, str]) -> str
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1100-L1115)

Remove a single file from this Session.

The singular form of [`remove_files()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.remove_files).

**Parameters**

- **file** (`Union[roboto.domain.files.File, str]`): A [`File`](/reference/python-sdk/roboto/domain/files/file#roboto.domain.files.file.File) or a file ID.

**Returns**

- `str`: The ID of the removed file.

**Raises**

- [`roboto.exceptions.RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): This Session does not hold the file.

#### Session.remove_files()

```python
def remove_files(
    files: collections.abc.Sequence[Union[roboto.domain.files.File, str]],
) -> roboto.http.BatchResponse[str]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1117-L1147)

Remove the given files from this Session.

A file this Session does not hold is reported as its own element rather than failing the call, while a failure the platform did not anticipate, such as a timeout, removes none of the files. The platform then recomputes this Session's aggregate bounds across the files that remain, and this instance reflects the new `min_timestamp_ns` / `max_timestamp_ns` on return.

**Parameters**

- **files** (`collections.abc.Sequence[Union[roboto.domain.files.File, str]]`): Files to remove, each a [`File`](/reference/python-sdk/roboto/domain/files/file#roboto.domain.files.file.File) or a file ID. An empty sequence returns an empty response without contacting the platform.

**Returns**

- `roboto.http.BatchResponse[str]`: One element per named file, in request order, holding either its ID or why the platform refused to remove it.

**Raises**

- `pydantic.ValidationError`: The sequence names a file more than once, or names more than [`MAX_FILES_AND_TOPICS_PER_REQUEST`](/reference/python-sdk/roboto/experimental/ingest/operations#roboto.experimental.ingest.operations.MAX_FILES_AND_TOPICS_PER_REQUEST) files; rejected client-side, before any request is made.

#### Session.remove_metadata()

```python
def remove_metadata(metadata: roboto.updates.StrSequence) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1149-L1161)

Remove metadata keys from this Session.

**Parameters**

- **metadata** (`roboto.updates.StrSequence`): Metadata keys to remove. Dot notation addresses nested keys (`"weather.condition"`).

**Returns**

- `Session`: This Session, refreshed from the server response.

**Usage**

```python
session.remove_metadata(["pilot", "weather.condition"])
```

#### Session.remove_tags()

```python
def remove_tags(tags: roboto.updates.StrSequence) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1163-L1176)

Remove the given tags from this Session.

**Parameters**

- **tags** (`roboto.updates.StrSequence`): Tags to remove. Tags not present on the Session are silently ignored.

**Returns**

- `Session`: This Session, refreshed from the server response.

**Usage**

```python
session.remove_tags(["training"])
```

#### Session.set_custom_field()

```python
def set_custom_field(name: str, value: Any) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1178-L1187)

Set a single custom-field value on this session.

`name` must be the name of a [`Ready`](/reference/python-sdk/roboto/domain/custom_fields/record#roboto.domain.custom_fields.record.CustomFieldStatus.Ready) custom field for this session's org and the [`Session`](/reference/python-sdk/roboto/domain/custom_fields/record#roboto.domain.custom_fields.record.TargetEntityType.Session) entity type; `value` must satisfy the field's declared type.

**Parameters**

- **name** (`str`)
- **value** (`Any`)

**Returns**

- `Session`

#### Session.set_custom_fields()

```python
def set_custom_fields(fields: dict[str, Any]) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1189-L1196)

Set or overwrite multiple custom-field values on this session.

Each key must name a Ready custom field for this session's org and the [`Session`](/reference/python-sdk/roboto/domain/custom_fields/record#roboto.domain.custom_fields.record.TargetEntityType.Session) entity type; each value must satisfy the field's declared type.

**Parameters**

- **fields** (`dict[str, Any]`)

**Returns**

- `Session`

#### Session.set_unix_offset()

```python
def set_unix_offset(anchor: roboto.time.Time) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1198-L1270)

Anchor this Session's data to wall-clock time.

`anchor` becomes the wall-clock instant of stored time 0 for all of this Session's topic data, and the Session's aggregate bounds are recomputed to reflect it.

This write reaches this Session's data and no more. Where several Sessions share one file, each owning a slice of it, it anchors the slices this Session holds and leaves the file's other slices at whatever instant they were given. Data another Session also holds is shared, not copied, so that Session reads the same anchor.

An anchor exists only when a caller supplies one, either as the data is added (the `anchor` argument of [`add_file()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.add_file), or `anchor_ns` on a [`SessionFile`](/reference/python-sdk/roboto/experimental/sessions/operations#roboto.experimental.sessions.operations.SessionFile)) or through this method. Until then, the data carries an offset of 0 and its stored timestamps are read as nanoseconds since the Unix epoch. Applying an anchor overwrites whatever anchor the data carried before; applying the one it already carries changes nothing, so repeating the call succeeds. An anchor survives re-ingest: redeclaring a slice without supplying an anchor preserves the one it already had.

**Parameters**

- **anchor** (`roboto.time.Time`): Wall-clock instant of stored time 0: an `int` of nanoseconds since the Unix epoch, or any other [`Time`](/reference/python-sdk/roboto/time#roboto.time.Time), read as [`to_epoch_nanoseconds()`](/reference/python-sdk/roboto/time#roboto.time.to_epoch_nanoseconds) reads it (a `datetime` or ISO 8601 string is that instant; a `float`, `Decimal`, or numeric string is seconds since the epoch). Must fall after the Unix epoch: zero is not an anchor (use [`clear_unix_offset()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.clear_unix_offset) to return the Session to an offset of 0), and earlier instants are rejected.

**Returns**

- `Session`: This Session, refreshed with recomputed aggregate bounds.

**Raises**

- `TypeError`: `anchor` is not one of the [`Time`](/reference/python-sdk/roboto/time#roboto.time.Time) types.
- `ValueError`: `anchor` is a boolean, a string that is neither seconds nor ISO 8601, zero, before the Unix epoch, or too large for a signed 64-bit integer of nanoseconds; rejected client-side, before any request is made. A range refusal is raised as `pydantic.ValidationError`, a subclass of `ValueError`.
- `OverflowError`: `anchor` is infinite, such as `float("inf")`; rejected client-side, before any request is made.
- [`RobotoInvalidRequestException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoInvalidRequestException): Any of three cases: the Session has no topic data to anchor; the Session's own data already carries several distinct anchors, and the server will not pick one of them to move everything from (anchor less than a whole Session at a time instead, either one topic with [`set_unix_offset()`](/reference/python-sdk/roboto/experimental/topics/topic#roboto.experimental.topics.topic.Topic.set_unix_offset) on a Topic from [`get_topic()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.get_topic) or [`list_topics()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.list_topics), or one whole file with [`set_timeline_offset()`](/reference/python-sdk/roboto/domain/files/file#roboto.domain.files.file.File.set_timeline_offset)); or the anchor would move the Session's data, or a time range declared over it, before the Unix epoch or past the largest storable Unix-epoch nanosecond value; anchor the data at the instant it was recorded.
- [`RobotoConflictException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoConflictException): A concurrent writer added files to the Session while the anchor was being applied; retry the call.
- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): The Session no longer exists.
- [`RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller lacks permission to manage Sessions in the org that owns this Session.

**Usage**

The recomputed bounds are the offset plus the Session's stored values:

```python
session.min_timestamp_ns
# 250000000
session = session.set_unix_offset(1_700_000_000_000_000_000)
session.min_timestamp_ns
# 1700000000250000000
```

The same anchor given as a `datetime`:

```python
import datetime
session = session.set_unix_offset(
    datetime.datetime(2023, 11, 14, 22, 13, 20, tzinfo=datetime.timezone.utc)
)
session.min_timestamp_ns
# 1700000000250000000
```

#### Session.skip_waiting_for()

```python
def skip_waiting_for(
    file_ids: collections.abc.Sequence[str],
) -> roboto.experimental.sessions.record.SkipWaitingResponse
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L859-L884)

Stop waiting for these files to be ingested, e.g. ones whose ingestion failed.

A complete Session is announced ingested once its other ingestable files are. The skip covers each file's current upload: uploading it again makes the Session wait for it again, while editing its tags or metadata does not. Skipped files stay in the Session and are listed in [`SessionIngestionStatus.skipped_files`](/reference/python-sdk/roboto/experimental/sessions/record#roboto.experimental.sessions.record.SessionIngestionStatus.skipped_files).

**Parameters**

- **file_ids** (`collections.abc.Sequence[str]`): Files in this Session.

**Returns**

- `roboto.experimental.sessions.record.SkipWaitingResponse`: Where this Session stands in ingestion afterwards, and the requested files that are not in it.

**Usage**

```python
response = session.skip_waiting_for(["fl_0123456789abcdef"])
response.ingestion.state
# <SessionIngestionState.Ingested: 'ingested'>
response.not_in_session
# []
```

#### Session.update()

```python
def update(
    description: Optional[Union[str, roboto.sentinels.NotSetType]] = NotSet,
    metadata_changeset: Union[roboto.updates.MetadataChangeset, roboto.sentinels.NotSetType] = NotSet,
    name: Optional[Union[str, roboto.sentinels.NotSetType]] = NotSet,
    custom_fields_changeset: Optional[roboto.updates.CustomFieldChangeset] = None,
    completion_policy: Union[roboto.experimental.sessions.record.CompletionPolicy, None, roboto.sentinels.NotSetType] = NotSet,
) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1272-L1326)

Update mutable Session fields.

Fields left at the `NotSet` default are preserved; for nullable fields (`description`, `name`, `completion_policy`), pass `None` to clear.

**Parameters**

- **description** (`Optional[Union[str, roboto.sentinels.NotSetType]]`): New description for the Session. Set to `None` to clear the description. Leave at the default to leave the description unchanged.
- **metadata_changeset** (`Union[roboto.updates.MetadataChangeset, roboto.sentinels.NotSetType]`): Tag and metadata changes to apply (put/remove tags and fields). See [`put_tags()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.put_tags), [`remove_tags()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.remove_tags), [`put_metadata()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.put_metadata), and [`remove_metadata()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.remove_metadata) for shorthand helpers.
- **name** (`Optional[Union[str, roboto.sentinels.NotSetType]]`): New name for the Session. Set to `None` to clear the name. Leave at the default to leave the name unchanged.
- **custom_fields_changeset** (`Optional[roboto.updates.CustomFieldChangeset]`): Changes to apply to Ready custom-field values on this session. Field names not referenced by the changeset are left unchanged.
- **completion_policy** (`Union[roboto.experimental.sessions.record.CompletionPolicy, None, roboto.sentinels.NotSetType]`): When Roboto marks the Session complete on its own. `None` removes the policy, so the Session is marked complete only by [`complete()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.complete). On a Session in progress that has files, a new policy counts its inactivity from now. A complete Session stays complete, and the policy applies after a file is added to it.

**Returns**

- `Session`: This Session, refreshed from the server response.

**Usage**

```python
session.update(description="formation flight #4", name="flight-2026-04-23-001")
```

Let Roboto mark a Session complete 30 minutes after its last file is added:

```python
from roboto.experimental.sessions import CompletionPolicy
session.update(completion_policy=CompletionPolicy(inactivity_minutes=30))
```

#### Session.wait_until_ingested()

```python
def wait_until_ingested(timeout: float = 600, poll_interval: int = 15) -> Session
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/sessions/session.py#L1328-L1388)

Block until every ingestable file in this Session is ingested and Roboto has announced it ingested.

The wait ends once Roboto records the announcement that fires `session.ingested`, which raises [`ingestion_count`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.ingestion_count). A Session already announced ingested since it was last marked complete returns at once.

A Session in progress is waited for when it will complete on its own: it has a [`completion_policy`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.completion_policy) and files, so [`completes_at`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.completes_at) is set. The wait then includes the policy's inactivity. A Session in progress that will not complete on its own is refused, as only [`complete()`](/reference/python-sdk/roboto/experimental/sessions/session#roboto.experimental.sessions.session.Session.complete) would complete it.

The default `timeout` of 10 minutes may be too short for a Session of large recordings. Pass a longer one, e.g. `timeout=2 * 60 * 60` for two hours.

**Parameters**

- **timeout** (`float`): Maximum seconds to wait.
- **poll_interval** (`int`): Seconds between status checks.

**Returns**

- `Session`: This Session, refreshed from the server.

**Raises**

- `RuntimeError`: This Session is in progress and will not complete on its own: it has no completion policy, or it has one but no files.
- [`roboto.waiters.TimeoutError`](/reference/python-sdk/roboto/waiters#roboto.waiters.TimeoutError): `timeout` elapsed first. The message says where the Session stands and lists up to 10 of the files it is still waiting for. It is also a built-in `TimeoutError`.

**Usage**

```python
session = session.complete().wait_until_ingested(timeout=2 * 60 * 60)
```
