---
sidebar:
  hidden: true
title: roboto.domain.custom_fields.custom_field
---
## Module Contents

### CustomField

```python
class roboto.domain.custom_fields.custom_field.CustomField(
    record: roboto.domain.custom_fields.record.CustomFieldRecord,
    roboto_client: roboto.http.RobotoClient,
)
```

`from roboto.domain.custom_fields import CustomField`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L35-L637)

A typed, queryable schema extension defined by an organization for a Roboto entity type.

Custom fields let an organization extend Roboto's built-in entity schemas with typed fields tailored to its data and workflows. Each field is scoped to one [`TargetEntityType`](/reference/python-sdk/roboto/domain/custom_fields/record#roboto.domain.custom_fields.record.TargetEntityType) (e.g. Dataset, Collection, or Device) and is optimized for efficient search — equality, range, prefix, and sort — on its values.

A field's [`status`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.status) tells you what you can do with it:

- `Creating`: the field is being set up. Values cannot yet be assigned to entities, and search and sort queries cannot reference it.
- `Ready`: the field is available end-to-end. Values can be set on entities of `entity_type`, and the field can be used in search filters and to sort search results.
- `Deleting`: the field is on its way out. Callers should treat it as already gone — values can no longer be set and the field will shortly disappear.
- `Failed`: the most recent create or delete attempt did not succeed.

Create and delete return as soon as the status transition has been recorded; the rest of the work happens asynchronously. Use [`wait_to_be_ready()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.wait_to_be_ready) after [`create()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.create) and [`wait_to_be_deleted()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.wait_to_be_deleted) after [`delete()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.delete) if you need to wait for that work to finish.

Field names are unique within an `(org_id, entity_type)` pair, must match `^[a-z][a-z0-9_]{0,62}$` (lowercase ASCII, max 63 chars), and are subject to a per-org-tier quota on each entity type.

**Parameters**

- **record** (`roboto.domain.custom_fields.record.CustomFieldRecord`)
- **roboto_client** (`roboto.http.RobotoClient`)

**Properties**

- **CustomField.created** (`datetime.datetime`): UTC timestamp when this field was defined.

- **CustomField.created_by** (`str`): User ID that defined this field.

- **CustomField.description** (`str | None`): Long-form description of the field's meaning, or `None` if unset.

  Return type: `Optional[str]`

- **CustomField.display_name** (`str | None`): Human-readable label for the field, or `None`.

  Return type: `Optional[str]`

- **CustomField.entity_type** (`roboto.domain.custom_fields.record.TargetEntityType`): Roboto entity type this field extends.

- **CustomField.field_id** (`str`): Opaque, globally unique identifier for this field.

- **CustomField.field_name** (`str`): Name of the field, unique within `(org_id, entity_type)` and fixed at creation time.

- **CustomField.field_type** (`roboto.domain.custom_fields.record.CustomFieldType`): Value type of the field. Fixed at creation time.

- **CustomField.last_error** (`str | None`): Human-readable summary of the most recent failure, if any.

  Populated when [`status`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.status) is [`CustomFieldStatus.Failed`](/reference/python-sdk/roboto/domain/custom_fields/record#roboto.domain.custom_fields.record.CustomFieldStatus.Failed), and may stay set until the next failure or success.

  Return type: `Optional[str]`

- **CustomField.metadata_path** (`str | None`): Source metadata key the field was promoted from, if any. Reserved for future use.

  Return type: `Optional[str]`

- **CustomField.modified** (`datetime.datetime`): UTC timestamp of the field's most recent status or other change.

- **CustomField.modified_by** (`str`): User ID of the most recent modifier. May be a system identity for automatic status changes.

- **CustomField.options** (`roboto.domain.custom_fields.record.CustomFieldOptions | None`): Type-specific configuration, or `None` if the field type takes none.

  For enum fields this carries the allowed values and is required.

  Return type: `Optional[roboto.domain.custom_fields.record.CustomFieldOptions]`

- **CustomField.org_id** (`str`): Organization that owns this field.

- **CustomField.status** (`roboto.domain.custom_fields.record.CustomFieldStatus`): Current lifecycle status. See the class docstring for the state machine.

#### CustomField.add_enum_values()

```python
def add_enum_values(*values: str) -> CustomField
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L494-L538)

Add values to the set this enum field accepts.

Re-fetches the field before updating it: an update has to list every value the field already has, so a value someone else added since this object was loaded would otherwise read as a removal and be rejected.

Only administrators in this field's organization can update it.

**Parameters**

- **values** (`str`): Values to allow, on top of the field's current ones. Each is normalized to Unicode NFC with runs of whitespace collapsed to a single space; passing one the field already has, in any spelling that normalizes the same, changes nothing. A field may hold at most 250 values.

**Returns**

- `CustomField`: This same CustomField, with its in-memory record replaced by the server's updated copy. Called with no values, it returns without contacting the server.

**Raises**

- [`RobotoInvalidRequestException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoInvalidRequestException): This field is not an enum field.
- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): The field no longer exists.
- [`RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller lacks permission to update this field.
- `pydantic.ValidationError`: A value cannot be stored, or the field would end up with more values than it is allowed.

**Usage**

```python
field.options.enum_values
# ['low', 'medium', 'high']
field.add_enum_values("critical").options.enum_values
# ['low', 'medium', 'high', 'critical']
```

#### CustomField.create()

```python
@classmethod
def create(
    field_name: str,
    field_type: roboto.domain.custom_fields.record.CustomFieldType,
    entity_type: roboto.domain.custom_fields.record.TargetEntityType,
    display_name: Optional[str] = None,
    description: Optional[str] = None,
    options: Optional[roboto.domain.custom_fields.record.CustomFieldOptions] = None,
    metadata_path: Optional[str] = None,
    caller_org_id: Optional[str] = None,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
) -> CustomField
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L69-L160)

