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

# Attributes

> Promote a custom field to an attribute so it appears on every ticket in the workspace, regardless of which form created the ticket.

Attributes are custom fields promoted to workspace-wide status. Once a field is an attribute, it appears on every ticket in the workspace, not just tickets created through the form that owns the field.

## Mental model

An attribute is a custom field that has been registered on a workspace. The registration lifts the field out of its form-only scope, so the field renders on every ticket in the workspace regardless of how the ticket was created.

Key points:

* Attributes are a per-workspace overlay on the custom field library. The custom field itself is unchanged. Adding it as an attribute is a separate join record on the workspace.
* Because attributes reuse the underlying custom field, editing an attribute edits the field for every form it also appears on.
* Removing an attribute detaches the field from the workspace overlay only. The field survives on any forms that still reference it.

***

## Eligible types

Only these `CustomFieldType` values can be promoted to attributes: `TEXT`, `TEXT_AREA`, `DATE`, `DATETIME`, `BOOLEAN`, `SELECT`, `MULTI_SELECT`. Other types (`NUMBER`, `DURATION`, `TIME`, `FILE_PICKER`, `USER_SELECT`, `APPLICATION_SELECT`, and the other resource pickers) render empty off a form context and are rejected by the API with `CUSTOM_FIELD_TYPE_NOT_ATTRIBUTE_ELIGIBLE`. `TIME` is ineligible even though `DATE` and `DATETIME` are eligible.

The UI enforces this in three places:

* The **Add as attribute** menu item is disabled for ineligible fields.
* The **Create Attribute** dialog's type picker is filtered to eligible types only.
* The plus button on a ticket's **Attributes** section only lists eligible, non-system fields that aren't attributes yet.

***

## API

The public REST endpoints are under `/workspace-custom-fields`:

| Endpoint                        | Method | Purpose                                       |
| ------------------------------- | ------ | --------------------------------------------- |
| `/workspace-custom-fields`      | GET    | List attributes in the current workspace      |
| `/workspace-custom-fields`      | POST   | Promote an existing custom field to attribute |
| `/workspace-custom-fields/{id}` | PUT    | Reorder an attribute                          |
| `/workspace-custom-fields/{id}` | DELETE | Remove an attribute from the workspace        |

POST accepts `customFieldId` and an optional `order`. The custom field must already exist in the workspace and be an eligible type. `order` defaults to one past the current highest order in the workspace.

PUT accepts only `id` and `order`, so it is a reorder, not an edit. Change an attribute's label, description, or options through the custom field endpoints instead.

***

## Constraints and gotchas

* Attributes are workspace-scoped. Promoting a field in one workspace does not affect other workspaces, even if the same underlying custom field is used there.
* Removing an attribute never deletes the underlying custom field. Delete the field from **Settings > Fields > Fields** if you want it gone entirely.
* Deleting the underlying custom field cascades: the attribute registration goes with it.
* Editing an attribute's label, description, or options edits the underlying custom field, so the change propagates to every form referencing it. Edits from the **Attributes** tab apply immediately with no confirmation step.
* Attributes render in the order set on the **Attributes** tab. A single PUT with a new `order` is enough, since the sibling attributes are reshuffled around it server-side.
* `system` custom fields are excluded from attribute filters, table columns, and exports even when registered as attributes.
* Attribute values populate on any ticket the field is set on, regardless of source. Tickets created before an attribute was added simply start empty and can be filled in afterwards.
* Attribute values are merged into the ticket's `attributeFields` blob by key, not replaced wholesale. Clearing a value requires sending an explicit `null` for that key.