Define a new custom field for an entity type in the caller's organization.

Returns as soon as the field has been registered. The newly created field is typically still `Creating` and is not yet usable for setting values or running queries; call [`wait_to_be_ready()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.wait_to_be_ready) if you need to use the field immediately.

Only administrators in the organization can define new custom fields.

**Parameters**

- **field_name** (`str`): Name of the field, unique within the `(org_id, entity_type)` pair. Must match `^[a-z][a-z0-9_]{0,62}$`. Fixed at creation time — cannot be changed later.
- **field_type** (`roboto.domain.custom_fields.record.CustomFieldType`): Value type of the field. Determines which operators are supported in search and sort.
- **entity_type** (`roboto.domain.custom_fields.record.TargetEntityType`): Roboto entity type this field extends.
- **display_name** (`Optional[str]`): Human-readable label shown in the UI. Defaults to `None`.
- **description** (`Optional[str]`): Longer description of the field's meaning.
- **options** (`Optional[roboto.domain.custom_fields.record.CustomFieldOptions]`): Type-specific configuration. Required for [`Enum`](/reference/python-sdk/roboto/domain/custom_fields/record#roboto.domain.custom_fields.record.CustomFieldType.Enum) fields (to declare the allowed values).
- **metadata_path** (`Optional[str]`): Reserved for promoting an existing metadata key into a custom field. Not yet supported; leave as `None`.
- **caller_org_id** (`Optional[str]`): Organization that should own the field. If omitted, the field is created in the caller's organization.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Roboto client instance. Uses the default if omitted.

**Returns**

- `CustomField`: The newly created CustomField. Its [`status`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.status) is typically `Creating`; call [`wait_to_be_ready()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.wait_to_be_ready) to block until it is `Ready`.

**Raises**

- [`RobotoConflictException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoConflictException): A field with this `field_name` and `entity_type` already exists in the target org.
- [`RobotoInvalidRequestException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoInvalidRequestException): The request fails validation (e.g., a `field_name` that does not match the regex, an enum field without `options`, or `options` whose `field_type` does not match `field_type`).
- [`RobotoLimitExceededException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoLimitExceededException): The limit on custom field definitions has been reached for the target organization and Roboto entity type.
- [`RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller is not an administrator in the target org.

**Usage**

Define a string field on datasets:

```python
field = CustomField.create(
    field_name="flight_id",
    field_type=CustomFieldType.String,
    entity_type=TargetEntityType.Dataset,
    display_name="Flight ID",
)
field.wait_to_be_ready()
```

Define an enum field with a fixed set of allowed values:

```python
from roboto.domain.custom_fields import EnumFieldOptions
field = CustomField.create(
    field_name="severity",
    field_type=CustomFieldType.Enum,
    entity_type=TargetEntityType.Event,
    options=EnumFieldOptions(enum_values=["low", "medium", "high"]),
)
```

#### CustomField.delete()

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

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L384-L405)

Delete this custom field.

Returns as soon as the field has been moved to [`Deleting`](/reference/python-sdk/roboto/domain/custom_fields/record#roboto.domain.custom_fields.record.CustomFieldStatus.Deleting). From the caller's perspective the field is gone at that point: values can no longer be set, and the field will shortly disappear from query results. The rest of the removal happens asynchronously; call [`wait_to_be_deleted()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.wait_to_be_deleted) if you need to know when it has finished.

Only administrators in the target organization can delete custom fields.

**Raises**

- [`RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller lacks permission to delete this field.

**Returns**

- `None`

**Usage**

```python
field = CustomField.from_name_and_entity_type("flight_id", TargetEntityType.Dataset)
field.delete()
field.wait_to_be_deleted()
```

#### CustomField.from_id()

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

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L163-L186)

Load an existing custom field by its `field_id`.

**Parameters**

- **field_id** (`str`): Opaque identifier assigned at create time.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Roboto client instance. Uses the default if omitted.

**Returns**

- `CustomField`: The CustomField with this `field_id`.

**Raises**

- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): No field with this `field_id` is visible to the caller.

**Usage**

```python
field = CustomField.from_id("cf_abc123")
field.field_name
# 'flight_id'
```

#### CustomField.from_name_and_entity_type()

```python
@classmethod
def from_name_and_entity_type(
    field_name: str,
    entity_type: roboto.domain.custom_fields.record.TargetEntityType,
    owner_org_id: Optional[str] = None,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
) -> CustomField
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L189-L231)

Load a custom field by `field_name` and `entity_type`.

Field names are unique within an `(org_id, entity_type)` pair, so the name plus the entity type fully qualifies a field within a given org.

**Parameters**

- **field_name** (`str`): Name of the field, as supplied to [`create()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.create).
- **entity_type** (`roboto.domain.custom_fields.record.TargetEntityType`): Entity type the field extends.
- **owner_org_id** (`Optional[str]`): Organization that owns the field. If omitted, searches the caller's organization.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Roboto client instance. Uses the default if omitted.

**Returns**

- `CustomField`: The matching CustomField.

**Raises**

- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): No field with this name and entity type exists in the target org.
- [`RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller is not authorized to retrieve the field.

**Usage**

```python
field = CustomField.from_name_and_entity_type(
    field_name="flight_id",
    entity_type=TargetEntityType.Dataset,
)
```

#### CustomField.list()

```python
@classmethod
def list(
    entity_type: Optional[roboto.domain.custom_fields.record.TargetEntityType] = None,
    statuses: Optional[collections.abc.Sequence[roboto.domain.custom_fields.record.CustomFieldStatus]] = None,
    owner_org_id: Optional[str] = None,
    roboto_client: Optional[roboto.http.RobotoClient] = None,
) -> collections.abc.Generator[CustomField, None, None]
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L234-L293)

Yield custom fields visible to the caller, optionally filtered by entity type and status.

By default, only fields in `Creating`, `Ready`, or `Failed` are returned; fields in `Deleting` are excluded because they are on their way out. Pass `statuses=` explicitly to override this.

**Parameters**

- **entity_type** (`Optional[roboto.domain.custom_fields.record.TargetEntityType]`): If provided, restrict results to fields targeting this entity type.
- **statuses** (`Optional[collections.abc.Sequence[roboto.domain.custom_fields.record.CustomFieldStatus]]`): If provided, restrict results to fields in any of these statuses. Defaults to `(Creating, Ready, Failed)`.
- **owner_org_id** (`Optional[str]`): Organization to list fields from. If omitted, lists fields in the caller's organization.
- **roboto_client** (`Optional[roboto.http.RobotoClient]`): Roboto client instance. Uses the default if omitted.

**Yields**

- CustomField instances matching the filters, in pages transparently fetched as the generator is consumed.

**Returns**

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

**Usage**

List every Ready field on datasets in the caller's org:

```python
for field in CustomField.list(
    entity_type=TargetEntityType.Dataset,
    statuses=[CustomFieldStatus.Ready],
):
    print(field.field_name, field.field_type)
```

#### CustomField.refresh()

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

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L407-L425)

Re-fetch this field's record from the server and return `self`.

Useful for observing asynchronous state transitions (e.g., `Creating` → `Ready`) without constructing a new object.

**Returns**

- `CustomField`: This same CustomField, with its in-memory record replaced by a freshly fetched copy.

**Raises**

- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): The field has been fully deleted.

**Usage**

```python
field.refresh().status
# <CustomFieldStatus.Ready: 'ready'>
```

#### CustomField.update()

```python
def update(
    display_name: Union[str, None, roboto.sentinels.NotSetType] = NotSet,
    description: Union[str, None, roboto.sentinels.NotSetType] = NotSet,
    options: Union[roboto.domain.custom_fields.record.CustomFieldOptions, roboto.sentinels.NotSetType] = NotSet,
) -> CustomField
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L427-L492)

Update mutable metadata on this custom field in place.

Passing [`NotSet`](/reference/python-sdk/roboto/sentinels#roboto.sentinels.NotSet) (the default) leaves a field unchanged; passing `None` clears it.

Only administrators in this field's organization can update it.

**Parameters**

- **display_name** (`Union[str, None, roboto.sentinels.NotSetType]`): New display name, or `None` to clear it.
- **description** (`Union[str, None, roboto.sentinels.NotSetType]`): New description, or `None` to clear it.
- **options** (`Union[roboto.domain.custom_fields.record.CustomFieldOptions, roboto.sentinels.NotSetType]`): Replacement type-specific configuration. Must be for this field's [`field_type`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.field_type). For an enum field, the new `enum_values` must include every existing value: values can be added but not removed. Re-sending a value the field already has is harmless: repeats are dropped, not rejected.

**Returns**

- `CustomField`: This same CustomField, with its in-memory record replaced by the server's updated copy.

**Raises**

- [`RobotoInvalidRequestException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoInvalidRequestException): `options` are for a different field type, or would remove an existing enum value.
- [`RobotoNotFoundException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoNotFoundException): The field no longer exists.
- [`RobotoUnauthorizedException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoUnauthorizedException): The caller lacks permission to update this field.
- `pydantic.ValidationError`: An enum value cannot be stored, or `enum_values` is longer than a field may declare. Raised before the request is sent.

**Usage**

```python
field.update(display_name="Flight identifier")
```

Add a value to an enum field:

```python
from roboto.domain.custom_fields import EnumFieldOptions
field.update(options=EnumFieldOptions(enum_values=[*field.options.enum_values, "critical"]))
```

A value the field already has keeps its place, and the new one is appended:

```python
field.options.enum_values
# ['low', 'medium', 'high']
updated = field.update(
    options=EnumFieldOptions(enum_values=[*field.options.enum_values, "medium", "critical"])
)
updated.options.enum_values
# ['low', 'medium', 'high', 'critical']
```

Clear the description:

```python
field.update(description=None)
```

#### CustomField.wait_to_be_deleted()

```python
def wait_to_be_deleted(timeout: float = 5 * 60, poll_interval: int = 2) -> None
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L594-L637)

Block until this custom field is fully deleted.

Intended to be called immediately after [`delete()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.delete) to know when the field has been fully removed from the platform.

**Parameters**

- **timeout** (`float`): Maximum time, in seconds, to wait. Most fields are deleted well within the default; raise this value if you observe legitimate timeouts.
- **poll_interval** (`int`): Seconds to sleep between polls.

**Raises**

- [`RobotoInvalidStateTransitionException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoInvalidStateTransitionException): The field is in a state from which it will not progress to deleted (`Ready`, `Creating`, or `Failed`). Call [`delete()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.delete) first.
- [`roboto.waiters.TimeoutError`](/reference/python-sdk/roboto/waiters#roboto.waiters.TimeoutError): The field is still `Deleting` after `timeout` seconds.

**Returns**

- `None`

**Usage**

```python
field.delete()
field.wait_to_be_deleted()
```

#### CustomField.wait_to_be_ready()

```python
def wait_to_be_ready(timeout: float = 5 * 60, poll_interval: int = 2) -> None
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/domain/custom_fields/custom_field.py#L540-L592)

Block until this custom field reaches the [`Ready`](/reference/python-sdk/roboto/domain/custom_fields/record#roboto.domain.custom_fields.record.CustomFieldStatus.Ready) state.

Intended to be called immediately after [`create()`](/reference/python-sdk/roboto/domain/custom_fields/custom_field#roboto.domain.custom_fields.custom_field.CustomField.create) to know when the field is usable for setting values and querying.

**Parameters**

- **timeout** (`float`): Maximum time, in seconds, to wait. Most fields reach Ready well within the default; raise this value if you observe legitimate timeouts.
- **poll_interval** (`int`): Seconds to sleep between polls.

**Raises**

- [`RobotoInvalidStateTransitionException`](/reference/python-sdk/roboto/exceptions/domain#roboto.exceptions.domain.RobotoInvalidStateTransitionException): The field is in a state from which it cannot reach Ready (`Failed` or `Deleting`), or it was deleted while waiting.
- [`roboto.waiters.TimeoutError`](/reference/python-sdk/roboto/waiters#roboto.waiters.TimeoutError): The field is still `Creating` after `timeout` seconds.

**Returns**

- `None`

**Usage**

```python
field = CustomField.create(
    field_name="flight_id",
    field_type=CustomFieldType.String,
    entity_type=TargetEntityType.Dataset,
)
field.wait_to_be_ready()
field.status
# <CustomFieldStatus.Ready: 'ready'>
```
